/// \file dtsc.h
/// Holds all headers for DDVTECH Stream Container parsing/generation.

#pragma once
#include "defines.h"
#include "json.h"
#include "shared_memory.h"
#include "socket.h"
#include "timing.h"
#include "util.h"
#include <deque>
#include <iostream>
#include <set>
#include <stdint.h> //for uint64_t
#include <stdio.h>  //for FILE
#include <string>
#include <vector>

#define DTSC_INT 0x01
#define DTSC_STR 0x02
#define DTSC_OBJ 0xE0
#define DTSC_ARR 0x0A
#define DTSC_CON 0xFF

// Increase this value every time the DTSH file format changes in an incompatible way
// Changelog:
//  Version 0-2: Undocumented changes
//  Version 3: switched to bigMeta-style by default, Parts layout switched from 3/2/4 to 3/3/3 bytes
//  Version 4: renamed bps to maxbps (peak bit rate) and added new value bps (average bit rate)
#define DTSH_VERSION 4

namespace DTSC{

  extern uint64_t veryUglyJitterOverride;

  ///\brief This enum holds all possible datatypes for DTSC packets.
  enum datatype{
    AUDIO,          ///< Stream Audio data
    VIDEO,          ///< Stream Video data
    META,           ///< Stream Metadata
    PAUSEMARK,      ///< Pause marker
    MODIFIEDHEADER, ///< Modified header data.
    INVALID         ///< Anything else or no data available.
  };

  extern char Magic_Header[];  ///< The magic bytes for a DTSC header
  extern char Magic_Packet[];  ///< The magic bytes for a DTSC packet
  extern char Magic_Packet2[]; ///< The magic bytes for a DTSC packet version 2
  extern char Magic_Command[]; ///< The magic bytes for a DTCM packet

  enum packType{DTSC_INVALID, DTSC_HEAD, DTSC_V1, DTSC_V2, DTCM};

  /// This class allows scanning through raw binary format DTSC data.
  /// It can be used as an iterator or as a direct accessor.
  class Scan{
  public:
    Scan();
    Scan(char *pointer, size_t len);
    operator bool() const;
    std::string toPrettyString(size_t indent = 0) const;
    bool hasMember(const std::string &indice) const;
    bool hasMember(const char *indice, size_t ind_len) const;
    Scan getMember(const std::string &indice) const;
    Scan getMember(const char *indice) const;
    Scan getMember(const char *indice, size_t ind_len) const;
    Scan getIndice(size_t num) const;
    std::string getIndiceName(size_t num) const;
    size_t getSize() const;

    char getType() const;
    bool asBool() const;
    int64_t asInt() const;
    std::string asString() const;
    void getString(char *&result, size_t &len) const;
    JSON::Value asJSON() const;

  private:
    char *p;
    size_t len;
  };

  /// DTSC::Packets can currently be three types:
  /// DTSC_HEAD packets are the "DTSC" header string, followed by 4 bytes len and packed content.
  /// DTSC_V1 packets are "DTPD", followed by 4 bytes len and packed content.
  /// DTSC_V2 packets are "DTP2", followed by 4 bytes len, 4 bytes trackID, 8 bytes time, and packed
  /// content. The len is always without the first 8 bytes counted.
  class Packet{
  public:
    Packet();
    Packet(const Packet &rhs, size_t idx = INVALID_TRACK_ID);
    Packet(const char *data_, unsigned int len, bool noCopy = false);
    virtual ~Packet();
    void null();
    void operator=(const Packet &rhs);
    operator bool() const;
    packType getVersion() const;
    void reInit(Socket::Connection &src);
    void reInit(const char *data_, unsigned int len, bool noCopy = false);
    void genericFill(uint64_t packTime, int64_t packOffset, uint32_t packTrack, const char *packData,
                     size_t packDataSize, uint64_t packBytePos, bool isKeyframe);
    void appendData(const char *appendData, uint32_t appendLen);
    void getString(const char *identifier, char *&result, size_t &len) const;
    void getString(const char *identifier, std::string &result) const;
    void getInt(const char *identifier, uint64_t &result) const;
    uint64_t getInt(const char *identifier) const;
    void getFlag(const char *identifier, bool &result) const;
    bool getFlag(const char *identifier) const;
    bool hasMember(const char *identifier) const;
    void appendNal(const char *appendData, uint32_t appendLen);
    void upgradeNal(const char *appendData, uint32_t appendLen);
    void setKeyFrame(bool kf);
    virtual uint64_t getTime() const;
    void setTime(uint64_t _time);
    size_t getTrackId() const;
    char *getData() const;
    size_t getDataLen() const;
    size_t getPayloadLen() const;
    size_t getDataStringLen();
    size_t getDataStringLenOffset();
    JSON::Value toJSON() const;
    std::string toSummary() const;
    Scan getScan() const;

