/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-2006  Robert Walker <rob@tenfoot.org.uk>
 *
 *    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
#include "dialogs.h"

#include "controls/slider.h"

#include <kdebug.h>
#include <klocale.h>
#include <knuminput.h>

#include <qradiobutton.h>
#include <qgrid.h>
#include <qhgroupbox.h>
#include <qpushbutton.h>
#include <qbuttongroup.h>

using Sonik::InsertChannelsDialog;
using Sonik::RemoveChannelsDialog;
using Sonik::MonoToStereoDialog;
using Sonik::StereoToMonoDialog;
using Sonik::MixDialog;

namespace
{
  QString channelName(size_t channel, size_t numChannels)
  {
    QString name;

    if (numChannels == 1)
      name = "";
    else if (numChannels == 2)
    {
      if (channel == 0)
        name = i18n("Left");
      else
        name = i18n("Right");
    }
    else
      name = QString(i18n("Channel %1")).arg(channel + 1);

    return name;
  }
}

InsertChannelsDialog::InsertChannelsDialog(size_t inChannels,
                                           QWidget* parent, const char* name)
  : InsertChannelsDialogBase(parent, name, true, 0),
    mGain(inChannels)
{
  channelCount->setMinValue(1);
  channelCount->setValue(1);

  afterChannel->setMinValue(1);
  afterChannel->setMaxValue(inChannels);
  afterChannel->setValue(1);

  connect(mixButton, SIGNAL(clicked()),
          this, SLOT(mixButtonClicked()));
  connect(channelCount, SIGNAL(valueChanged(int)),
          this, SLOT(recalculateGain()));
  connect(whereGroup, SIGNAL(clicked(int)),
          this, SLOT(recalculateGain()));
  connect(afterChannel, SIGNAL(valueChanged(int)),
          this, SLOT(recalculateGain()));

  recalculateGain();
}

void InsertChannelsDialog::mixButtonClicked()
{
  MixDialog dlg(mGain, parentWidget(), "channel_mix_dlg");

  if (dlg.exec() == QDialog::Accepted)
    mGain = dlg.gain();
}

void InsertChannelsDialog::recalculateGain()
{
  size_t outChannels = mGain.inChannels() + channelCount->value();

  size_t insertBefore;

  // work out insertion point
  //  channels are 0 based, but spin boxes 1 based
  if (beforeFirst->isChecked())
  {
    insertBefore = 0;
  }
  else
  {
    insertBefore = afterChannel->value();
  }

  mGain.resize(mGain.inChannels(), outChannels);

  mGain.zero();

  for (size_t i = 0; i < insertBefore; ++i)
    mGain[i][i] = 1.0f;

  for (size_t i = insertBefore; i < mGain.inChannels(); ++i)
    mGain[i][i+channelCount->value()] = 1.0f;
}

RemoveChannelsDialog::RemoveChannelsDialog(size_t inChannels,
                                           QWidget* parent, const char* name)
  : RemoveChannelsDialogBase(parent, name, true, 0),
    mGain(inChannels)
{
  channelCount->setMinValue(1);
  channelCount->setMaxValue(inChannels-1);
  channelCount->setValue(1);

  startChannel->setMinValue(1);
  startChannel->setMaxValue(inChannels);

  connect(startChannel, SIGNAL(valueChanged(int)),
          this, SLOT(startChannelChanged(int)));
  connect(channelCount, SIGNAL(valueChanged(int)),
          this, SLOT(recalculateGain()));

  connect(mixButton, SIGNAL(clicked()),
          this, SLOT(mixButtonClicked()));

  recalculateGain();
}

void RemoveChannelsDialog::mixButtonClicked()
{
  MixDialog dlg(mGain, parentWidget(), "channel_mix_dlg");

  if (dlg.exec() == QDialog::Accepted)
    mGain = dlg.gain();
}

void RemoveChannelsDialog::startChannelChanged(int value)
{
  channelCount->setMaxValue(mGain.inChannels() - (value - 1));

  recalculateGain();
}

void RemoveChannelsDialog::recalculateGain()
{
  size_t outChannels = mGain.inChannels() - channelCount->value();

  mGain.resize(mGain.inChannels(), outChannels);

  mGain.zero();

  for (size_t i = 0; i < (size_t)startChannel->value() - 1; ++i)
    mGain[i][i] = 1.0f;

  for (size_t i = startChannel->value() - 1 + channelCount->value();
       i < mGain.inChannels();
       ++i)
    mGain[i][i-channelCount->value()] = 1.0f;
}

