#include "VST3Editor.h"

#include <pluginterfaces/gui/iplugview.h>

#include "VST3ParametersWindow.h"
#include "VST3Utils.h"
#include "VST3Wrapper.h"
#include "effects/StatelessPerTrackEffect.h"
#include "internal/PlugFrame.h"
#include "widgets/NumericTextCtrl.h"

#ifdef __WXGTK__
#include "internal/x11/SocketWindow.h"
#endif

VST3Editor::VST3Editor(wxWindow* parent, VST3Wrapper& wrapper,
   const EffectUIServices& effect, EffectType type,
   EffectSettingsAccess& access,
   bool useNativeUI)
   : EffectEditor(effect, access), mWrapper(wrapper), mParent(parent)
{
   if (type == EffectTypeGenerate)
   {
      auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
      auto controlsRoot = safenew wxWindow(parent, wxID_ANY);
      if(!useNativeUI || !TryLoadNativeUI(controlsRoot))
         mPlainUI = VST3ParametersWindow::Setup(*controlsRoot, *mWrapper.mEditController, *mWrapper.mComponentHandler);
      vSizer->Add(controlsRoot);

      auto &extra = access.Get().extra;
      mDuration = safenew NumericTextCtrl(
            FormatterContext::SampleRateContext(mWrapper.mSetup.sampleRate),
            parent, wxID_ANY,
            NumericConverterType_TIME(),
            extra.GetDurationFormat(),
            extra.GetDuration(),
            NumericTextCtrl::Options{}
               .AutoPos(true)
         );
      mDuration->SetName( XO("Duration") );

      auto hSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
      hSizer->Add(safenew wxStaticText(parent, wxID_ANY, _("Duration:")));
      hSizer->AddSpacer(5);
      hSizer->Add(mDuration);
      vSizer->AddSpacer(10);
      vSizer->Add(hSizer.release());

      parent->SetMinSize(vSizer->CalcMin());
      parent->SetSizer(vSizer.release());
   }
   else if(!useNativeUI || !TryLoadNativeUI(parent))
   {
      mPlainUI = VST3ParametersWindow::Setup(
         *parent,
         *mWrapper.mEditController,
         *mWrapper.mComponentHandler
      );
   }

   mWrapper.ParamChangedHandler =
      [this](Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue value) {
         Publish({ static_cast<size_t>(id), static_cast<float>(value) });
      };

   mWrapper.BeginParameterEdit(mAccess);

   Bind(wxEVT_IDLE, &VST3Editor::OnIdle, this);

   mParent->PushEventHandler(this);
}

VST3Editor::~VST3Editor()
{
   mWrapper.ParamChangedHandler = {};
}

void VST3Editor::OnIdle(wxIdleEvent& evt)
{
   evt.Skip();
   if(!mWrapper.IsActive())
   {
      mAccess.ModifySettings([this](EffectSettings& settings)
      {
         bool hasChanges{false};
         mWrapper.FlushParameters(settings, &hasChanges);
         if(hasChanges)
            mWrapper.StoreSettings(settings);
         return nullptr;
      });
   }
}


bool VST3Editor::TryLoadNativeUI(wxWindow* parent)
{
   using namespace Steinberg;

   if(const auto view = owned (mWrapper.mEditController->createView (Vst::ViewType::kEditor)))
   {
      //Workaround: override default min size set by EffectUIHost::Initialize()
      parent->SetMinSize(wxDefaultSize);
      //IPlugFrame::resizeView is supposed to call IPlugView::setSize
      //in the same call stack, assign before frame is attached
      mPlugView = view;

#if __WXGTK__
      safenew internal::x11::SocketWindow(parent, wxID_ANY, view);
#else

      static const auto platformType =
#  if __WXMAC__
         kPlatformTypeNSView;
#  elif __WXMSW__
         kPlatformTypeHWND;
#  else
#     error "Platform not supported"
#  endif
      auto plugFrame = owned(safenew internal::PlugFrame { parent });
      view->setFrame(plugFrame);
      if(view->attached(parent->GetHandle(), platformType) != kResultOk)
         return false;
      mPlugFrame = plugFrame;

      ViewRect initialSize;
      if(view->getSize(&initialSize) == kResultOk)
         plugFrame->init(view.get(), &initialSize);
#endif
      return true;
   }
   return false;
}

bool VST3Editor::IsGraphicalUI()
{
   return mPlugView != nullptr;
}

bool VST3Editor::ValidateUI()
{
   mAccess.ModifySettings([&](EffectSettings &settings){
      if (mDuration != nullptr)
         settings.extra.SetDuration(mDuration->GetValue());
      mWrapper.FlushParameters(settings);
      mWrapper.StoreSettings(settings);
      return nullptr;
   });

   return true;
}

void VST3Editor::OnClose()
{
   using namespace Steinberg;

   mParent->PopEventHandler();

   mPlainUI = nullptr;
   mParent = nullptr;
   if(mPlugView)
   {
      mPlugView->setFrame(nullptr);
      mPlugView->removed();
      mPlugView = nullptr;
      mPlugFrame = nullptr;
   }

   mWrapper.EndParameterEdit();

   mAccess.ModifySettings([&](EffectSettings &settings){
      if (mDuration != nullptr)
         settings.extra.SetDuration(mDuration->GetValue());
      //Flush changes if there is no processing performed at the moment
      mWrapper.FlushParameters(settings);
      mWrapper.StoreSettings(settings);
      return nullptr;
   });
   //Make sure that new state has been written to the caches...
   mAccess.Flush();

   mWrapper.ParamChangedHandler = {};

   EffectEditor::OnClose();
}

bool VST3Editor::UpdateUI()
{
   mAccess.ModifySettings([&](EffectSettings& settings) {
      mWrapper.FetchSettings(settings);
      return nullptr;
   });

   if (mPlainUI != nullptr)
      mPlainUI->ReloadParameters();

   //Write to main...
   mAccess.Flush();

   return true;
}
