// Aseprite
// Copyright (C) 2019-2022  Igara Studio S.A.
// Copyright (C) 2017-2018  David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include "app/color.h"
#include "app/commands/command.h"
#include "app/commands/filters/filter_manager_impl.h"
#include "app/commands/filters/filter_window.h"
#include "app/commands/filters/filter_worker.h"
#include "app/commands/new_params.h"
#include "app/context.h"
#include "app/ini_file.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/ui/button_set.h"
#include "app/ui/color_button.h"
#include "app/ui/color_sliders.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "filters/hue_saturation_filter.h"
#include "ui/button.h"
#include "ui/label.h"
#include "ui/slider.h"
#include "ui/widget.h"
#include "ui/window.h"

namespace app {

using Mode = filters::HueSaturationFilter::Mode;

struct HueSaturationParams : public NewParams {
  Param<bool> ui{ this, true, "ui" };
  Param<filters::Target> channels{ this, 0, "channels" };
  Param<Mode> mode{ this, Mode::HSL_MUL, "mode" };
  Param<double> hue{ this, 0.0, "hue" };
  Param<double> saturation{ this, 0.0, "saturation" };
  Param<double> lightness{
    this,
    0.0,
    { "lightness", "value" }
  };
  Param<double> alpha{ this, 0.0, "alpha" };
};

static const char* ConfigSection = "HueSaturation";

class HueSaturationWindow : public FilterWindow {
public:
  HueSaturationWindow(HueSaturationFilter& filter, FilterManagerImpl& filterMgr)
    : FilterWindow("Hue/Saturation",
                   ConfigSection,
                   &filterMgr,
                   WithChannelsSelector,
                   WithoutTiledCheckBox)
    , m_filter(filter)
    , m_colorType(2)
  {
    getContainer()->addChild(&m_colorType);
    getContainer()->addChild(&m_sliders);

    static_assert(int(Mode::HSV_MUL) == 0 && int(Mode::HSL_MUL) == 1 && int(Mode::HSV_ADD) == 2 &&
                    int(Mode::HSL_ADD) == 3,
                  "Adjust widget showing filters::HueSaturationFilter::Mode enum");
    auto mode = Preferences::instance().hueSaturation.mode();
    m_colorType.addItem("HSV")->setFocusStop(false);
    m_colorType.addItem("HSL")->setFocusStop(false);
    m_colorType.addItem("HSV+")->setFocusStop(false);
    m_colorType.addItem("HSL+")->setFocusStop(false);
    m_colorType.setSelectedItem(int(mode));
    m_colorType.ItemChange.connect([this] { onChangeMode(); });

    m_sliders.setColorType(app::Color::HslType);
    m_sliders.setMode(ColorSliders::Mode::Relative);
    m_sliders.ColorChange.connect([this] { onChangeControls(); });

    initTheme();
    onChangeMode();
  }

  Mode mode() const { return Mode(m_colorType.selectedItem()); }

private:
  bool isHsl() const { return (mode() == Mode::HSL_MUL || mode() == Mode::HSL_ADD); }

  void onChangeMode()
  {
    stopPreview();

    Preferences::instance().hueSaturation.mode(mode());
    m_filter.setMode(mode());
    m_sliders.setColorType(isHsl() ? app::Color::HslType : app::Color::HsvType);

    onChangeControls();
  }

  void onChangeControls()
  {
    stopPreview();

    m_sliders.syncRelHsvHslSliders();

    if (isHsl()) {
      m_filter.setHue(double(m_sliders.getRelSliderValue(ColorSliders::Channel::HslHue)));
      m_filter.setSaturation(m_sliders.getRelSliderValue(ColorSliders::Channel::HslSaturation) /
                             100.0);
      m_filter.setLightness(m_sliders.getRelSliderValue(ColorSliders::Channel::HslLightness) /
                            100.0);
    }
    else {
      m_filter.setHue(double(m_sliders.getRelSliderValue(ColorSliders::Channel::HsvHue)));
      m_filter.setSaturation(m_sliders.getRelSliderValue(ColorSliders::Channel::HsvSaturation) /
                             100.0);
      m_filter.setLightness(m_sliders.getRelSliderValue(ColorSliders::Channel::HsvValue) / 100.0);
    }
    m_filter.setAlpha(m_sliders.getRelSliderValue(ColorSliders::Channel::Alpha) / 100.0);

    restartPreview();
  }

  HueSaturationFilter& m_filter;
  ButtonSet m_colorType;
  ColorSliders m_sliders;
};

class HueSaturationCommand : public CommandWithNewParams<HueSaturationParams> {
public:
  HueSaturationCommand();

protected:
  bool onEnabled(Context* ctx) override;
  void onExecute(Context* ctx) override;
};

HueSaturationCommand::HueSaturationCommand()
  : CommandWithNewParams<HueSaturationParams>(CommandId::HueSaturation(), CmdRecordableFlag)
{
}

bool HueSaturationCommand::onEnabled(Context* ctx)
{
  return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable | ContextFlags::HasActiveSprite);
}

void HueSaturationCommand::onExecute(Context* ctx)
{
  const bool ui = (params().ui() && ctx->isUIAvailable());

  HueSaturationFilter filter;
  FilterManagerImpl filterMgr(ctx, &filter);
  if (params().mode.isSet())
    filter.setMode(params().mode());
  if (params().hue.isSet())
    filter.setHue(params().hue());
  if (params().saturation.isSet())
    filter.setSaturation(params().saturation() / 100.0);
  if (params().lightness.isSet())
    filter.setLightness(params().lightness() / 100.0);
  if (params().alpha.isSet())
    filter.setAlpha(params().alpha() / 100.0);

  filters::Target channels = TARGET_RED_CHANNEL | TARGET_GREEN_CHANNEL | TARGET_BLUE_CHANNEL |
                             TARGET_GRAY_CHANNEL | TARGET_ALPHA_CHANNEL;
  if (params().channels.isSet())
    channels = params().channels();
  filterMgr.setTarget(channels);

  if (ui) {
    HueSaturationWindow window(filter, filterMgr);
    window.doModal();
  }
  else {
    start_filter_worker(&filterMgr);
  }
}

Command* CommandFactory::createHueSaturationCommand()
{
  return new HueSaturationCommand;
}

} // namespace app