  protected:
    bool master;
    packType version;
    void resize(size_t size);
    char *data;
    size_t bufferLen;
    size_t dataLen;

    uint64_t prevNalSize;
  };

  /// A child class of DTSC::Packet, which allows overriding the packet time efficiently.
  class RetimedPacket : public Packet{
  public:
    RetimedPacket(uint64_t reTime){timeOverride = reTime;}
    RetimedPacket(uint64_t reTime, const Packet &rhs) : Packet(rhs){timeOverride = reTime;}
    RetimedPacket(uint64_t reTime, const char *data_, unsigned int len, bool noCopy = false)
        : Packet(data_, len, noCopy){
      timeOverride = reTime;
    }
    ~RetimedPacket(){}
    virtual uint64_t getTime() const{return timeOverride;}

  protected:
    uint64_t timeOverride;
  };

  class Parts{
  public:
    Parts(const Util::RelAccX &_parts);
    size_t getFirstValid() const;
    size_t getEndValid() const;
    size_t getValidCount() const;
    size_t getSize(size_t idx) const;
    uint64_t getDuration(size_t idx) const;
    int64_t getOffset(size_t idx) const;

  private:
    const Util::RelAccX &parts;
    Util::RelAccXFieldData sizeField;
    Util::RelAccXFieldData durationField;
    Util::RelAccXFieldData offsetField;
  };

  class Keys{
  public:
    Keys(Util::RelAccX &_keys);
    Keys(const Util::RelAccX &_keys);
    size_t getFirstValid() const;
    size_t getEndValid() const;
    size_t getValidCount() const;
    size_t getFirstPart(size_t idx) const;
    size_t getBpos(size_t idx) const;
    uint64_t getDuration(size_t idx) const;
    size_t getNumber(size_t idx) const;
    size_t getParts(size_t idx) const;
    uint64_t getTime(size_t idx) const;
    void setSize(size_t idx, size_t _size);
    size_t getSize(size_t idx) const;
    size_t getNumForTime(uint64_t time) const;

  private:
    bool isConst;
    Util::RelAccX empty;

    Util::RelAccX &keys;
    const Util::RelAccX &cKeys;

    Util::RelAccXFieldData firstPartField;
    Util::RelAccXFieldData bposField;
    Util::RelAccXFieldData durationField;
    Util::RelAccXFieldData numberField;
    Util::RelAccXFieldData partsField;
    Util::RelAccXFieldData timeField;
    Util::RelAccXFieldData sizeField;
  };

  class Fragments{
  public:
    Fragments(const Util::RelAccX &_fragments);
    size_t getFirstValid() const;
    size_t getEndValid() const;
    size_t getValidCount() const;
    uint64_t getDuration(size_t idx) const;
    size_t getKeycount(size_t idx) const;
    size_t getFirstKey(size_t idx) const;
    size_t getSize(size_t idx) const;

  private:
    const Util::RelAccX &fragments;
  };

