
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1995-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "wx.h"
#pragma hdrstop

#include "ctrledit.h"
#include "eventwin.h"
#include "song.h"
#include "track.h"

static const long wbar = 2;

tCtrlEditBase::tCtrlEditBase(int min, int max, tEventWin *p, char const *label, int dx, int x, int y, int w, int h)
    : array((w-dx)/wbar, min, max)
{
  Create(p, label, dx, x, y, w, h);
}

void tCtrlEditBase::Create(tEventWin *p, char const *label, int dx, int x, int y, int w, int h)
{
  parent = p;
  track  = 0;
  from_clock = 0;
  clocks_per_pixel = 0;
  sticky = 1;

  panel = new tCtrlPanel(this, (wxWindow*)parent, x, y, dx, h, 0, "Controller Edit");
  //(void) new wxMessage(panel, (char *)label);
  //panel->NewLine();
  (void)new wxButton(panel, (wxFunction)Apply,  "Apply") ;
  panel->NewLine();
  (void)new wxButton(panel, (wxFunction)Revert, "Revert") ;
  edit = new tArrayEdit((wxFrame *)parent, array, x+dx, y, w - dx, h, 0);
  edit->SetLabel(label);
}


tCtrlEditBase::~tCtrlEditBase()
{
  delete panel;
  delete edit;
}

void tCtrlEditBase::SetSize(int dx, int x, int y, int w, int h)
{
  array.Resize((long)(w-dx) / wbar);
  panel->SetSize(x, y, dx, h);
  edit->SetSize(x+dx, y, w - dx, h);
}


void tCtrlEditBase::ReInit(tTrack *t, long fc, long cpp)
{
  track = t;
  from_clock = fc;
  clocks_per_pixel = cpp;
  OnRevert();
}

long tCtrlEditBase::Clock2i(long clock)
{
  return (clock - from_clock) / clocks_per_pixel / wbar;
}

long tCtrlEditBase::i2Clock(long i)
{
  return i * clocks_per_pixel * wbar + from_clock;
}

int tCtrlEditBase::Clock2Val(long clock)
{
  long i = Clock2i(clock);
  long v1 = array[i];
  long v2 = array[i+1];
  long c1 = i2Clock(i);
  long c2 = i2Clock(i+1);
  int  val = (v2 - v1) * (clock - c1) / (c2 - c1) + v1;
  return val;
}

void tCtrlEditBase::OnRevert()
{
  int i, w, h;
  edit->GetSize(&w, &h);
  long to_clock = from_clock + (long)w * clocks_per_pixel;
  tEventIterator iter(track);
  tEvent *e = iter.Range(from_clock, to_clock);

  int val = Missing();
  for (i = 0; i < array.Size(); i++)
    array[i] = val;

  i = 0;
  while (e)
  {
    if (IsCtrlEdit(e))
    {
      int k = Clock2i(e->Clock);
      if (sticky)
	while (i < k)
	  array[i++] = val;
      val = GetValue(e);
      array[k] = val;
    }
    e = iter.Next();
  }
  if (sticky)
    while (i < array.Size())
      array[i++] = val;

  edit->OnPaint();
}


void tCtrlEditBase::Revert(wxButton &but, wxCommandEvent& event)
{
  tCtrlPanel *panel = (tCtrlPanel *)but.GetParent();
  panel->edit->OnRevert();
}


void tCtrlEditBase::OnApply()
{
  int w, h;
  edit->GetSize(&w, &h);
  long to_clock = from_clock + (long)w * clocks_per_pixel;

  wxBeginBusyCursor();
  parent->Song->NewUndoBuffer();
  // delete old events
  tEventIterator iter(track);
  tEvent *e = iter.Range(from_clock, to_clock);
  while (e)
  {
    if (IsCtrlEdit(e))
      track->Kill(e);
    e = iter.Next();
  }

  // create new events
  long clock;
  int old_val = Missing();
  for (clock = from_clock; clock < to_clock; clock++)
  {
    int new_val = Clock2Val(clock);
    if (old_val != new_val)
    {
      e = NewEvent(clock, new_val);
      track->Put(e);
      old_val = new_val;
    }
  }

  // done
  track->Cleanup();
  wxEndBusyCursor();
}


void tCtrlEditBase::Apply(wxButton &but, wxCommandEvent& event)
{
  ((tCtrlPanel *)but.GetParent())->edit->OnApply();
}

// ------------------------------------------------------------------

tPitchEdit::tPitchEdit(tEventWin *parent, char const *label, int xoff, int x, int y, int w, int h)
  : tCtrlEditBase(-8191, 8191, parent, label, xoff, x, y, w, h)
{
}

int tPitchEdit::Missing()
{
  return 0;
}

int tPitchEdit::IsCtrlEdit(tEvent *e)
{
  return e->IsPitch() != 0;
}

int tPitchEdit::GetValue(tEvent *e)
{
  return e->IsPitch()->Value;
}

tEvent * tPitchEdit::NewEvent(long clock, int val)
{
  return new tPitch(clock, track->Channel - 1, val);
}

// ------------------------------------------------------------------

tCtrlEdit::tCtrlEdit(int CtrlNum, tEventWin *parent, char const *label, int xoff, int x, int y, int w, int h)
  : tCtrlEditBase(0, 127, parent, label, xoff, x, y, w, h)
{
  ctrl_num = CtrlNum;
  if (ctrl_num == 10)	// panpot
    array.SetNull(64);
}

int tCtrlEdit::Missing()
{
  if (ctrl_num == 10)
    return 64;
  return 0;
}

int tCtrlEdit::IsCtrlEdit(tEvent *e)
{
  tControl *c = e->IsControl();
  return (c && c->Control == ctrl_num);
}

int tCtrlEdit::GetValue(tEvent *e)
{
  return e->IsControl()->Value;
}

tEvent * tCtrlEdit::NewEvent(long clock, int val)
{
  return new tControl(clock, track->Channel - 1, ctrl_num, val);
}

// ------------------------------------------------------------------

tVelocEdit::tVelocEdit(tEventWin *parent, char const *label, int xoff, int x, int y, int w, int h)
  : tCtrlEditBase(1, 127, parent, label, xoff, x, y, w, h)
{
  sticky = 0;
}

int tVelocEdit::Missing()
{
  return 1;
}

int tVelocEdit::IsCtrlEdit(tEvent *e)
{
  return e->IsKeyOn() != 0;
}

int tVelocEdit::GetValue(tEvent *e)
{
  return e->IsKeyOn()->Veloc;
}

void tVelocEdit::OnApply()
{
  wxBeginBusyCursor();
  parent->Song->NewUndoBuffer();

  int w, h;
  edit->GetSize(&w, &h);
  long to_clock = from_clock + (long)w * clocks_per_pixel;
  tEventIterator iter(track);
  tEvent *e = iter.Range(from_clock, to_clock);
  while (e)
  {
    tKeyOn *k = e->IsKeyOn();
    if (k)
    {
      tKeyOn *cpy = k->Copy()->IsKeyOn();

      int i = Clock2i(cpy->Clock);
      cpy->Veloc = array[i];
      track->Kill(k);
      track->Put(cpy);
    }
    e = iter.Next();
  }
  track->Cleanup();

  wxEndBusyCursor();
}

