#include "MString.h"
#include "Log.h"
#include "uuid/uuid.h"
#include "Exception.h"
#include "Base64.h"
#include <cstring>
#include <string>
#include <format>
#include <iomanip>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <cctype>
#include <fstream>

namespace coreutils {
   
   MString::MString() {
      data = NULL;
      length = 0;
      cursor = NULL;
      setSize(0);
   }
   
   MString::MString(const char *data) {
      setSize(strlen(data));
      memcpy(this->data, data, length);
      cursor = this->data;
   }
   
   MString::MString(char *data, size_t length) {
      setSize(length);
      memcpy(this->data, data, length);
      cursor = this->data;
   }
   
   MString::MString(unsigned char *data, size_t length) {
      setSize(length);
      memcpy(this->data, data, length);
      cursor = this->data;
   }
   
   MString::MString(const char *data, size_t length) {
      setSize(length);
      memcpy(this->data, data, length);
      cursor = this->data;
   }

   MString::MString(const ZString &zstring) {
      setSize(zstring.getLength());
      memcpy(data, zstring.getData(), zstring.getLength());
      cursor = data;      
   }   

   MString::MString(ZString &zstring) {
      setSize(zstring.getLength());
      memcpy(data, zstring.getData(), zstring.getLength());
      cursor = data;
   }
  
   MString::MString(const MString &mstring) {
      setSize(mstring.length);
      memcpy(data, mstring.data, mstring.length);
      cursor = data;
   }
   
   MString::MString(MString &mstring) {
      setSize(mstring.getLength());
      memcpy(data, mstring.getData(), mstring.getLength());
      cursor = data;
   }
   
//   MString::MString(MString mstring) {
//      setSize(mstring.getLength());
//      memcpy(data, mstring.getData(), mstring.getLength());
//      cursor = data;
//      printf("MString Copy Constructor %p\n", this);
//   }
   
   MString::MString(std::string data) {
      setSize(data.length());
      memcpy(this->data, (char *)data.c_str(), data.length());
      cursor = this->data;
   }
   
   MString::MString(std::FILE *file) {
      setSize(1000000);
      this->length = fread(this->data, 1000000, 1000000, file);
      cursor = this->data;
   }

   MString::MString(double value) {
      std::string temp = std::format("{:.12f}", value);
      temp.erase(temp.find_last_not_of('0') + 1, std::string::npos);
      if (temp.back() == '.')
	temp.pop_back();      
      setSize(temp.length());
      memcpy(this->data, (char *)temp.data(), temp.length());
      cursor = this->data;
   }
   
   MString::~MString() {
      if (data)
	free(data);
   }
   
   MString &MString::operator=(coreutils::MString value) {
      setSize(value.getLength());
      memcpy(data, value.getData(), value.getLength());
      length = value.getLength();
      onChange();
      return *this;
   }

   MString &MString::operator=(coreutils::ZString value) {
      setSize(value.getLength());
      memcpy(data, value.getData(), value.getLength());
      length = value.getLength();
      onChange();
      return *this;
   }

   MString &MString::operator=(std::string value) {
      setSize(value.length());
      memcpy(data, value.c_str(), value.length());
      length = value.length();
      onChange();
      return *this;
   }
   
   MString &MString::operator=(const char *value) {
      int len = strlen(value);
      setSize(len);
      memcpy(data, value, len);
      onChange();
      return *this;
   }
   
   MString &MString::operator=(char value) {
      int len = 1;
      setSize(1);
      *data = value;
      onChange();
      return *this;
   }

   MString &MString::operator=(double value) {
      std::string temp = std::format("{:.12f}", value);
      temp.erase(temp.find_last_not_of('0') + 1, std::string::npos);
      if (temp.back() == '.')
	temp.pop_back();      
      setSize(temp.length());
      memcpy(this->data, (char *)temp.data(), temp.length());
      cursor = this->data;
      onChange();
      return *this;
   }

   MString &MString::operator=(int value) {
      std::string temp = std::format("{}", value);
      setSize(temp.length());
      memcpy(this->data, (char *)temp.data(), temp.length());
      cursor = this->data;
      onChange();
      return *this;
   }

   MString &MString::operator<<(const char *value) {
      int temp = length;
      int len = strlen(value);
      setSize(len + length);
      memcpy(data + temp, value, len);
      onChange();
      return *this;
   }
   
   MString &MString::operator<<(const int value) {
      std::stringstream temp;
      temp << value;
      MString temp2 = temp.str();
      write(temp2);
      onChange();
      return *this;
   }
   
   //   MString& MString::operator<<(coreutils::ZString &zstring) {
   //      int temp = length;
   //      int len = length + zstring.getLength();
   //      setSize(len);
   //      memcpy(data + temp, zstring.getData(), zstring.getLength());
   //      return *this;
   //   }
   
   MString &MString::operator<<(coreutils::ZString zstring) {
      int temp = length;
      int len = length + zstring.getLength();
      setSize(len);
      memcpy(data + temp, zstring.getData(), zstring.getLength());
      onChange();
      return *this;
   }
   
   MString &MString::operator<<(char value) {
      setSize(length + 1);
      data[length - 1] = value;
      onChange();
      return *this;
   }

//   MString &MString::operator<<(std::ostream (*f)(std::ostream&)) {
   MString &MString::operator<<(std::basic_ostream<char> (*pf)(std::basic_ostream<char>&)) {
//      int temp = length;
//      int len = length + value.length();
//      setSize(len);
//      memcpy(data + temp, value.c_str(), value.length());
      std::cout << "xxx" << std::endl;
      return *this;
   }

