
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-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 "command.h"
#include "song.h"
#include "track.h"
#include "filter.h"

// ************************************************************************
// tSelectedEvents
// ************************************************************************

class tSelectedKeys : public tCommand
{
  public:
    long Keys[128];
  tSelectedKeys(tFilter *f);
  void tSelectedKeys::ExecuteEvent(tTrack *t, tEvent *e);
};

tSelectedKeys::tSelectedKeys(tFilter *f)
  : tCommand(f)
{
  int i;
  for (i = 0; i <  128; i++)
    Keys[i] = 0;
}

void tSelectedKeys::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k = e->IsKeyOn();
  if (k)
    Keys[ k->Key ] += k->Length;
}

// ************************************************************************
// tScale
// ************************************************************************

                            //  c     d     e  f     g    a      b
static const int CMajor[12] = { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 };

void tScale::Init(int ScaleNr, tFilter *f)
{
  int i;

  for (i = 0; i < 12; i++)
    ScaleKeys[i] = 0;

  if (ScaleNr == ScaleChromatic)
  {
    for (i = 0; i < 12; i++)
      ScaleKeys[i] = 1;
  }

  else if (ScaleNr == ScaleSelected)
  {
    int found = 0;
    tSelectedKeys cmd(f);
    cmd.Execute(0);
    for (i = 0; i < 128; i++)
    {
      if (cmd.Keys[i])
      {
        ScaleKeys[ i % 12 ] = 1;
        found = 1;
      }
    }
    if (!found)
      ScaleKeys[0] = 1;	// avoid loop in Member()
  }

  else
  {
    for (i = 0; i < 12; i++)
      ScaleKeys[ (i + ScaleNr) % 12 ] = CMajor[i];
  }
}


int tScale::Analyze(tFilter *f)
{
  int i, s, scale;
  unsigned long min;

  long keys[12];
  for (i = 0; i < 12; i++)
    keys[i] = 0;

  tSelectedKeys cmd(f);
  cmd.Execute(0);
  for (i = 0; i < 128; i++)
    keys[i % 12] += cmd.Keys[i];

  min = (unsigned long)-1;	// max ulong
  scale = 0;
  for (s = 0; s < 12; s++)
  {
    int i;
    tScale sc;
    sc.Init(s);
    long err = 0;
    for (i = 0; i < 12; i++)
    {
      if (sc.ScaleKeys[i] == 0)
        err += keys[i];
    }
    if (err < min)
    {
      scale = s;
      min   = err;
    }
  }
  return scale;
}


int tScale::Next(int Key)
{
  do
    ++ Key;
  while (!Member(Key));
  return Key;
}

int tScale::Prev(int Key)
{
  do
    -- Key;
  while (!Member(Key));
  return Key;
}

int tScale::Transpose(int Key, int Steps)
{
  int Offset = 0;

  while (!Member(Key))
  {
    ++ Key;
    ++ Offset;
  }

  while (Steps > 0)
  {
    Key = Next(Key);
    -- Steps;
  }
  while (Steps < 0)
  {
    Key = Prev(Key);
    ++ Steps;
  }
  return Key - Offset;
}


int tScale::FitInto(int Key)
{
  int Offset = 0;

  while (!Member(Key))
  {
    ++ Offset;
    if (Offset & 1)
      Key += Offset;
    else
      Key -= Offset;
  }
  return Key;
}

// ************************************************************************
// tCommand
// ************************************************************************

tCommand::tCommand(tFilter *f)
{
  Filter = f;
  Song = f->Song;
  Reverse = 0;
}

void tCommand::Execute(int NewUndo)
{
  wxBeginBusyCursor();
  if (NewUndo)
    Song->NewUndoBuffer();
  tTrackIterator Tracks(Filter, Reverse);
  tTrack *t = Tracks.First();
  while (t)
  {
    ExecuteTrack(t);
    t = Tracks.Next();
  }
  wxEndBusyCursor();
}


