#include "ZString.h"
#include "Log.h"
#include "Exception.h"
#include <stdlib.h>
#include <iostream>

namespace coreutils {

   std::ostream &operator<<(std::ostream &os, const ZString &zstring) {
      for (char *ix = zstring.data; ix < (zstring.data + zstring.length); ++ix)
        os << *ix;
      return os;
   }

   std::ostream &operator<<(std::ostream &os, const std::string &string) {
      os << string;
      return os;
   }

   std::ostream &operator+(std::ostream &os, const ZString &zstring) {
      os << zstring;
      return os;
   }

   std::ostream &operator+(std::ostream &os, const char *value) {
      os << value;
      return os;
   }

   ZString::ZString() {
      data = NULL;
      length = 0;
      cursor = data;
   }

   ZString::ZString(const char *data) {
      if((char *)data != NULL) {	   
	 this->data = (char *)data;
	 this->length = strlen(data);
	 this->cursor = (char *)data;
      } else {
	 this->data = NULL;
	 this->length = 0;
	 this->cursor = NULL;
      }
   }
   
   ZString::ZString(char *data, size_t length) : data(data), length(length), cursor(data) {}

   ZString::ZString(unsigned char *data, size_t length) : data((char *)data), length(length), cursor((char *)data) {}

   ZString::ZString(const char *data, size_t length) : data((char *)data), length(length), cursor((char *)data) {}

   ZString::ZString(const unsigned char *data, size_t length) : data((char *)data), length(length), cursor((char *)data) {}

   ZString::ZString(const ZString &zstring) {
      data = zstring.data;
      length = zstring.length;
      cursor = zstring.cursor;
   }

   ZString::ZString(std::string &data) {
      this->data = (char *)data.c_str();
      length = data.length();
      cursor = (char *)data.c_str();
   }

   ZString::~ZString() {
      if(cdata != NULL) 
	free(cdata);      
   }

   bool ZString::operator<(const ZString &valuex) const {
      char *end1 = data + length;
      char *end2 = valuex.getData() + valuex.getLength();
      char *cursor1 = cursor;
      char *cursor2 = valuex.getCursor();
      while((cursor1 < end1) && (cursor2 < end2)) {
	 if(*cursor1 < *cursor2)
	   return 1;
	 if(*cursor1 > *cursor2)
	   return 0;
	 ++cursor1;
	 ++cursor2;
      }
      if((cursor1 == end1) && (cursor2 != end2))
	return 1;
      if((cursor2 == end2) && (cursor1 != end1))
	return 0;
      return 0;          
   }

   // TODO: Fix the following comparison operators to handle the length problem appropriately.
   
   bool ZString::operator<=(const ZString &valuex) const {
      return (strncmp(data, valuex.data, valuex.length <= length ? valuex.length : length) <= 0);
   }
   
   bool ZString::operator>=(const ZString &valuex) const {
      return (strncmp(data, valuex.data, valuex.length <= length ? valuex.length : length) >= 0);
   }
   
   bool ZString::operator>(const ZString &valuex) const {
      return (strncmp(data, valuex.data, valuex.length <= length ? valuex.length : length) > 0);
   }

   bool ZString::operator==(const ZString valuex) {
      if(valuex.getLength() != length)
	return false; 
      return (strncmp(data, valuex.data, length) == 0);
   }

   bool ZString::operator!=(const ZString valuex) const {
      if(length != valuex.getLength())
	return true;
      return (strncmp(data, valuex.data, length) != 0);
   }

   bool ZString::operator!=(const char *valuex) const {
      if(valuex == NULL) {
	 if(length == 0)
	   return false;
	 else
	   return true;
      } else {	   
	 if(strlen(valuex) != length)
	   return true;
	 return (strncmp(data, valuex, length) != 0);
      }
   }
   
   std::vector<ZString> &ZString::getList() {
      return list;
   }

