#include "ZString.h"
#include "Log.h"
#include "Exception.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) : data((char *)data), length(strlen(data)), cursor((char *)data) {}

   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;
      //      Log(LOG_DEBUG_2) << "ZString Copy Constructor: ";
   }

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

//   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 {
      return (strncmp(cursor, valuex.cursor, (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) {
      return (strncmp(data, valuex.data, valuex.length <= length ? valuex.length : length) == 0);
   }

   // bool ZString::operator==(std::string value) {
   //    if(value.length() != length)
	// return false;
   //    return (strncmp(data, value.c_str(), length) == 0);
   // }

//   bool ZString::operator==(const char *valuex) const {
//      return (strncmp(data, valuex, length) == 0);
//   }

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

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

   std::vector<ZString> &ZString::getList() {
      return list;
   }

   int ZString::asInteger() {
      std::stringstream temp(std::string(data, length));
      int tempInt = 0;
      temp >> tempInt;
      return tempInt;
   }

   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() {
      cdata = (char *)malloc(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) {
      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, const char *string) {
      int len = strlen(string);
      for (int ix = 0; ix < len; ++ix) {
         if (ch == string[ix])
	   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 *start = cursor;
      while ((cursor <= (data + length)) && !isCharacter(*cursor, exclude))
	++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;
   }

   bool ZString::startsWith(const char *value) {
      return strncmp(cursor, value, strlen(value)) == 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(const char *value) {
      if (((data + length) - cursor) < strlen(value))
	return false;
      bool test = (strncmp(cursor, value, strlen(value)) == 0);
      if (test)
	cursor += strlen(value);
      return test;
   }

   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;      
   }   
   
   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() {
      int len = strspn(cursor, " \n\t");
      cursor += len;
      return len;
   }

   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);
   }

   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;
            count = 0;
            break;
         }
      }
      cursor = temp2;
      return count;
   }

   ZString ZString::readBlock(size_t size) {
      char *temp = cursor;
      cursor += size;
      return ZString(temp, size);
   }
   
   char *ZString::getData() {
      return data;
   }
   
   char *ZString::getCursor() {
      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() {
      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);
   }

   void ZString::reset() {
      cursor = data;
   }

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

   bool ZString::ifCRLF() {
      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;
   }


   void ZString::nextChar() {
      if(!eod())
	++cursor;
   }

}