void tCommand::ExecuteTrack(tTrack *t)
{
  tEventIterator Iterator(t);
  tEvent *e = Iterator.Range(Filter->FromClock, Filter->ToClock);
  while (e)
  {
    if (Filter->IsSelected(e))
      ExecuteEvent(t, e);
    e = Iterator.Next();
  }
  t->Cleanup();
}


void tCommand::ExecuteEvent(tTrack *t, tEvent *e)
{
}

long tCommand::Interpolate(long clk, long vmin, long vmax)
{
  long cmin = Filter->FromClock;
  long cmax = Filter->ToClock;
  return (clk - cmin) * (vmax - vmin) / (cmax - cmin) + vmin;
}

// ***********************************************************************
// tCmdShift
// ***********************************************************************

tCmdShift::tCmdShift(tFilter *f, long dclk)
  : tCommand(f)
{
  DeltaClock = dclk;
}

void tCmdShift::ExecuteEvent(tTrack *t, tEvent *e)
{
  tEvent *c = e->Copy();
  t->Kill(e);
  c->Clock += DeltaClock;
  t->Put(c);
}

// ************************************************************************
// tCmdErase
// ************************************************************************

tCmdErase::tCmdErase(tFilter *f, int lvsp)
  : tCommand(f)
{
  LeaveSpace = lvsp;
}

void tCmdErase::Execute(int NewUndo)
{
  tCommand::Execute(NewUndo);
  if (!LeaveSpace)
  {
    tFilter f(Filter);
    f.FromClock = Filter->ToClock;
    f.ToClock   = Song->GetLastClock() + 1;
    long DeltaClock = Filter->FromClock - Filter->ToClock;
    tCmdShift shift(&f, DeltaClock);
    shift.Execute(0);
  }
}

void tCmdErase::ExecuteEvent(tTrack *t, tEvent *e)
{
  t->Kill(e);
}

// ************************************************************************
// tCmdQuantize
// ************************************************************************

tCmdQuantize::tCmdQuantize(tFilter *f, long clks)
  : tCommand(f)
{
  QntClocks = clks;
  NoteStart = 1;
  NoteLength = 0;
}

long tCmdQuantize::Quantize(long Clock)
{
  Clock += QntClocks / 2;
  Clock -= Clock % QntClocks;
  return Clock;
}

void tCmdQuantize::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k;
  if ((k = e->IsKeyOn()) != 0)
  {
    k = (tKeyOn *)e->Copy();
    if (NoteStart)
      k->Clock = Quantize(k->Clock);
    if (NoteLength)
      k->Length = Quantize(k->Length);
    t->Kill(e);
    t->Put(k);
  }
}

// ************************************************************************
// tCmdTranspose
// ************************************************************************

tCmdTranspose::tCmdTranspose(tFilter *f, int notes, int ScaleNr, int fit)
  : tCommand(f)
{
  Scale.Init(ScaleNr, Filter);
  Notes = notes;
  FitIntoScale = fit;
}

void tCmdTranspose::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k;
  if (e->IsKeyOn())
  {
    k = (tKeyOn *)e->Copy();
    if (FitIntoScale)
    {
      k->Key += Notes;
      k->Key = Scale.FitInto(k->Key);
    }
    else if (Notes)
      k->Key = Scale.Transpose(k->Key, Notes);
    t->Kill(e);
    t->Put(k);
  }
}

// ************************************************************************
// tCmdSetChannel
// ************************************************************************

tCmdSetChannel::tCmdSetChannel(tFilter *f, int chan)
  : tCommand(f)
{
  NewChannel = chan;
}

void tCmdSetChannel::ExecuteEvent(tTrack *t, tEvent *e)
{
  tChannelEvent *c;

  if ((c = e->IsChannelEvent()) != 0)
  {
    c = (tChannelEvent *)e->Copy();
    c->Channel = NewChannel;
    t->Kill(e);
    t->Put(c);
  }
}

// ************************************************************************
// tCmdVelocity
// ************************************************************************

tCmdVelocity::tCmdVelocity(tFilter *f, int from, int to, int m)
  : tCommand(f)
{
  FromValue = from;
  ToValue  = to;
  Mode = m;
}