   int ZString::asInteger() {
      int value = 0;
      bool negative = false;
      char *end = data + length;
      if(*cursor == '-') {
	 negative = true;
	 ++cursor;
      } else if(*cursor == '+') 
	   ++cursor;
      if(cursor == end)
	return 0;
      while((*cursor >= '0') && (*cursor <= '9')) {
	 value *= 10;
	 value += *cursor - 48;
	 ++cursor;
	 if(cursor == end) {
	    if(negative)
	      return -value;
	    else
	      return value;
	 }	 
      }
      if(negative)
	return -value;
      return value;
   }

   double ZString::asDouble() {
      double value = 0;
      bool negative = false;
      char *end = data + length;
      if(*cursor == '-') {
	 negative = true;
	 ++cursor;
      } else if(*cursor == '+') 
	   ++cursor;
      if(cursor == end)
	return 0;
      while((*cursor >= '0') && (*cursor <= '9')) {
	 value *= 10;
	 value += *cursor - 48;
	 ++cursor;
	 if(cursor == end) {
	    if(negative)
	      return -value;
	    else
	      return value;
	 }	 
      }
      if(*cursor != '.') {	   
	 if(negative)
	   return -value;
	 else
	   return value;
      }	       
      ++cursor;
      if(cursor == end) {
	 if(negative)
	   return -value;
	 else
	   return value;
      }	 
      double div = 10;
      while((*cursor >= '0') && (*cursor <= '9')) {
	 value += (*cursor - 48) / div;
	 div *= 10;
	 ++cursor;
	 if(cursor == end) {
	    if(negative)
	      return -value;
	    else
	      return value;
	 }	 
      }             
      if(negative)
	return -value;
      return value;
   }

   int ZString::incrementCursor(int length) {
      char *temp = cursor;
      if((cursor + length) > (data + this->length))
	cursor = data + this->length;
      else
	cursor  += length;
      return cursor - temp;
   }

   void ZString::push() {
      stack.push(cursor);
   }

   void ZString::pop() {
      cursor = stack.top();
      stack.pop();
   }

   std::string ZString::str() {
      return std::string(data, length);
   }

   std::string ZString::str(int len) {
      return std::string(data, len);
   }

   char* ZString::c_str() {
      if(cdata == NULL)
	cdata = (char *)malloc(length + 1);
      else
	cdata = (char *)realloc(cdata, length + 1);
      strncpy(cdata, data, length);
      cdata[length] = '\0';
      return cdata;
   }

   ZString ZString::substring(int start) {
      char *end = data + length;
      char *startChar = data + start;
      if(startChar < end) {
	 return ZString(startChar, end - startChar);
      }
      return ZString();
   }
   
   ZString ZString::substring(int start, int len) {
      char *end = data + length;
      char *startChar = data + start;
      char *endChar = startChar + len;
      char newlen = endChar > end ? endChar - end: len;
      if(startChar < end) 
	return ZString(startChar, newlen);
      return ZString();
   }   
   
   std::vector<ZString> &ZString::split(std::string delimiter, size_t maxSize) {
      coreutils::ZString zDelimiter((char *)delimiter.c_str(), delimiter.size());
      return split(zDelimiter, maxSize);
   }

   std::vector<ZString> &ZString::split(ZString &delimiter, size_t maxSize) {	   
      list.clear();
      if (length == 0) {
         list.push_back(ZString(""));
         return list;
      }
      maxSize = maxSize == 0 ? 255: maxSize;
      char *end = data + length;
      char *pos = cursor;
      while ((pos + delimiter.getLength()) <= end) {
         if ((strncmp(pos, delimiter.getData(), delimiter.getLength()) == 0) && (maxSize != 0)) {
            list.push_back(ZString(cursor, pos - cursor));
            cursor = pos + delimiter.getLength();
            pos = cursor;
	    --maxSize;
         }
         else {
            ++pos;
         }
      }
      list.push_back(ZString(cursor, pos - cursor));
      cursor = data;
      return list;
   }
   
   bool ZString::isCharacter(char ch, ZString include) {
      for (int ix = 0; ix < include.getLength(); ++ix) {
         if (ch == include.charAt(ix))
	   return true;
      }
      return false;
   }

   bool ZString::ifNextInclude(ZString include) {
      if((cursor <= (data + length)) && isCharacter(*cursor, include))
	return true;
      return false;
   }

   
   ZString ZString::getTokenInclude(const char *include) {
      char *start = cursor;
      while ((cursor <= (data + length)) && isCharacter(*cursor, include))
	++cursor;
      return ZString(start, cursor - start);
   }

