#pragma once
#include "analytics.h"
#include "limiter.h"
#include "marshalvector.h"
#include "timing/piecewise_linear.h"
#include "track.h"
#include <catch2/catch.hpp>
#include <emscripten/val.h>
#include <memory>
#include <vector>

struct TimelineSharedState {
  double position;
  uint32_t playing;
  uint32_t delayCompensationIsPrerolled;

  // Loop state
  double loopStart;
  double loopEnd;
  uint32_t loopEnabled;

  double fadeInStartBeats;
  double fadeInLengthBeats;
  double fadeInExponent;
  double fadeOutEndBeats;
  double fadeOutLengthBeats;
  double fadeOutExponent;

  float masterGain;

  TimelineSharedState()
      : position(0.0), playing(0), delayCompensationIsPrerolled(0),
        loopStart(0.0), loopEnd(0.0), loopEnabled(0), fadeInStartBeats(0.0),
        fadeInLengthBeats(0.0), fadeInExponent(0.0), fadeOutEndBeats(INFINITY),
        fadeOutLengthBeats(0.0), fadeOutExponent(0.0), masterGain(1.0) {}
};

class Timeline {
private:
  const int sampleRate;
  const std::vector<std::shared_ptr<Track>> tracks;
  const std::shared_ptr<time_transform::TimelineTempoMap> timing;
  const std::shared_ptr<AnalyticsObserver> analyticsObserver;
  std::shared_ptr<Limiter> limiter;
  int delayCompensationFrames;
  const std::shared_ptr<Meter> masterMeter;
  const std::shared_ptr<TimelineSharedState> sharedState;

  BufferF32 tempBuffer;

  void applyFadesAndGain(double position, double bps, BufferF32 output);

public:
  Timeline(int sampleRate,
           const std::shared_ptr<time_transform::TimelineTempoMap> &timing,
           const std::vector<std::shared_ptr<Track>> &tracks,
           const std::shared_ptr<AnalyticsObserver> &analyticsObserver,
           const std::shared_ptr<Limiter> &limiter,
           const std::shared_ptr<Meter> &masterMeter,
           const std::shared_ptr<TimelineSharedState> &sharedState);

  double getPosition() const;
  void setPosition(double position);
  bool isPlaying() const;
  void setPlaying(bool playing);

  // Loop methods
  double getLoopStart() const;
  void setLoopStart(double start);
  double getLoopEnd() const;
  void setLoopEnd(double end);
  bool isLoopEnabled() const;
  void setLoopEnabled(bool enabled);

  // Fade methods
  double getFadeInStartBeats() const;
  void setFadeInStartBeats(double start);
  double getFadeInLengthBeats() const;
  void setFadeInLengthBeats(double length);
  double getFadeInExponent() const;
  void setFadeInExponent(double exponent);
  double getFadeOutEndBeats() const;
  void setFadeOutEndBeats(double end);
  double getFadeOutLengthBeats() const;
  void setFadeOutLengthBeats(double length);
  double getFadeOutExponent() const;
  void setFadeOutExponent(double exponent);

  // Master gain methods
  float getMasterGain() const;
  void setMasterGain(float gain);

  int getTrackCount() const { return tracks.size(); }
  std::shared_ptr<Track> getTrack(int index) const;
  void read(BufferF32 output);
};