#pragma once
#include "input.h"
#include <mist/dtsc.h>
#include <mist/nal.h>
#include <mist/segmentreader.h>

#define BUFFERTIME 10

namespace Mist{

  extern bool streamIsLive; //< Playlist can be sliding window or get new segments appended
  extern bool streamIsVOD; //< Playlist segments do not disappear
  extern uint32_t globalWaitTime; // largest waitTime for any playlist we're loading - used to update minKeepAway
  void parseKey(std::string key, char *newKey, unsigned int len);

  struct playListEntries{
    std::string filename;
    std::string relative_filename;
    std::string mapName;
    uint64_t startAtByte; ///< Byte position inside filename where to start reading
    uint64_t stopAtByte; ///< Byte position inside filename where to stop sending
    uint64_t bytePos;
    uint64_t mUTC; ///< UTC unix millis timestamp of first packet, if known
    float duration; ///< Duration of entry in seconds
    uint64_t timestamp; ///< zUTC-based timestamp for this entry
    int64_t timeOffset; ///< Value timestamps in the media are shifted by to get zUTC-based timestamps
    uint64_t wait;
    char ivec[16];
    char keyAES[16];
    playListEntries(){
      bytePos = 0;
      mUTC = 0;
      duration = 0;
      timestamp = 0;
      timeOffset = 0;
      wait = 0;
      startAtByte = 0;
      stopAtByte = 0;
      for (size_t i = 0; i < 16; ++i){
        ivec[i] = 0;
        keyAES[i] = 0;
      }
    }
    std::string shortName() const{
      if (!startAtByte && !stopAtByte){return filename;}
      std::string ret = filename;
      ret += " (";
      ret += JSON::Value(startAtByte).asString();
      ret += "-";
      ret += JSON::Value(stopAtByte).asString();
      ret += ")";
      return ret;
    }
  };

  inline bool operator< (const playListEntries a, const playListEntries b){
    return a.filename < b.filename || (a.filename == b.filename && a.startAtByte < b.startAtByte);
  }

  inline bool operator== (const playListEntries a, const playListEntries b){
    return a.filename == b.filename && a.startAtByte == b.startAtByte;
  }

  /// Keeps the segment entry list by playlist ID
  extern std::map<uint32_t, std::deque<playListEntries> > listEntries;

  class Playlist{
  public:
    Playlist(const std::string &uriSrc = "");
    bool isUrl() const;
    bool reload();
    void addEntry(const std::string & absolute_filename, const std::string &filename, float duration, uint64_t &bpos,
                  const std::string &key, const std::string &keyIV, const std::string mapName, uint64_t startByte, uint64_t lenByte);

    std::string uri; // link to the current playlistfile
    HTTP::URL root;
    std::string relurl;

    uint64_t reloadNext;

    uint32_t id;
    bool playlistEnd;
    int noChangeCount;

    uint64_t waitTime;
    uint64_t lastTimestamp;
    uint64_t startTime;
    int64_t oUTC;
    uint64_t nextUTC; ///< If non-zero, the UTC timestamp of the next segment on this playlist
    char keyAES[16];
    std::map<std::string, std::string> keys;
    std::map<std::string, std::string> maps;
    uint64_t firstIndex; //< the index of the first segment in the playlist
    uint64_t lastSegment;
    std::map<size_t, bool> tracks;
  };

  void playlistRunner(void *ptr);

  class InputHLS : public Input{
  public:
    InputHLS(Util::Config *cfg);
    ~InputHLS(){}
    bool needsLock(){return !config->getBool("realtime");}
    bool openStreamSource();
    bool callback();

  protected:
    uint64_t zUTC; ///< Zero point in local millis, as UTC unix time millis
    uint64_t nUTC; ///< Next packet timestamp in UTC unix time millis
    int64_t streamOffset; ///< bootMsOffset we need to set once we have parsed the header
    unsigned int startTime;
    SegmentReader segDowner;
    int version;
    int targetDuration;
    bool endPlaylist;
    uint64_t currentPlaylist;

    bool allowRemap;     ///< True if the next packet may remap the timestamps
    std::map<uint64_t, uint64_t> pidMapping;
    std::map<uint64_t, uint64_t> pidMappingR;
    std::map<int, int64_t> plsTimeOffset;
    std::map<int, int64_t> DVRTimeOffsets;
    std::map<int, uint64_t> plsLastTime;
    std::map<int, uint64_t> plsInterval;

    size_t currentIndex;
    std::string currentFile;

    // Used to map packetId of packets in pidMapping
    int pidCounter;

    // Override userLeadOut to buffer new data as live packets
    void userLeadOut();
    // Removes any metadata which is no longer and the playlist or buffered in memory
    void updateMeta();
    /// Tries to add as much live packets from a TS file at the given location
    bool parseSegmentAsLive(uint64_t segmentIndex);
    // index of last playlist entry finished parsing
    long previousSegmentIndex;

    size_t firstSegment();
    void waitForNextSegment();
    void readPMT();
    bool checkArguments();
    bool preSetup();
    bool readHeader();
    bool readExistingHeader();
    void getNext(size_t idx = INVALID_TRACK_ID);
    void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);

    bool readIndex();
    bool initPlaylist(const std::string &uri, bool fullInit = true);
    bool readPlaylist(const HTTP::URL &uri, const std::string & relurl, bool fullInit = true);
    bool readNextFile();

    void parseStreamHeader();
    void parseLivePoint();
    void streamMainLoop();

    uint32_t getMappedTrackId(uint64_t id);
    uint32_t getMappedTrackPlaylist(uint64_t id);
    uint64_t getOriginalTrackId(uint32_t playlistId, uint32_t id);
    uint64_t getPacketTime(uint64_t packetTime, uint64_t tid, uint64_t currentPlaylist, uint64_t nUTC = 0);
    uint64_t getPacketID(uint64_t currentPlaylist, uint64_t trackId);
    virtual void finish();
    void injectLocalVars();
    virtual void checkHeaderTimes(const HTTP::URL & streamFile);
    // Used to immediately mark pages for removal when we're bursting through segments on initial boot
    bool isInitialRun;
  };
}// namespace Mist

typedef Mist::InputHLS mistIn;