#pragma once
#include "dtsc.h"
#include "socket.h"
#include "theora.h"
#include "vorbis.h"
#include <cstdlib>
#include <deque>
#include <sstream>
#include <string>
#include <vector>

namespace OGG{

  class oggSegment{
  public:
    oggSegment();
    std::string dataString;
    int isKeyframe;
    long long unsigned int lastKeyFrameSeen;
    long long unsigned int framesSinceKeyFrame;
    unsigned int frameNumber;
    long long unsigned int timeStamp;
  };

  enum oggCodec{THEORA, VORBIS, OPUS};

  enum HeaderType{Plain = 0, Continued = 1, BeginOfStream = 2, EndOfStream = 4};

  std::deque<unsigned int> decodeXiphSize(char *data, size_t len);

  class Page{
  public:
    Page();
    Page(const Page &rhs);
    void operator=(const Page &rhs);
    bool read(std::string &newData);
    bool read(FILE *inFile);
    bool getSegment(unsigned int index, std::string &ret);
    const char *getSegment(unsigned int index);
    unsigned long getSegmentLen(unsigned int index);
    void setMagicNumber();
    char getVersion();
    void setVersion(char newVal = 0);
    char getHeaderType();
    void setHeaderType(char newVal);
    long long unsigned int getGranulePosition();
    void setGranulePosition(long long unsigned int newVal);
    uint32_t getBitstreamSerialNumber();
    void setBitstreamSerialNumber(uint32_t newVal);
    long unsigned int getCRCChecksum();
    void setCRCChecksum(long unsigned int newVal);
    long unsigned int getPageSequenceNumber();
    void setPageSequenceNumber(long unsigned int newVal);
    char getPageSegments();                   // get the amount of page segments
    inline void setPageSegments(char newVal); // set the amount of page segments
    int getPayloadSize();
    const std::deque<std::string> &getAllSegments();

    bool possiblyContinued();

    std::string toPrettyString(size_t indent = 0);

    long unsigned int calcChecksum();
    bool verifyChecksum();
    unsigned int calcPayloadSize();
    // void clear();
    void clear(char HeaderType, long long unsigned int GranPos, long unsigned int BSN, long unsigned int PSN);
    void prepareNext(bool continueMe = false);           // prepare the page to become the next page
    bool setPayload(char *newData, unsigned int length); // probably obsolete
    unsigned int addSegment(const std::string &content); // add a segment to the page, returns added bytes
    unsigned int addSegment(const char *content, unsigned int length); // add a segment to the page, returns added bytes
    void sendTo(Socket::Connection &destination, int calcGranule = -2); // combines all data and sends it to socket
    unsigned int setNextSegmentTableEntry(unsigned int entrySize); // returns the size that could not be added to the table
    unsigned int overFlow(); // returns the amount of bytes that don't fit in this page from the segments;

    long long unsigned int calculateGranule(oggSegment &currentSegment);
    bool shouldSend();
    void vorbisStuff(); // process most recent segment
    long long unsigned int totalFrames;
    int granules;
    OGG::oggCodec codec;
    std::deque<oggSegment> oggSegments; // used for ogg output
    unsigned int pageSequenceNumber;

    unsigned int framesSeen;
    unsigned int lastKeyFrame;
    unsigned int firstSample; // for vorbis, to handle "when to send the page"
    unsigned int sampleRate;  // for vorbis, to handle the sampleRate
    int prevBlockFlag;
    char blockSize[2];
    std::deque<vorbis::mode> vorbisModes; // modes for vorbis
    unsigned int split;                   // KFGShift for theora
  private:
    char data[282]; // Fulldata
    std::deque<std::string> segments;
  };

  class oggTrack{
  public:
    oggTrack() : KFGShift(0), lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){}
    oggCodec codec;
    std::string name;
    std::string contBuffer; // buffer for continuing pages
    long long unsigned int dtscID;
    char KFGShift;
    double lastTime;
    long long unsigned int lastGran;
    bool parsedHeaders;
    long long unsigned int lastPageOffset;
    unsigned int nxtSegment;
    double msPerFrame;
    Page myPage;
    // Codec specific elements
    // theora
    // theora::header idHeader;//needed to determine keyframe //bullshit?
    // vorbis
    std::deque<vorbis::mode> vModes;
    char channels;
    long long unsigned int blockSize[2];
    // unsigned long getBlockSize(unsigned int vModeIndex);
  };

  class headerPages{
  public:
    std::map<long long unsigned int, unsigned int> DTSCID2OGGSerial;
    std::map<long long unsigned int, unsigned int> DTSCID2seqNum;
    std::string parsedPages;
  };
}// namespace OGG