#pragma once

#include "geometry/screenbase.hpp"

namespace df
{

class Interpolator
{
public:
  Interpolator(double duration, double delay = 0);
  virtual ~Interpolator() = default;

  virtual void Advance(double elapsedSeconds);
  virtual void Finish();

  bool IsActive() const;
  void SetActive(bool active);

  bool IsFinished() const;

  static double constexpr kInvalidDuration = -1.0;
  void SetMaxDuration(double maxDuration);
  void SetMinDuration(double minDuration); 
  double GetMaxDuration() const;
  double GetMinDuration() const;

  double GetDuration() const;

protected:
  double GetT() const;
  double GetElapsedTime() const;

private:
  double m_elapsedTime;
  double m_duration;
  double m_maxDuration;
  double m_minDuration;

  double m_delay;
  bool m_isActive;
};

class PositionInterpolator: public Interpolator
{
  using TBase = Interpolator;

public:
  PositionInterpolator();
  PositionInterpolator(double duration, double delay,
                       m2::PointD const & startPosition, m2::PointD const & endPosition);

  PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition,
                       ScreenBase const & convertor);

  PositionInterpolator(double delay,
                       m2::PointD const & startPosition, m2::PointD const & endPosition,
                       ScreenBase const & convertor);

  PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition,
                       m2::RectD const & viewportRect, double scale);

  PositionInterpolator(double delay,
                       m2::PointD const & startPosition, m2::PointD const & endPosition,
                       m2::RectD const & viewportRect, double scale);

  static double GetMoveDuration(double globalDistance, m2::RectD const & viewportRect, double scale);

  static double GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
                                m2::RectD const & viewportRect, double scale);

  static double GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
                                ScreenBase const & convertor);

  // Interpolator overrides:
  void Advance(double elapsedSeconds) override;
  void Finish() override;

  m2::PointD GetPosition() const { return m_position; }
  m2::PointD GetTargetPosition() const { return m_endPosition; }

private:
  m2::PointD m_startPosition;
  m2::PointD m_endPosition;
  m2::PointD m_position;
};

class ScaleInterpolator: public Interpolator
{
  using TBase = Interpolator;

public:
  ScaleInterpolator();
  ScaleInterpolator(double startScale, double endScale, bool isAutoZoom);
  ScaleInterpolator(double delay, double startScale, double endScale, bool isAutoZoom);

  static double GetScaleDuration(double startScale, double endScale, bool isAutoZoom);

  // Interpolator overrides:
  void Advance(double elapsedSeconds) override;
  void Finish() override;

  double GetScale() const { return m_scale; }
  double GetStartScale() const { return m_startScale; }
  double GetTargetScale() const { return m_endScale; }

private:
  double m_startScale;
  double m_endScale;
  double m_scale;
};

class AngleInterpolator: public Interpolator
{
  using TBase = Interpolator;

public:
  AngleInterpolator();
  AngleInterpolator(double startAngle, double endAngle);
  AngleInterpolator(double delay, double startAngle, double endAngle);
  AngleInterpolator(double delay, double duration, double startAngle, double endAngle);

  static double GetRotateDuration(double startAngle, double endAngle);

  // Interpolator overrides:
  void Advance(double elapsedSeconds) override;
  void Finish() override;

  double GetAngle() const { return m_angle; }
  double GetStartAngle() const { return m_startAngle; }
  double GetTargetAngle() const { return m_endAngle; }

private:
  double m_startAngle;
  double m_endAngle;
  double m_angle;
};

} // namespace df