   ZString ZString::getTokenExclude(const char *exclude) {
      char previous = 0;
      char *start = cursor;
      do {	   
	 while ((cursor < (data + length)) && !isCharacter(*cursor, exclude)) {
	    previous = *cursor;
	    ++cursor;
	 }
	 ++cursor;
      } while(previous == '\\');
      --cursor;
      return ZString(start, cursor - start);
   }

   ZString ZString::getTokenExclude(std::string exclude) {
      return getTokenExclude(exclude.c_str());
   }

   ZString ZString::getContainer() {
      char term = *cursor == '"' ? '"': *cursor == '(' ? ')': *cursor == '[' ? ']': *cursor == '{' ? '}': 0;
      if(!term)
        return ZString();
      char *start = cursor;
      char nest = *cursor;
      char *end = cursor + length;
      int level = 0;
      ++cursor;
      while(cursor != end) {
         if(*cursor == term) {
            if(!level) {
               ++cursor;
               return ZString(start, cursor - start);
            }
            --level;
         } else if(*cursor == nest) {
            ++level;
         }
         ++cursor;
      }
      return ZString();
   }

   ZString &ZString::operator[](int index) {
      return list[index];
   }

   bool ZString::eod() {
      return cursor >= data + length;
   }

   int ZString::goeod() {
      char *temp = cursor;
      cursor = data + length;
      return cursor - temp;
   }
   
   bool ZString::startsWith(const char *value) {
      return strncmp(cursor, value, strlen(value)) == 0;
   }

   bool ZString::startsWithNumber() {
      if(*cursor == '-')
	return true;
      return ((*cursor >= '0') && (*cursor <= '9'));
   }

   int ZString::compare(ZString zstring) {
      char *end1 = data + length;
      char *end2 = zstring.getData() + zstring.getLength();
      char *cursor1 = cursor;
      char *cursor2 = zstring.getCursor();
      while((cursor1 < end1) && (cursor2 < end2)) {
	 if(*cursor1 < *cursor2)
	   return -1;
	 if(*cursor1 > *cursor2)
	   return 1;
	 ++cursor1;
	 ++cursor2;
      }
      if((cursor1 == end1) && (cursor2 != end2))
	return -1;
      if((cursor2 == end2) && (cursor1 != end1))
	return 1;
      return 0;      
   }
      
   bool ZString::equals(const char *value) {
      if (strlen(value) != length)
	return false;
      return strncmp(data, value, length) == 0;
   }

   bool ZString::equals(char *value) {
      if (strlen(value) != length)
	return false;
      return strncmp(data, value, length) == 0;
   }

   bool ZString::equals(ZString &zstring) {
      if (zstring.getLength() != length)
	return false;
      return strncmp(data, zstring.getData(), length) == 0;
   }

   bool ZString::equals(std::string &string) {
      return string == std::string(data, length);
   }

   bool ZString::ifNext(ZString value) {
      if (((data + length) - cursor) < value.getLength())
	return false;
      bool test = (strncmp(cursor, value.getCursor(), value.getLength()) == 0);
      if (test)
	cursor += value.getLength();
      return test;      
   }   

   bool ZString::ifNextIgnoreCase(ZString value) {
      if (((data + length) - cursor) < value.getLength())
	return false;
      bool test = (strncasecmp(cursor, value.getCursor(), value.getLength()) == 0);
      if (test)
	cursor += value.getLength();
      return test;      
   }   
   
   int ZString::ifEqualsCount(ZString &comparator) {
      int count = 0;
      while (cursor < (data + length)) {
         if (*cursor == *comparator.cursor) {
            ++count;
            ++cursor;
            ++comparator.cursor;
         }
         else {
            return count;
         }
      }
      return count;
   }
   
   int ZString::skipWhitespace() {
      char *end = data + length;
      int len = 0;
      while((cursor < end) && ((*cursor == ' ') || (*cursor == '\n') || (*cursor == '\t'))) {
	 ++cursor;
	 ++len;
      }      
      return len;
   }