  class Track{
  public:
    Util::RelAccX parts;
    Util::RelAccX keys;
    Util::RelAccX fragments;

    Util::RelAccX pages;

    Util::RelAccX track;

    // Internal buffers so we don't always need to search for everything
    Util::RelAccXFieldData trackIdField;
    Util::RelAccXFieldData trackTypeField;
    Util::RelAccXFieldData trackCodecField;
    Util::RelAccXFieldData trackFirstmsField;
    Util::RelAccXFieldData trackLastmsField;
    Util::RelAccXFieldData trackBpsField;
    Util::RelAccXFieldData trackMaxbpsField;
    Util::RelAccXFieldData trackLangField;
    Util::RelAccXFieldData trackInitField;
    Util::RelAccXFieldData trackRateField;
    Util::RelAccXFieldData trackSizeField;
    Util::RelAccXFieldData trackChannelsField;
    Util::RelAccXFieldData trackWidthField;
    Util::RelAccXFieldData trackHeightField;
    Util::RelAccXFieldData trackFpksField;
    Util::RelAccXFieldData trackMissedFragsField;

    Util::RelAccXFieldData partSizeField;
    Util::RelAccXFieldData partDurationField;
    Util::RelAccXFieldData partOffsetField;

    Util::RelAccXFieldData keyFirstPartField;
    Util::RelAccXFieldData keyBposField;
    Util::RelAccXFieldData keyDurationField;
    Util::RelAccXFieldData keyNumberField;
    Util::RelAccXFieldData keyPartsField;
    Util::RelAccXFieldData keyTimeField;
    Util::RelAccXFieldData keySizeField;

    Util::RelAccXFieldData fragmentDurationField;
    Util::RelAccXFieldData fragmentKeysField;
    Util::RelAccXFieldData fragmentFirstKeyField;
    Util::RelAccXFieldData fragmentSizeField;
  };

  class Meta{
  public:
    Meta(const std::string &_streamName, const DTSC::Scan &src);
    Meta(const std::string &_streamName = "", bool master = true);
    Meta(const std::string &_streamName, const std::string &fileName);

    ~Meta();
    void reInit(const std::string &_streamName, bool master = true);
    void reInit(const std::string &_streamName, const std::string &fileName);
    void reInit(const std::string &_streamName, const DTSC::Scan &src);

    void refresh();

    operator bool() const;

    void setMaster(bool _master);
    bool getMaster() const;

    void clear();

    void minimalFrom(const Meta &src);

    bool trackLoaded(size_t idx) const;
    bool trackValid(size_t idx) const;
    size_t trackCount() const;

    size_t addCopy(size_t source);
    size_t addDelayedTrack(size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
                           size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);
    size_t addTrack(size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
                    size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT,
                    bool setValid = true);
    void resizeTrack(size_t source, size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
                     size_t partCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);
    void initializeTrack(Track &t, size_t fragCount = DEFAULT_FRAGMENT_COUNT, size_t keyCount = DEFAULT_KEY_COUNT,
                         size_t parCount = DEFAULT_PART_COUNT, size_t pageCount = DEFAULT_PAGE_COUNT);

    void merge(const DTSC::Meta &M, bool deleteTracks = true, bool copyData = true);

    void updatePosOverride(DTSC::Packet &pack, uint64_t bpos);
    void update(const DTSC::Packet &pack);
    void update(uint64_t packTime, int64_t packOffset, uint32_t packTrack, uint64_t packDataSize,
                uint64_t packBytePos, bool isKeyframe, uint64_t packSendSize = 0);

    size_t trackIDToIndex(size_t trackID, size_t pid = 0) const;

    std::string getTrackIdentifier(size_t idx, bool unique = false) const;

    void setInit(size_t trackIdx, const std::string &init);
    void setInit(size_t trackIdx, const char *init, size_t initLen);
    std::string getInit(size_t idx) const;

