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

#pragma once
#include <vector>
#include <iostream>
#include <stdint.h> //for uint64_t
#include <string>
#include <deque>
#include <set>



/// Holds all DDVTECH Stream Container classes and parsers.
///Video:
/// - codec (string: H264, H263, VP6)
/// - width (int, pixels)
/// - height (int, pixels)
/// - fpks (int, frames per kilosecond (FPS * 1000))
/// - bps (int, bytes per second)
/// - init (string, init data)
///
///Audio:
/// - codec (string: AAC, MP3)
/// - rate (int, Hz)
/// - size (int, bitsize)
/// - bps (int, bytes per second)
/// - channels (int, channelcount)
/// - init (string, init data)
///
///All packets:
/// - datatype (string: audio, video, meta (unused))
/// - data (string: data)
/// - time (int: ms into video)
///
///Video packets:
/// - keyframe (int, if set, is a seekable keyframe)
/// - interframe (int, if set, is a non-seekable interframe)
/// - disposableframe (int, if set, is a disposable interframe)
///
///H264 video packets:
/// - nalu (int, if set, is a nalu)
/// - nalu_end (int, if set, is a end-of-sequence)
/// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames)
namespace DTSC{

  /// Enumerates all possible DTMI types.
  enum DTMItype {
    DTMI_INT = 0x01, ///< Unsigned 64-bit integer.
    DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type.
    DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type.
    DTMI_OBJ_END = 0xEE, ///< End of object marker.
    DTMI_ROOT = 0xFF ///< Root node for all DTMI data.
  };
  
  /// Recursive class that holds DDVTECH MediaInfo.
  class DTMI {
  public:
    std::string Indice();
    DTMItype GetType();
    uint64_t & NumValue();
    std::string & StrValue();
    const char * Str();
    int hasContent();
    bool isEmpty();
    void addContent(DTMI c);
    DTMI* getContentP(int i);
    DTMI getContent(int i);
    DTMI* getContentP(std::string s);
    DTMI getContent(std::string s);
    DTMI();
    DTMI(std::string indice, uint64_t val, DTMItype setType = DTMI_INT);
    DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING);
    DTMI(std::string indice, DTMItype setType = DTMI_OBJECT);
    void Print(std::string indent = "");
    std::string Pack(bool netpack = false);
    bool netpacked;
    std::string packed;
  protected:
    std::string myIndice; ///< Holds this objects indice, if any.
    DTMItype myType; ///< Holds this objects AMF0 type.
    std::string strval; ///< Holds this objects string value, if any.
    uint64_t numval; ///< Holds this objects numeric value, if any.
    std::vector<DTMI> contents; ///< Holds this objects contents, if any (for container types).
  };//AMFType
  
  /// Parses a C-string to a valid DTSC::DTMI.
  DTMI parseDTMI(const unsigned char * data, unsigned int len);
  /// Parses a std::string to a valid DTSC::DTMI.
  DTMI parseDTMI(std::string data);
  /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions.
  DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name);
  
  /// This enum holds all possible datatypes for DTSC packets.
  enum datatype {
    AUDIO, ///< Stream Audio data
    VIDEO, ///< Stream Video data
    META, ///< Stream Metadata
    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

  /// A part from the DTSC::Stream ringbuffer.
  /// Holds information about a buffer that will stay consistent
  class Ring {
    public:
      Ring(unsigned int v);
      volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly!
      volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill.
      volatile bool starved; ///< If true, this Ring can no longer receive valid data.
  };

  /// Holds temporary data for a DTSC stream and provides functions to utilize it.
  /// Optionally also acts as a ring buffer of a certain requested size.
  /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe.
  class Stream {
    public:
      Stream();
      ~Stream();
      Stream(unsigned int buffers);
      DTSC::DTMI metadata;
      DTSC::DTMI & getPacket(unsigned int num = 0);
      datatype lastType();
      std::string & lastData();
      bool hasVideo();
      bool hasAudio();
      bool parsePacket(std::string & buffer);
      std::string & outPacket(unsigned int num);
      std::string & outHeader();
      Ring * getRing();
      unsigned int getTime();
      void dropRing(Ring * ptr);
  private:
      std::deque<DTSC::DTMI> buffers;
      std::set<DTSC::Ring *> rings;
      std::deque<DTSC::Ring> keyframes;
      void advanceRings();
      std::string * datapointer;
      datatype datapointertype;
      unsigned int buffercount;
  };
};