#ifndef MSTREAM_HPP_
#define MSTREAM_HPP_

#include <stddef.h>
#include <string>

namespace tls
{
  //------------------------------------------------------------------------------
  //To avoid writing the cursor position in the array at each step
  class MStream
  {
  private:
  	char* m_data;
  	unsigned short m_pos;
  	bool m_owner;
  public:
  	MStream(){ m_data=NULL; m_pos=0; m_owner=false;};
  	MStream(char* data, unsigned short pos, bool owner){ m_data=data; m_pos=pos; m_owner=owner; };
  	MStream(unsigned char* data, unsigned short pos, bool owner){ m_data=(char*)data; m_pos=pos; m_owner=owner; };
  	~MStream(){ if(m_owner) delete m_data; };

  	unsigned short getPos(){ return m_pos; };
  	void setPos(unsigned short pos){ m_pos = pos; };
  	char* getData(){ return m_data; };
  	void setData(char* data){ m_data = data; };
  	void setOwner(bool val){m_owner=val;};
  	bool getOwner(bool val){ return m_owner;};

  	bool write(char val){ *((char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool write(signed char val){ *((signed char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool write(unsigned char val){ *((unsigned char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool write(signed short int val){ *((signed short int*)&m_data[m_pos]) = val; m_pos+=2; return true; };
  	bool write(unsigned short val){ *((unsigned short*)&m_data[m_pos]) = val; m_pos+=2; return true; };
  	bool write(signed int val){ *((signed int*)&m_data[m_pos]) = val; m_pos+=4; return true; };
  	bool write(unsigned int val){ *((unsigned int*)&m_data[m_pos]) = val; m_pos+=4; return true; };


  	//Не С++ стиль а "big endian"
  	bool writeR(char val){ *((char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool writeR(signed char val){ *((signed char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool writeR(unsigned char val){ *((unsigned char*)&m_data[m_pos]) = val; m_pos+=1; return true; };
  	bool writeR(signed short int val){
  		m_data[m_pos] = ((char*)&val)[0];
  		m_data[m_pos+1] = ((char*)&val)[1];
  		m_pos+=2;
  		return true;
  	};
  	bool writeR(unsigned short val){
  		m_data[m_pos] = ((char*)&val)[1];
  		m_data[m_pos+1] = ((char*)&val)[0];
  		m_pos+=2;
  		return true;
  	};
  	bool writeR(signed int val)
  	{
  		m_data[m_pos] = ((char*)&val)[3];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[2];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[1];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[0];
  		m_pos++;
  		return true;
  	};
  	bool writeR(unsigned int val)
  	{
  		m_data[m_pos] = ((char*)&val)[3];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[2];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[1];
  		m_pos++;
  		m_data[m_pos] = ((char*)&val)[0];
  		m_pos++;
  		return true;
  	};

  	bool write(char* val, int len){ for(int i=0;i<len;i++) { m_data[m_pos+i]=val[i];} m_pos+=len; return true; };
  	bool write(const char* val, int len){ return write((char*)val,len); };
  	bool write(std::string str){ return write((char*)str.c_str(),str.length()); };
  	bool writeStrZZ(char* val){ int pos=0; while(true){ m_data[m_pos+pos]=val[pos]; if(val[pos]==0) break; pos++; } m_pos+=pos; return true; }; //Переписать строку с ноликом на конце
  	bool writeStrZ(char* val){ int pos=0; while(true){ if(val[pos]==0) break; m_data[m_pos+pos]=val[pos]; pos++; } m_pos+=pos; return true; }; //Переписать строку без нолика на конце
  	bool writeStr2r(std::string str) //Первых 2 байта под длину
  	{
  		if(str.length()>=65535) return false;
  		unsigned short size=str.length();
  		m_data[m_pos] = ((uint8_t*)&size)[1];
  		m_pos++;
  		m_data[m_pos] = ((uint8_t*)&size)[0];
  		m_pos++;
  		write(str.c_str(),str.length());
  		return true;
  	}//Для MQTT протокола (2 первых байта длина строки)

  	std::string readStr2r() //Первых 2 байта под длину (Для MQTT протокола)
  	{
  		std::string result;
  		unsigned short len;
  		((uint8_t*)&len)[1] = m_data[m_pos];
  		m_pos++;
  		((uint8_t*)&len)[0] = m_data[m_pos];
  		m_pos++;
  		result.append((char *)&m_data[m_pos], len);
  		m_pos+=len;
  		return result;
  	}


  	unsigned char readUInt1()
  	{
  	    unsigned char result=m_data[m_pos];
  	    m_pos++;
  	    return result;
  	};
  	char readInt1(){ char result=m_data[m_pos]; m_pos+=1; return result; };
  	unsigned short readUInt2() { unsigned short result=*((unsigned short*)&m_data[m_pos]); m_pos+=2; return result; };

  	short readInt2() { short result=*((short*)&m_data[m_pos]); m_pos+=2; return result; };
  	short readInt2R() //Байты в обратном порядке
  	{
  		short result;
  		result = *((unsigned char*)&m_data[m_pos]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+1]);
  		m_pos+=2;
  		return result;
  	};
  	unsigned short readUInt2R() //Байты в обратном порядке (big endian)
  	{
  		unsigned short result;
  		result = *((unsigned char*)&m_data[m_pos]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+1]);
  		m_pos+=2;
  		return result;
  	};

  	unsigned int readUInt3() //Читаем 3 байта
  	{
  		unsigned int result = 0;
  		((unsigned char*)&result)[0] = m_data[m_pos];
  		((unsigned char*)&result)[1] = m_data[m_pos+1];
  		((unsigned char*)&result)[2] = m_data[m_pos+2];
  		m_pos+=3;
  		return result;
  	}

  	unsigned int readUInt3R() //Читаем 3 байта
  	{
  		unsigned int result = 0;
  		((unsigned char*)&result)[0] = m_data[m_pos+2];
  		((unsigned char*)&result)[1] = m_data[m_pos+1];
  		((unsigned char*)&result)[2] = m_data[m_pos];
  		m_pos+=3;
  		return result;
  	}

  	unsigned int readUInt4() { unsigned int result=*((unsigned int*)&m_data[m_pos]); m_pos+=4; return result; };
  	unsigned int readUInt4R() // Байты в обратном порядке
  	{
  		unsigned int result;
  		result = *((unsigned char*)&m_data[m_pos]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+1]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+2]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+3]);
  		m_pos+=4;
  		return result;
  	};
  	int readInt4() { int result=*((int*)&m_data[m_pos]); m_pos+=4; return result; };
  	unsigned long long readUInt7()
  	{
  		char buf[8]={0,0,0,0,0,0,0,0};
  		for(int ch=0;ch<7;ch++)
  			buf[ch]=m_data[m_pos+ch];
  		unsigned long long result=*((unsigned long long*)buf);
  		m_pos+=7;
  		return result;
  	};
  	unsigned long long readUInt7R() //014135835392AB = 353173069075115
  	{
  		char buf[8]={0,0,0,0,0,0,0,0};
  		for(int ch=0;ch<7;ch++)
  		{
  			buf[ch]=m_data[m_pos+6-ch];
  		}
  		unsigned long long result=*((unsigned long long*)buf);
  		m_pos+=7;
  		return result;
  	};

  	unsigned long long readUInt8() { unsigned long long result=*((unsigned long long*)&m_data[m_pos]); m_pos+=8; return result; };
  	unsigned long long readUInt8R()
  	{
  		unsigned long long result;
  		result = *((unsigned char*)&m_data[m_pos]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+1]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+2]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+3]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+4]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+5]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+6]);
  		result = result << 8;
  		result = result | *((unsigned char*)&m_data[m_pos+7]);
  		m_pos+=8;
  		return result;
  	};


  	bool Read(void* val, int len)
  	{
  		for(int i=0;i<len;i++)
  		{
  			((char*)val)[i]=m_data[m_pos];
  			m_pos+=1;
  		}
  		return true;
  	};
  	std::string readString(int len){
  		std::string result;
  		if(len>0)
  		{
  			result.assign(&m_data[m_pos], len);
  			m_pos+=len;
  		}
  		return result;
  	}
  };

}
#endif //MSTREAM_HPP_