   MString &MString::operator<<(std::ifstream &pf) {
      std::string line;
      while(std::getline(pf, line)) 
	*this << line << '\n';      
      return *this;
   }
   
//   MString &operator<<(std::ostream &out) {
//      *this << out;
//   }
   
   void MString::insert(char ch, int offset) {
      setSize(length + 1);
      for (int ix = length; ix >= offset; ix--)
	getData()[ix + 1] = getData()[ix];
      data[offset] = ch;
      onChange();
   }
   
   void MString::insert(ZString value, int offset) {
      setSize(length + value.getLength());
      for (int ix = length; ix >= offset; ix--)
	getData()[ix] = getData()[ix - value.getLength()];
      std::memcpy(data + offset, value.getData(), value.getLength());
      onChange();
   }
   
//   void MString::insert(std::string value, int offset) {
//      setSize(length + value.size());
//      for (int ix = length; ix >= offset; ix--)
//	getData()[ix + value.size()] = getData()[ix];
//      std::memcpy(data + offset, value.c_str(), value.size());
//   }
   
   void MString::replace(char ch, int offset) {
   }
   
   void MString::replace(ZString &value, int offset) {
   }
   
   void MString::replace(std::string value, int offset) {
   }
   
   void MString::remove(int length) {
      int len = (data + this->length - cursor);
      for (int ix = 0; ix < len; ix++)
	cursor[ix] = cursor[ix + length];
      setSize(this->length - length);
      onChange();
   }

   void MString::remove(int offset, int length) {
      for (int ix = offset; ix < this->length; ix++)
	getData()[ix] = getData()[ix + length];
      setSize(this->length - length);
      onChange();
   }
   
   MString &MString::write(char ch) {
      setSize(length + 1);
      *(data + length - 1) = ch;
      onChange();
      return *this;
   }
   
   MString &MString::write(ZString value) {
      int len = length;
      setSize(length + value.getLength());
      memcpy(data + len, value.getData(), value.getLength());
      onChange();
      return *this;
   }
   
   void MString::setSize(int size) {
      int cursorOffset = cursor - data;
      int newBufferSize = ((size / 256) + 1) * 256;
      if (bufferSize != newBufferSize) {
         bufferSize = newBufferSize;
         data = (char *)realloc(data, bufferSize);
         cursor = data + cursorOffset;
      }
      length = size;
   }

   MString &MString::read(int fd) {
      char ch;
      while(::read(fd, &ch, 1))
	write(ch);
      onChange();
      return *this;
   }

   MString &MString::read(int fd, int count) {
      char ch;
      while(count-- && ::read(fd, &ch, 1)) 	 
	write(ch);
      onChange();
      return *this;
   }
   
   int MString::offset() {
      return cursor - data;
   }

   MString MString::toBinary() {
      push();
      reset();
      MString target;
      char temp;
      while(!eod()) {
	 temp = nextChar();
	 if(strchr("\\'\".\0\r\n", temp))
	   target.write('\\');
	 target.write(temp);
      }
      pop();
      return target;
   }
   
   MString MString::fromBinary() {
      push();
      reset();
      MString target;
      while(!eod()) {
	 if(ifNext("\\r"))
	   target.write(13);
	 else if(ifNext("\\n"))
	   target.write(10);
	 else if(ifNext("\\0"))
	   target.write(0);
	 else if(ifNext("\\\\"))
	   target.write("\\");
	 else if(ifNext("\\."))
	   target.write(".");
	 else if(ifNext("\\\""))
	   target.write("\"");
	 else if(ifNext("\\\'"))
	   target.write("'");
	 else
	   target.write(nextChar());
      }
      pop();
      return target;
   }
   
   MString MString::toHex() {
      push();
      reset();
      MString target;
      char temp;
      while(!eod()) {	   
	   temp = nextChar();
	   char temp2 = temp;
	   temp >>= 4;
	   target.write(hexChar(temp));
	   target.write(hexChar(temp2));
	}      
      pop();
      return target;
   }
   
   MString MString::fromHex() {
      push();
      reset();
      MString target;
      while(!eod()) {
	 char ch1 = nextChar();
	 ch1 -= 48;
	 if(ch1 > 9)
	   ch1 -= 7;
	 ch1 <<= 4;
	 ch1 &= 240;
	 if(eod())
	   coreutils::Exception("conversion from hex requires even number of characters.");
	 char ch2 = nextChar();
	 ch2 -= 48;
	 if(ch2 > 9)
	   ch2 -= 7;
	 ch2 &= 15;
	 ch1 |= ch2;
	 target.write(ch1);
      }
      pop();
      return target;
   }
      
   MString MString::toBase64() {
      Base64 b64;
      return b64.encode(*this);
   }
   
   
   MString MString::fromBase64() {
      Base64 b64;
      return b64.decode(*this);
   }
   
   
   MString MString::toUpper() {
      MString target;
      for(int ix = 0; ix < length; ++ix)
	target.write(std::toupper(data[ix]));      
      return target;
   }
   
   
   MString MString::toLower() {
      MString target;
      for(int ix = 0; ix < length; ++ix)
	target.write(std::tolower(data[ix]));      
      return target;
   }
   
   
   MString MString::toCGI() {
      MString target;
      return target;
   }
   
   
   MString MString::fromCGI() {
      MString target;
      return target;
   }

   char MString::hexChar(char c) {
      c &= 15;
      c += 48;
      if(c > 57)
	c += 7;
      return c;
   }

   void MString::onChange() {}
   
}