void tCmdVelocity::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k;

  if (e->IsKeyOn() != 0)
  {
    k = (tKeyOn *)e->Copy();
    long val = 0;
    if (ToValue <= 0)
      val = FromValue;
    else
      val = Interpolate(k->Clock, FromValue, ToValue);
    switch (Mode) {
      case 0: break;
      case 1: val = k->Veloc + val; break;
      case 2: val = k->Veloc - val; break;
    }
    k->Veloc = val < 1 ? 1 : (val > 127 ? 127 : val);
    t->Kill(e);
    t->Put(k);
  }
}

// ************************************************************************
// tCmdLength
// ************************************************************************

tCmdLength::tCmdLength(tFilter *f, int from, int to, int m)
  : tCommand(f)
{
  FromValue = from;
  ToValue  = to;
  Mode = m;
}

void tCmdLength::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k;

  if (e->IsKeyOn() != 0)
  {
    k = (tKeyOn *)e->Copy();
    long val = 0;
    if (ToValue <= 0)
      val = FromValue;
    else
      val = Interpolate(k->Clock, FromValue, ToValue);
    switch (Mode) {
      case 0: break;
      case 1: val = k->Length + val; break;
      case 2: val = k->Length - val; break;
    }

    k->Length = val < 1 ? 1 : val;
    t->Kill(e);
    t->Put(k);
  }
}

// ************************************************************************
// tCmdCleanup
// ************************************************************************

tCmdCleanup::tCmdCleanup(tFilter *f, long clks)
  : tCommand(f)
{
  lengthLimit = clks;
}

void tCmdCleanup::ExecuteEvent(tTrack *t, tEvent *e)
{
  tKeyOn *k;
  if ((k = e->IsKeyOn()) != 0)
  {
    if (k->Length < lengthLimit)
    {
    	t->Kill(e);
    }
  }
}

// ************************************************************************
// tCmdSearchReplace
// ************************************************************************

tCmdSearchReplace::tCmdSearchReplace(tFilter *f, short sf, short st)
  : tCommand(f)
{
  fr = sf;
  to = st;
}

void tCmdSearchReplace::ExecuteEvent(tTrack *t, tEvent *e)
{
  tControl *ctrl;
  if ((ctrl = e->IsControl()) != 0)
  {
    if (ctrl->Control == fr)
    {
      tControl *copy = (tControl *)ctrl->Copy();
      copy->Control = to;
      t->Kill(ctrl);
      t->Put(copy);
    }
  }
}

// ************************************************************************
// tCmdCopyToBuffer
// ************************************************************************

tCmdCopyToBuffer::tCmdCopyToBuffer(tFilter *f, tEventArray *buf)
  : tCommand(f)
{
  Buffer = buf;
}

void tCmdCopyToBuffer::ExecuteEvent(tTrack *t, tEvent *e)
{
  Buffer->Put(e->Copy());
}

// **********************************************************************
// tCmdCopy
// **********************************************************************



tCmdCopy::tCmdCopy(tFilter *f, long dt, long dc)
  : tCommand(f)
{
  DestTrack = dt;
  DestClock = dc;

  EraseSource = 0;	// no
  EraseDestin = 1;	// yes
  InsertSpace = 0;	// no
  RepeatClock = -1;	// -1L

  Reverse = DestTrack > Filter->FromTrack;
  if (Reverse)
    DestTrack += Filter->ToTrack - Filter->FromTrack; // ToTrack inclusive
}