   int ZString::trimTrailingWhitespace() {
      goeod();
      int len = 0;
      --cursor;
      while((cursor > data) && ((*cursor == ' ') || (*cursor == '\n') || (*cursor == '\t'))) {
	 --cursor;
	 --length;
	 ++len;
      }      
      ++cursor;
      return len;
   }
   
   bool ZString::lineIsWhitespace() {
      char *end = data + length;
      char *temp = cursor;
      while(temp < end) {
	 if(*temp == '\n')
	   return true;
	 if((*temp != ' ') && (*temp != '\t'))
	   return false;
	 ++temp;
      }
      return true;
   }
      
   ZString ZString::goeol() {
      bool set = false;
      char *temp = cursor;
      char *tempend = data + length;
      while (cursor <= (data + length)) {
         if (*cursor == '\r') {
            tempend = cursor++;
            set = true;
         }
         if (*cursor == '\n') {
            if (!set)
	      tempend = cursor;
            ++cursor;
            break;
         }
         ++cursor;
      }
      return ZString(temp, tempend - temp);
   }

   ZString ZString::goeolwithContinuation() {
      ZString temp = goeol();
      while(ifNextInclude(" \t"))
	goeol();
      return temp;
   }

   ZString ZString::trim() {
      trimTrailingWhitespace();
      reset();
      skipWhitespace();
      return unparsed();
   }

   int ZString::find(ZString comparator) {
      int count = 0;
      char *temp = cursor;
      char *temp2 = cursor;
      while(!ifNext(comparator)) {
         ++count;
         ++cursor;
         ++temp2;
         if(cursor > (data + length)) {
            cursor = temp;
            return -1;
         }
      }
      cursor = temp2;
      return count;
   }

   ZString ZString::readBlock(size_t size) {
      char *temp = cursor;
      cursor += size;
      return ZString(temp, size);
   }
   
   char *ZString::getData() const {
      return data;
   }
   
   char *ZString::getCursor() const {
      return cursor;
   }

   void ZString::setCursor(char *cursor) {
      if((cursor < data) || (cursor > (data + length))) 
	throw Exception("Cursor out of range on setCursor.");
      this->cursor = cursor;
   }
   
   size_t ZString::getLength() const {
      return length - (cursor - data);
   }
   
   void ZString::setZString(ZString zstring) {
      data = zstring.getData();
      length = zstring.getLength();
      cursor = data;
   }

   ZString ZString::parsed() {
      return ZString(data, cursor - data);
   }

   ZString ZString::unparsed() {
      return ZString(cursor, data + length - cursor);
   }

   int ZString::setParsed() {
      cursor = data + length;
      return length;
   }
      
   void ZString::reset() {
      cursor = data;
   }

   char ZString::charAt(int index) {
      return *(cursor + index);
   }

   bool ZString::trimCRLF() {
      int len = length;
      if (*(data + length - 1)  == '\n')
	--length;
      if (*(data + length - 1) == '\r')
	--length;
      if (cursor >= (data + length))
	     cursor = data + length;
      return len != length;
   }

   bool ZString::boolValue() {
      if(this->equals("false"))
        return false;
      else if(this->equals("0"))
        return false;
      else if(this->equals(""))
        return false;
      return true;
   }

   ZString ZString::removeTrailingZeros() {
      if(length > 2) {	   
	 while((*(data + length - 1)) == '0')
	   --length;
	 if(*(data + length - 1) == '.')
	   --length;
      }
      return *this;
   }

   char ZString::getChar() {
      return *cursor;
   }
      
   char ZString::nextChar() {
      char temp = 0;
      if(!eod()) 
	temp = *cursor++;
      return temp;
   }

   int ZString::getLineNumberAtCursor() {
      char *temp = cursor;
      int line = 1;
      while(temp != data) 
	if(*temp-- == '\n')
	  ++line;
      return line;      	   
   }

   void ZString::moveBackToLineStart() {
      while(cursor != data) {
	 if(*cursor == '\n') {
	    cursor++;
	    break;
	 }
	 cursor--;
      }  
      return;
   }
      
}