MonoToStereoDialog::MonoToStereoDialog(QWidget* parent, const char* name)
  : SimpleChannelDialogBase(parent, name, true, 0),
    mGain(1, 2)
{
  setCaption(i18n("Convert to Stereo"));
  left->setText(i18n("Existing data in left channel"));
  right->setText(i18n("Existing data in right channel"));
  mix->setText(i18n("Existing data in both channels"));

  connect(mixButton, SIGNAL(clicked()),
          this, SLOT(mixButtonClicked()));

  mGain[0][0] = 1.0f;
  mGain[0][1] = 1.0f;
}

void MonoToStereoDialog::accept()
{
  if (left->isChecked())
  {
    mGain[0][0] = 1.0f;
    mGain[0][1] = 0.0f;
  }
  else if (right->isChecked())
  {
    mGain[0][0] = 0.0f;
    mGain[0][1] = 1.0f;
  }

  SimpleChannelDialogBase::accept();
}

void MonoToStereoDialog::mixButtonClicked()
{
  MixDialog dlg(mGain, parentWidget(), "channel_mix_dlg");

  if (dlg.exec() == QDialog::Accepted)
    mGain = dlg.gain();
}

StereoToMonoDialog::StereoToMonoDialog(QWidget* parent, const char* name)
  : SimpleChannelDialogBase(parent, name, true, 0),
    mGain(2, 1)
{
  setCaption(i18n("Convert to Mono"));
  left->setText(i18n("Keep left channel"));
  right->setText(i18n("Keep right channel"));
  mix->setText(i18n("Mix both channels"));

  connect(mixButton, SIGNAL(clicked()),
          this, SLOT(mixButtonClicked()));

  mGain[0][0] = 0.5f;
  mGain[1][0] = 0.5f;
}

void StereoToMonoDialog::accept()
{
  if (left->isChecked())
  {
    mGain[0][0] = 1.0f;
    mGain[1][0] = 0.0f;
  }
  else if (right->isChecked())
  {
    mGain[0][0] = 0.0f;
    mGain[1][0] = 1.0f;
  }

  SimpleChannelDialogBase::accept();
}

void StereoToMonoDialog::mixButtonClicked()
{
  MixDialog dlg(mGain, parentWidget(), "channel_mix_dlg");

  if (dlg.exec() == QDialog::Accepted)
    mGain = dlg.gain();
}

MixDialog::MixDialog(const Sonik::GainMatrix& gain,
                     QWidget* parent, const char* name)
  : KDialogBase(parent, name, true, i18n("Mix channels"),
                KDialogBase::Ok|KDialogBase::Cancel,
                KDialogBase::Ok),
    mGain(gain)
{
  size_t cols = 4;

  if (mGain.outChannels() == 5 || mGain.outChannels() == 6)
  {
    cols = 3;
  }

  QGrid* grid = makeGridMainWidget(cols, Qt::Horizontal);

  // resize sliders to match gain
  mSliders.resize(mGain.inChannels());
  for (size_t ci = 0; ci < mGain.inChannels(); ++ci)
  {
    mSliders[ci].resize(mGain.outChannels());
  }

  // create boxes & sliders
  for (size_t co = 0; co < mGain.outChannels(); ++co)
  {
    QString name;

    QHGroupBox* outBox = new QHGroupBox(channelName(co, mGain.outChannels()), grid);

    for (size_t ci = 0; ci < mGain.inChannels(); ++ci)
    {
      mSliders[ci][co] = new Sonik::Slider(channelName(ci, mGain.inChannels()),
                                           0.0f, 1.0f, 2, 0.1f, false, Qt::Vertical, outBox);
      mSliders[ci][co]->setValue(mGain[ci][co]);
    }
  }
}

void MixDialog::accept()
{
  // copy gain from sliders
  for (size_t co = 0; co < mGain.outChannels(); ++co)
    for (size_t ci = 0; ci < mGain.inChannels(); ++ci)
      mGain[ci][co] = mSliders[ci][co]->value();

  KDialogBase::accept();
}
