/*
 *
 *    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 "amplify.h"

#include "data.h"
#include "partwidget.h"
#include "actionmanager.h"

#include "editapplydialog.h"
#include "controls/slider.h"

#include "sonik_util.h"

#include <kaction.h>
#include <klocale.h>
#include <kgenericfactory.h>
#include <kmessagebox.h>
#include <kdialogbase.h>
#include <kdebug.h>

#include <qvbox.h>

using Sonik::AmplifyPlugin;

namespace Sonik
{
  class AmplifyProcessor : public Edit::Processor
  {
  public:
    AmplifyProcessor(float s, float e) : mGainStart(s), mGainEnd(e) { }

    virtual bool prepare(uint8_t, std::size_t len, uint32_t, std::size_t)
    {
      mGain = mGainStart;
      mDeltaGain = (mGainEnd - mGainStart) / len;

      return true;
    }

    virtual void apply(SampleSegment& seg)
    {
      float g = mGain;
      for (uint8_t channel = 0; channel < seg.rows(); ++channel)
      {
        g = mGain;
        Sample *p = seg.data(channel), *e = seg.end(channel);
        for ( ; p != e; ++p)
        {
          *p *= mGain;
          g += mDeltaGain;
        }
      }
      mGain = g;
    }

    virtual void cleanup() { }

  private:
    float mGainStart;
    float mGainEnd;

    float mGain;
    float mDeltaGain;
  };
}

AmplifyPlugin::AmplifyPlugin(QObject* parent, const char* name,
                             const QStringList& args)
  : Sonik::Edit("amplify", i18n("Amplify"), parent, name, args)
{
  new KAction(i18n("Amplify"), 0,
              this, SLOT(uiTransformAmplify()),
              actionCollection(),
              "transform_amplify");
  new KAction(i18n("Fade In"), 0,
              this, SLOT(uiTransformFadeIn()),
              actionCollection(),
              "transform_fadein");
  new KAction(i18n("Fade Out"), 0,
              this, SLOT(uiTransformFadeOut()),
              actionCollection(),
              "transform_fadeout");
}

AmplifyPlugin::~AmplifyPlugin()
{
}

QWidget* AmplifyPlugin::makeConfigPage(QWidget*)
{
  return 0;
}

void AmplifyPlugin::applyConfigPage()
{
}

void AmplifyPlugin::uiTransformAmplify()
{
  PluginContext* context = new PluginContext;

  QHBox* box = new QHBox();
  context->controls.push_back(
    new UiControl("gain", i18n("Gain"), UiControl::VSLIDER,
                  0.0f, -24.0f, 24.0f, 2, false, box)
    );

  pluginDialog("amplify_amplify", box, i18n("Amplify"),
               SLOT(applyTransformAmplify(Sonik::Edit::PluginContext*)),
               context);
}

void AmplifyPlugin::applyTransformAmplify(PluginContext* context)
{
  UiControl& gain = *context->controls.front();
  float g = dBToVal(gain.value());

  kdDebug(60606) << "AmplifyPlugin::uiTransformAmplify: " << g << "\n";

  AmplifyProcessor p(g, g);
  mActionManager->beginCompoundAction(i18n("Amplify"));
  apply(p);
  mActionManager->endCompoundAction();
}

void AmplifyPlugin::uiTransformFadeIn()
{
  PluginContext* context = new PluginContext;
  QHBox* box = new QHBox();

  context->controls.push_back(
    new UiControl("gain_start", i18n("Initial Gain"), UiControl::VSLIDER,
                  -24.0f, -24.0f, 24.0f, 2, false, box)
    );

  context->controls.push_back(
    new UiControl("gain_end", i18n("Final Gain"), UiControl::VSLIDER,
                  0.0f, -24.0f, 24.0f, 2, false, box)
    );

  pluginDialog("amplify_fade_in", box, i18n("Fade In"),
               SLOT(applyTransformFadeIn(Sonik::Edit::PluginContext*)),
               context);
}

void AmplifyPlugin::applyTransformFadeIn(PluginContext* context)
{
  UiControl& gainS = *context->controls[0];
  UiControl& gainE = *context->controls[1];

  float gS = dBToVal(gainS.value());
  float gE = dBToVal(gainE.value());

  AmplifyProcessor p(gS, gE);
  mActionManager->beginCompoundAction(i18n("Fade In"));
  apply(p);
  mActionManager->endCompoundAction();
}

void AmplifyPlugin::uiTransformFadeOut()
{
  PluginContext* context = new PluginContext;
  QHBox* box = new QHBox();

  context->controls.push_back(
    new UiControl("gain_start", i18n("Initial Gain"), UiControl::VSLIDER,
                  0.0f, -24.0f, 24.0f, 2, false, box)
    );

  context->controls.push_back(
    new UiControl("gain_end", i18n("Final Gain"), UiControl::VSLIDER,
                  -24.0f, -24.0f, 24.0f, 2, false, box)
    );

  pluginDialog("amplify_fade_out", box, i18n("Fade Out"),
               SLOT(applyTransformFadeOut(Sonik::Edit::PluginContext*)),
               context);
}

void AmplifyPlugin::applyTransformFadeOut(PluginContext* context)
{
  UiControl& gainS = *context->controls[0];
  UiControl& gainE = *context->controls[1];

  float gS = dBToVal(gainS.value());
  float gE = dBToVal(gainE.value());

  AmplifyProcessor p(gS, gE);
  mActionManager->beginCompoundAction(i18n("Fade Out"));
  apply(p);
  mActionManager->endCompoundAction();
}

//
// Factory definition
//
K_EXPORT_COMPONENT_FACTORY(libsonik_editamplify,
                           KGenericFactory<AmplifyPlugin>(
                             "sonikpart-edit-amplify")
                           );