    void setSource(const std::string &src);
    std::string getSource() const;

    void setID(size_t trackIdx, size_t id);
    size_t getID(size_t trackIdx) const;

    void markUpdated(size_t trackIdx);
    uint64_t getLastUpdated(size_t trackIdx) const;
    uint64_t getLastUpdated() const;

    void setChannels(size_t trackIdx, uint16_t channels);
    uint16_t getChannels(size_t trackIdx) const;

    void setRate(size_t trackIdx, uint32_t rate);
    uint32_t getRate(size_t trackIdx) const;

    void setWidth(size_t trackIdx, uint32_t width);
    uint32_t getWidth(size_t trackIdx) const;

    void setHeight(size_t trackIdx, uint32_t height);
    uint32_t getHeight(size_t trackIdx) const;

    void setSize(size_t trackIdx, uint16_t size);
    uint16_t getSize(size_t trackIdx) const;

    void setType(size_t trackIdx, const std::string &type);
    std::string getType(size_t trackIdx) const;

    void setCodec(size_t trackIdx, const std::string &codec);
    std::string getCodec(size_t trackIdx) const;

    void setLang(size_t trackIdx, const std::string &lang);
    std::string getLang(size_t trackIdx) const;

    void setFirstms(size_t trackIdx, uint64_t firstms);
    uint64_t getFirstms(size_t trackIdx) const;

    void setLastms(size_t trackIdx, uint64_t lastms);
    uint64_t getLastms(size_t trackIdx) const;

    uint64_t getDuration(size_t trackIdx) const;

    void setBps(size_t trackIdx, uint64_t bps);
    uint64_t getBps(size_t trackIdx) const;

    void setMaxBps(size_t trackIdx, uint64_t bps);
    uint64_t getMaxBps(size_t trackIdx) const;

    void setFpks(size_t trackIdx, uint64_t bps);
    uint64_t getFpks(size_t trackIdx) const;

    void setMissedFragments(size_t trackIdx, uint32_t missedFragments);
    uint32_t getMissedFragments(size_t trackIdx) const;

    void setMinKeepAway(size_t trackIdx, uint64_t minKeepAway);
    uint64_t getMinKeepAway(size_t trackIdx) const;

    /*LTS-START*/
    void setSourceTrack(size_t trackIdx, size_t sourceTrack);
    uint64_t getSourceTrack(size_t trackIdx) const;

    void setEncryption(size_t trackIdx, const std::string &encryption);
    std::string getEncryption(size_t trackIdx) const;

    void setPlayReady(size_t trackIdx, const std::string &playReady);
    std::string getPlayReady(size_t trackIdx) const;

    void setWidevine(size_t trackIdx, const std::string &widevine);
    std::string getWidevine(size_t trackIdx) const;

    void setIvec(size_t trackIdx, uint64_t ivec);
    uint64_t getIvec(size_t trackIdx) const;

    void setMinimumFragmentDuration(uint64_t newFragmentDuration = DEFAULT_FRAGMENT_DURATION);
    uint64_t getMinimumFragmentDuration() const;
    /*LTS-END*/
    /*LTS-START
    uint64_t getFragmentDuration() const{return DEFAULT_FRAGMENT_DURATION;}
    LTS-END*/

    void setVod(bool vod = true);
    bool getVod() const;

    void setLive(bool live = true);
    bool getLive() const;

    bool hasBFrames(size_t idx = INVALID_TRACK_ID) const;

    void setBufferWindow(uint64_t bufferWindow);
    uint64_t getBufferWindow() const;

    void setBootMsOffset(uint64_t bootMsOffset);
    uint64_t getBootMsOffset() const;

    std::set<size_t> getValidTracks(bool skipEmpty = false) const;
    std::set<size_t> getMySourceTracks(size_t pid) const;

