/*
**  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 "random.h"
#include <assert.h>


#ifdef wx_msw
#include <stdlib.h>
double tRandomGenerator::asDouble()
{
  return double(rand()) / double(RAND_MAX);
}
tRandomGenerator rnd;
#else
#ifndef VMS
ACG rnd(0, 55);
#else
#include <stdlib.h>
double tRandomGenerator::asDouble()
{
  return double(rand()) / double(RAND_MAX);
}
tRandomGenerator rnd;
#endif
#endif


// Array of probabilities

tRndArray::tRndArray(int nn, int mmin, int mmax)
{
  int i;
  n = nn;
  for (i = 0; i < n; i++)
    array[i] = mmin;
  min = mmin;
  max = mmax;
  nul = min > 0 ? min : 0;
}



tRndArray::~tRndArray()
{
}


tRndArray & tRndArray::operator = (const tRndArray &o)
{
  if (this == &o)
    return *this;

  array = o.array;
  n = o.n;
  min = o.min;
  max = o.max;
  nul = o.nul;
  return *this;
}


tRndArray::tRndArray(const tRndArray &o)
  : array(o.array)
{
  n = o.n;
  min = o.min;
  max = o.max;
  nul = o.nul;
}


int tRndArray::Random()
{
  double sum, dec;
  int i;

  assert(n > 0);

  sum = 0.0;
  for (i = 0; i < n; i++)
  {
    assert(array[i] >= 0);
    sum += array[i];
  }
  if (sum <= 0)
    return 0;

  dec = sum * rnd.asDouble();
  assert(dec < sum);

  i = 0;
  while (dec >= 0.0)
  {
    dec -= array[i];
    i++;
  }
  i--;

  assert(i >= 0 && i < n);
  return i;
}


int tRndArray::Interval(int seed)
{
  if (seed < 0)		// initial ?
    seed = int(rnd.asDouble() * n);
  int delta = Random();
  if (rnd.asDouble() < 0.5)
    delta = -delta;
  seed = (seed + n + delta) % n;
  return seed;
}

int tRndArray::Random(int i)
{
  return rnd.asDouble() * (max - min) < array[i];
}


ostream & operator << (ostream &os, tRndArray const &a)
{
  int i;

  os << a.n << " " << a.min << " " << a.max << endl;
  for (i = 0; i < a.n; i++)
    os << a.array[i] << " ";
  os << endl;
  return os;
}


istream & operator >> (istream &is, tRndArray &a)
{
  int i;
  is >> a.n >> a.min >> a.max;
  for (i = 0; i < a.n; i++)
    is >> a.array[i];
  return is;
}


// --------------------------------- tArrayEdit -------------------------------------

tArrayEdit::tArrayEdit(wxFrame *frame, tRndArray &ar, long xx, long yy, long ww, long hh, int sty)
  : wxCanvas(frame, xx, yy, ww, hh),
    array(ar),
    n(ar.n),
    min(ar.min),
    max(ar.max),
    nul(ar.nul)
{
  enabled = 1;
  dragging = 0;
  index = -1;
  label = 0;
  style_bits = sty;

  step = 4;
  fac  = 1;

  x = xx;
  y = yy;
  w = ww;
  h = hh;

  float tw, th;
  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);
  dc->GetTextExtent("123", &tw, &th);
  if (style_bits & ARED_TICKS)
  {
    // leave space for bottomline
    h -= (int)th;
  }

  if (style_bits & ARED_MINMAX)
  {
    // leave space to display min / max
    x += (int)tw;
    w -= (int)tw;
  }

  // draw to topleft corner of canvas
  x = y = 0;

  ynul = y + h - h * (nul - min) / (max - min);
// printf("ctor: n %d, min %d, max %d, nul %d, x %d, y %d, w %d, h %d, ynul %d\n", n, min, max, nul, x, y, w, h, ynul);
}


void tArrayEdit::OnSize(int ww, int hh)
{
  w = ww;
  h = hh;
  wxCanvas::OnSize(w, h);
  if (style_bits & ARED_TICKS)
  {
    float tw, th;
    GetDC()->GetTextExtent("123", &tw, &th);
    h -= (int)th;
  }
  ynul = y + h - h * (nul - min) / (max - min);
// printf("OnSize: n %d, min %d, max %d, nul %d, x %d, y %d, w %d, h %d, ynul %d\n", n, min, max, nul, x, y, w, h, ynul);
}

tArrayEdit::~tArrayEdit()
{
  delete [] label;
}

void tArrayEdit::DrawBar(int i, int black)
{
  wxDC *dc = GetDC();

  int gap = 0;
  if (style_bits & ARED_GAP)
  {
    gap = w / n / 6;
    if (!gap && w / n > 3)
      gap = 1;
  }
  long xbar, ybar, wbar, hbar;

  wbar = w / n - 2 * gap;
  xbar = x + i * w / n + gap;
#if 0
  hbar = h * (array[i] - min) / (max - min);
  ybar = y + h - hbar;
#else
  hbar = h * (array[i] - nul) / (max - min);
  if (hbar < 0)
  {
    ybar = ynul;
    hbar = -hbar;
  }
  else
    ybar = ynul - hbar;
#endif

// printf("array[i] %d, n %d, xbar %d, ybar %d, wbar %d, hbar %d, min %d, max %d, nul %d, x %d, y %d, w %d, h %d, ynul %d\n", array[i], n, xbar, ybar, wbar, hbar, min, max, nul, x, y, w, h, ynul);
  if (ybar == y)
    ++ybar, --hbar;

  if (!black)
  {
    dc->SetBrush(wxWHITE_BRUSH);
    dc->SetPen(wxWHITE_PEN);
  }
  if (wbar && hbar)
    dc->DrawRectangle(xbar, ybar, wbar, hbar);
  if (!black)
  {
    dc->SetBrush(wxBLACK_BRUSH);
    dc->SetPen(wxBLACK_PEN);
  }
}


void tArrayEdit::DrawTicks()
{
  char buf[20];
  float tw, th;

  if (!(style_bits & ARED_TICKS))
    return;

  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);

  // tick marks
  int i;
  for (i = 0; step && i < n; i += step)
  {
    int mark = ((int)(i * fac + 0.5)) + 1;
    sprintf(buf, "%d", mark);
    float yy = y + h;
    float xx = x + (i + 0.5) * w / n;
    dc->GetTextExtent(buf, &tw, &th);
    xx -= tw/2.0;
    dc->DrawText(buf, xx, yy);
  }
  dc->SetFont(wxNORMAL_FONT);
}

void tArrayEdit::DrawMinMax()
{
  if (!(style_bits & ARED_MINMAX))
    return;

  char buf[20];
  float tw, th;

  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);

  // min/max
  sprintf(buf, "%d", max);
  dc->GetTextExtent(buf, &tw, &th);
  dc->DrawText(buf, x - tw, y);
  sprintf(buf, "%d", min);
  dc->GetTextExtent(buf, &tw, &th);
  dc->DrawText(buf, x - tw, y + h - th);

  dc->SetFont(wxNORMAL_FONT);
}

void tArrayEdit::DrawLabel()
{
  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);
  if (label)
    dc->DrawText(label, x + 5, y);
  dc->SetFont(wxNORMAL_FONT);
}



void tArrayEdit::OnPaint()
{
  int i;
  wxDC *dc = GetDC();

  // surrounding rectangle
  dc->Clear();
  if (enabled)
    dc->SetBrush(wxWHITE_BRUSH);
  else
    dc->SetBrush(wxGREY_BRUSH);
  dc->SetPen(wxBLACK_PEN);
  if (w && h)
    dc->DrawRectangle(x, y, w, h);

  // sliders
  dc->SetBrush(wxBLACK_BRUSH);
  for (i = 0; i < n; i++)
    DrawBar(i, 1);

  DrawTicks();
  DrawLabel();
  DrawMinMax();

  // draw null line
  if (min < nul && nul < max)
  {
    dc->SetPen(wxCYAN_PEN);
    dc->DrawLine(x, ynul, x+w, ynul);
    dc->SetPen(wxBLACK_PEN);
  }
}



void tArrayEdit::SetTicks(int s, double f)
{
  step = s;
  fac  = f;
}

int tArrayEdit::Index(wxMouseEvent &e)
{
  float ex, ey;
  e.Position(&ex, &ey);
  int i = (int)( (ex - x) * n / w);
  i = i < 0 ? 0 : i;
  i = i >= n ? n-1 : i;
  return i;
}

int tArrayEdit::Dragging(wxMouseEvent &e)
{
  if (!dragging)
    return 0;

  if (index < 0)
    index = Index(e);

  int val = nul;
  if (e.LeftIsDown())
  {
    float ex, ey;
    e.Position(&ex, &ey);
    val = (int)( (y + h - ey) * (max - min) / h + min);
    val = val > max ? max : val;
    val = val < min ? min : val;
  }

  if (e.ShiftDown())
  {
    int k;
    for (k = 0; k < n; k++)
    {
      DrawBar(k, 0);
      array[k] = val;
      DrawBar(k, 1);
    }
  }
  else if (e.ControlDown())
  {
    DrawBar(index, 0);
    array[index] = val;
    DrawBar(index, 1);
  }
  else
  {
    int i = Index(e);
    int k = i;
    if (i < index)
      for (; i <= index; i++)
      {
	DrawBar(i, 0);
	array[i] = val;
	DrawBar(i, 1);
      }
    else
      for (; i >= index; i--)
      {
	DrawBar(i, 0);
	array[i] = val;
	DrawBar(i, 1);
      }
    index = k;
  }

  return 0;
}

int tArrayEdit::ButtonDown(wxMouseEvent &e)
{
  dragging = 1;
  index = Index(e);
  Dragging(e);
  return 0;
}

int tArrayEdit::ButtonUp(wxMouseEvent &e)
{
  dragging = 0;
  index    = -1;
  DrawLabel();
  return 0;
}


void tArrayEdit::OnEvent(wxMouseEvent &e)
{
  if (e.ButtonDown())
    ButtonDown(e);
  else if (e.Dragging())
    Dragging(e);
  else if (e.ButtonUp())
    ButtonUp(e);
}

void tArrayEdit::Enable(int e)
{
  enabled = e;
}

void tArrayEdit::SetLabel(char const *llabel)
{
  delete label;
  label = copystring(llabel);
}


tRhyArrayEdit::tRhyArrayEdit(wxFrame *parent, tRndArray &array, long xx, long yy, long ww, long hh)
  : tArrayEdit(parent, array, xx, yy, ww, hh)
{
  steps_per_count = 4;
  count_per_bar   = 4;
  n_bars          = 4;
}

void tRhyArrayEdit::SetMeter(int s, int c, int b)
{
  steps_per_count = s;
  count_per_bar   = c;
  n_bars          = b;
  array.Resize(s * c * b);
  //OnPaint();
}


void tRhyArrayEdit::DrawTicks()
{
  char buf[20];
  float tw, th;

  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);

  // tick marks
  assert(steps_per_count && count_per_bar && n_bars);
  int i;
  for (i = 0; i < n; i += steps_per_count)
  {
    int mark = (i / steps_per_count) % count_per_bar + 1;
    sprintf(buf, "%d", mark);
    float yy = y + h;
    float xx = x + (i + 0.5) * w / n;
    dc->GetTextExtent(buf, &tw, &th);
    xx -= tw/2.0;
    dc->DrawText(buf, xx, yy);
  }
  dc->SetFont(wxNORMAL_FONT);
}