void tCmdCopy::ExecuteTrack(tTrack *s)
{
  long StartClock, StopClock;
  tTrack *d;

  StartClock = DestClock;

  if (RepeatClock < 0)
    StopClock = StartClock + Filter->ToClock - Filter->FromClock;
  else
    StopClock = RepeatClock;

  d = Song->GetTrack(DestTrack);

  if (Reverse)
  {
    if (DestTrack)
      -- DestTrack;
  }
  else
    ++ DestTrack;

  if (s && d)
  {

    // Events nach tmp kopieren
    tEventArray tmp;
    {
      tEventIterator Iterator(s);
      long  DeltaClock = StartClock - Filter->FromClock;
      tEvent *e = Iterator.Range(Filter->FromClock, Filter->ToClock);
      while (e)
      {
	long NewClock = e->Clock + DeltaClock;
	if (NewClock >= StopClock)
	  break;

	if (Filter->IsSelected(e))
	{
	  tEvent *cpy = e->Copy();
	  cpy->Clock = NewClock;
	  tmp.Put(cpy);
	}

	e = Iterator.Next();
	if (!e)
	{
	  e = Iterator.First();
	  DeltaClock += Filter->ToClock - Filter->FromClock;
	}
      }
    }

    // ggf Freien Platz einfuegen

    if (InsertSpace && d->GetLastClock() > StartClock)
    {
      tEventIterator Iterator(d);
      tEvent *e = Iterator.Range(StartClock, d->GetLastClock() + 1);
      long DeltaClock = StopClock - StartClock;
      while (e)
      {
	if (Filter->IsSelected(e))
	{
	  tEvent *c = e->Copy();
	  c->Clock += DeltaClock;
	  d->Kill(e);
	  d->Put(c);
	}
	e = Iterator.Next();
      }
      d->Cleanup();
    }

    // ggf Quelle loeschen

    if (EraseSource)
    {
      tEventIterator Iterator(s);
      tEvent *e = Iterator.Range(Filter->FromClock, Filter->ToClock);
      while (e)
      {
	if (Filter->IsSelected(e))
	  s->Kill(e);
	e = Iterator.Next();
      }
      s->Cleanup();
    }

    // ggf Ziel loeschen

    if (EraseDestin)
    {
      tEventIterator Iterator(d);
      tEvent *e = Iterator.Range(StartClock, StopClock);
      while (e)
      {
	if (Filter->IsSelected(e))
	  d->Kill(e);
	e = Iterator.Next();
      }
      d->Cleanup();
    }

    // tmp und Zieltrack zusammenmischen, aufraeumen

    d->Merge(&tmp);
    d->Cleanup();
  }
}

// ************************************************************************
// tCmdExchLeftRight
// ************************************************************************

tCmdExchLeftRight::tCmdExchLeftRight(tFilter *f)
  : tCommand(f)
{
}

void tCmdExchLeftRight::ExecuteEvent(tTrack *t, tEvent *e)
{
  if (e->IsKeyOn())
  {
    tKeyOn *k = (tKeyOn *)e->Copy();
    k->Clock = Filter->FromClock + Filter->ToClock - k->Clock;
    t->Kill(e);
    t->Put(k);
  }
}


// ************************************************************************
// tCmdExchUpDown
// ************************************************************************

tCmdExchUpDown::tCmdExchUpDown(tFilter *f)
  : tCommand(f)
{
}

void tCmdExchUpDown::ExecuteTrack(tTrack *t)
{
  int i;
  int Keys[128];
  tEvent *e;

  // find all Key's selected

  for (i = 0; i < 128; i++)
    Keys[i] = 0;

  tEventIterator Iterator(t);
  e = Iterator.Range(Filter->FromClock, Filter->ToClock);
  while (e)
  {
    if (Filter->IsSelected(e) && e->IsKeyOn())
    {
      tKeyOn *k = (tKeyOn *)e;
      Keys[k->Key] = 1;
    }
    e = Iterator.Next();
  }

  // reverse Key's

  e = Iterator.Range(Filter->FromClock, Filter->ToClock);
  while(e)
  {
    if (Filter->IsSelected(e) && e->IsKeyOn())
    {
      tKeyOn *k = (tKeyOn *)e->Copy();
      int n_th = 0;
      // the n'th key from bottom ..
      for (i = 0; i <= k->Key; i++)
        n_th += Keys[i];
      // .. becomes the n'th key from top
      for (i = 127; i > 0 && n_th; --i)
        n_th -= Keys[i];
      k->Key = i + 1;

      t->Kill(e);
      t->Put(k);
    }
    e = Iterator.Next();
  }
  t->Cleanup();
}