    void validateTrack(size_t trackIdx);
    void removeEmptyTracks();
    void removeTrack(size_t trackIdx);
    void removeFirstKey(size_t trackIdx);

    size_t mainTrack() const;
    uint32_t biggestFragment(uint32_t idx = INVALID_TRACK_ID) const;
    bool tracksAlign(size_t idx1, size_t idx2) const;

    uint64_t getTimeForFragmentIndex(uint32_t idx, uint32_t fragmentIdx) const;
    uint32_t getFragmentIndexForTime(uint32_t idx, uint64_t timestamp) const;

    uint64_t getTimeForKeyIndex(uint32_t idx, uint32_t keyIdx) const;
    uint32_t getKeyIndexForTime(uint32_t idx, uint64_t timestamp) const;

    uint32_t getPartIndex(const DTSC::Packet &pack, size_t idx) const;

    bool nextPageAvailable(uint32_t idx, size_t currentPage) const;
    size_t getPageNumberForTime(uint32_t idx, uint64_t time) const;
    size_t getPageNumberForKey(uint32_t idx, uint64_t keynumber) const;

    const Util::RelAccX &parts(size_t idx) const;
    Util::RelAccX &keys(size_t idx);
    const Util::RelAccX &keys(size_t idx) const;
    const Util::RelAccX &fragments(size_t idx) const;
    Util::RelAccX &pages(size_t idx);
    const Util::RelAccX &pages(size_t idx) const;

    std::string toPrettyString() const;

    void remap(const std::string &_streamName = "");

    uint64_t getSendLen(bool skipDynamic = false, std::set<size_t> selectedTracks = std::set<size_t>()) const;
    void toFile(const std::string &fName) const;
    void send(Socket::Connection &conn, bool skypDynamic = false,
              std::set<size_t> selectedTracks = std::set<size_t>(), bool reID = false) const;
    void toJSON(JSON::Value &res, bool skipDynamic = true, bool tracksOnly = false) const;

    std::string getStreamName() const{return streamName;}

    JSON::Value inputLocalVars;

    uint8_t version;

  protected:
    void sBufMem(size_t trackCount = DEFAULT_TRACK_COUNT);
    void sBufShm(const std::string &_streamName, size_t trackCount = DEFAULT_TRACK_COUNT, bool master = true);
    void streamInit(size_t trackCount = DEFAULT_TRACK_COUNT);

    std::string streamName;

    IPC::sharedPage streamPage;
    Util::RelAccX stream;
    Util::RelAccX trackList;
    std::map<size_t, Track> tracks;
    std::map<size_t, IPC::sharedPage> tM;

    bool isMaster;

    char *streamMemBuf;
    bool isMemBuf;
    std::map<size_t, char *> tMemBuf;
    std::map<size_t, size_t> sizeMemBuf;

  private:
    // Internal buffers so we don't always need to search for everything
    Util::RelAccXFieldData streamVodField;
    Util::RelAccXFieldData streamLiveField;
    Util::RelAccXFieldData streamSourceField;
    Util::RelAccXFieldData streamBufferWindowField;
    Util::RelAccXFieldData streamBootMsOffsetField;
    Util::RelAccXFieldData streamMinimumFragmentDurationField;

    Util::RelAccXFieldData trackValidField;
    Util::RelAccXFieldData trackIdField;
    Util::RelAccXFieldData trackTypeField;
    Util::RelAccXFieldData trackCodecField;
    Util::RelAccXFieldData trackPageField;
    Util::RelAccXFieldData trackLastUpdateField;
    Util::RelAccXFieldData trackPidField;
    Util::RelAccXFieldData trackMinKeepAwayField;
    Util::RelAccXFieldData trackSourceTidField;
    Util::RelAccXFieldData trackEncryptionField;
    Util::RelAccXFieldData trackIvecField;
    Util::RelAccXFieldData trackWidevineField;
    Util::RelAccXFieldData trackPlayreadyField;
  };
}// namespace DTSC