/*  SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************

  Audacity: A Digital Audio Editor

  ProjectSnap.cpp

  Dmitry Vedenko

**********************************************************************/
#include "ProjectSnap.h"
#include "Project.h"
#include "XMLWriter.h"
#include "XMLAttributeValueView.h"

#include "ProjectRate.h"
#include "ProjectTimeSignature.h"

static const AudacityProject::AttachedObjects::RegisteredFactory sKey {
   [](AudacityProject& project)
   {
      auto result = std::make_shared<ProjectSnap>(project);
      return result;
   }
};

ProjectSnap& ProjectSnap::Get(AudacityProject& project)
{
   return project.AttachedObjects::Get<ProjectSnap>(sKey);
}

const ProjectSnap& ProjectSnap::Get(const AudacityProject& project)
{
   return Get(const_cast<AudacityProject&>(project));
}

ProjectSnap::ProjectSnap(const AudacityProject& project)
    : mProject(project)
{}

void ProjectSnap::SetSnapMode(SnapMode mode)
{
   if (mSnapMode != mode)
   {
      mSnapMode = mode;
      
      SnapModeSetting.WriteEnum(mSnapMode);
      gPrefs->Flush();

      Publish(SnapChangedMessage { mode, mSnapTo });
   }
}

SnapMode ProjectSnap::GetSnapMode() const
{
   return mSnapMode;
}

void ProjectSnap::SetSnapTo(Identifier snap)
{
   if (mSnapTo != snap)
   {
      mSnapTo = snap;

      SnapToSetting.Write(mSnapTo.GET());
      gPrefs->Flush();

      Publish(SnapChangedMessage { mSnapMode, snap });
   }
}

Identifier ProjectSnap::GetSnapTo() const
{
   return mSnapTo;
}

SnapResult ProjectSnap::SnapTime(double time) const
{
   if (mSnapMode == SnapMode::SNAP_OFF)
      return { time, false };

   return SnapFunctionsRegistry::Snap(mSnapTo, mProject, time, mSnapMode == SnapMode::SNAP_NEAREST);
}

SnapResult ProjectSnap::SingleStep(double time, bool upwards) const
{
   if (mSnapMode == SnapMode::SNAP_OFF)
      return { time, false };

   return SnapFunctionsRegistry::SingleStep(mSnapTo, mProject, time, upwards);
}

static ProjectFileIORegistry::AttributeWriterEntry entry {
   [](const AudacityProject& project, XMLWriter& xmlFile)
   {
      // Keep this attr for compatibility
      auto& snapSettings = ProjectSnap::Get(project);
      xmlFile.WriteAttr(
         wxT("snapto"), snapSettings.GetSnapMode() != SnapMode::SNAP_OFF ?
                           wxT("on") :
                           wxT("off"));
   }
};

static ProjectFileIORegistry::AttributeReaderEntries entries {
   // Just a pointer to function, but needing overload resolution as non-const:
   (ProjectSnap & (*)(AudacityProject&)) & ProjectSnap::Get,
   {
      // PRL:  The following has persisted as a per-project setting for long.
      // Maybe that should be abandoned.  Enough to save changes in the user
      // preference file.
      // DV: Keep the old behavior here: project only stored *off* and *nearest*
      { "snapto",
        [](auto& snapSettings, auto value)
        {
           snapSettings.SetSnapMode(
              value.ToWString() == wxT("on") ? SnapMode::SNAP_NEAREST :
                                               SnapMode::SNAP_OFF);
        } },
   }
};
