#include "JString.h"
#include "Exception.h"

namespace coreutils {

   std::ostream &operator<<(std::ostream &os, const JString::Proxy &proxy) {
      ZString tempkey = proxy.key;
      ZString temp = proxy.jstring.find(tempkey);
      os << temp;
      return os;
   }
   
   JString::JString() : MString("{}") {}

   void JString::set(ZString &path, ZString &value) {
      reset();
      if(locate(path)) {
         push();
         if(ifNext("\"")) {
            ZString old = getTokenExclude("\"");
            pop();
            remove(offset(), old.getLength() + 2);
            MString key;
	    if(value.startsWith("[")) 
	      key << value;
	    else if(value.startsWith("{")) 
	      key << value;
	    else
	      key << "\"" << value << "\"";	    
            insert(key, offset());
         } else {
	    pop();
	    throw coreutils::Exception("Fix me to accept integer type.");
	 }
      } else {
//         std::cout << "[" << unparsed() << "] >" << value << "<" << std::endl;
         MString key;
	 if(notfirst)
           key << ",";
	 // TODO: The value added must be parsed down to remove all whitespace.
	 if(value.startsWith("[")) 
	   key << "\"" << path << "\":" << value;
	 else if(value.startsWith("{")) 
	   key << "\"" << path << "\":" << value;
	 else
	   key << "\"" << path << "\":" << "\"" << value << "\"";
         insert(key, offset());
      }
   }

   ZString JString::find(ZString path) {
      reset();
      if(locate(path)) {
         if(ifNext("\""))
	   return getTokenExclude("\"");
// TODO: Handle numbers that are not in double quotes.

         else if(startsWith("[")) 
	   return getContainer();
         
         else if(startsWith("{")) 
	   return getContainer();         
      }
      return ZString();
   }

   bool JString::locate(ZString path) {
//      std::cout << "path locate: " << path << std::endl;
      path.getTokenExclude("[.");
      notfirst = false;
      if(ifNext("{")) {	
         while(startsWith("\"")) {
	    notfirst = true;
            ZString label;
            ZString value;
            if(ifNext("\"")) {
               label = getTokenExclude("\"");
               ifNext("\"");
            }  else
              throw coreutils::Exception("Labels must be contained in double quotes.");
            if(ifNext(":")) {
//	       std::cout << "path: (" << path.parsed() << ") (" << label << ") " << (label == path.parsed()) << std::endl;	       
               if(label == path.parsed()) {
                  if(path.unparsed() == "") {
//		     std::cout << "found cursor: " << unparsed() << std::endl;
		     return true;
		  }
		  if(path.ifNext("."))
		    return locate(path.unparsed());
		  if(path.ifNext("[")) {
		     int index = path.asInteger();
		     if(!path.ifNext("]"))
		       throw coreutils::Exception("expecting ] after index value in array selector.");
		     if(ifNext("[")) {
			while(index > 0) {
			   getContainer();
			   if(!ifNext(","))
			     throw coreutils::Exception("expecting comma to seperate array elements.");
			   --index;
			}			   
			return true;
		     } else
		       throw coreutils::Exception("Path is expecting an array for selector value.");		     
		     if(path.eod())
		       return false;
		     else if(path.ifNext("."))
		       return locate(path.unparsed());
		     throw coreutils::Exception("expecting '.' in path.");
		  }		  
               }
//	       std::cout << "+++" << unparsed() << std::endl;
	       if(startsWith("\"")) {
                  getContainer();
	       } else if(startsWith("[")) {
                  getContainer();
	       } else if(startsWith("{")) {
                  getContainer();
               } else {
                  throw coreutils::Exception("Unrecognized data type.");
               }
               ifNext(",");
            } else
              throw coreutils::Exception("Expected colon after label.");
         }
      } else if(ifNext("[")) {
         ZString label = path[0].getTokenExclude("[");
         path.ifNext("[");
         ZString key = path[0].getTokenExclude("=");
         path.ifNext("=");
         ZString value = path[0].getTokenExclude("]");
         while(!ifNext("]")) {
            push();
            ZString check = find(key);
            pop();
            if(check.equals(value)) {
               return locate(label);
            } else {
               getContainer();
               ifNext(",");
            }
         }
      }
//      std::cout << "not found cursor: " << unparsed() << std::endl;
      return false;
   }

   JString & JString::operator=(coreutils:: ZString value) {
      setSize(value.getLength());
      memcpy(data, value.getData(), value.getLength());
      removeWhitespace();
      return *this;
   }
      
   void JString::removeWhitespace() {
      reset();
      while(!eod()) {
	 if(*cursor == ' ')
	   remove(1);
	 else if(*cursor == '\r')
	   remove(1);
	 else if(*cursor == '\t')
	   remove(1);
	 else if(*cursor == '\n')
	   remove(1);
	 else if(startsWith("\""))
	   getContainer();
	 else
	   ++cursor;
      }
      reset();
   }

   MString JString::pretty() {
      MString output;
      reset();
      char ch;
      int index = 0;
      while(!eod()) {
	 ch = nextChar();
	 if(ch == '{') {
	    index += 3;
	    output << ch << '\n';
	    for(int ix = 0; ix < index; ++ix)
	      output << ' ';
	 }
	 else if(ch == '[') {
	    index += 3;
	    output << ch << '\n';
	    for(int ix = 0; ix < index; ++ix)
	      output << ' ';
	 }
	 else if(ch == ':') {
	    output << ": ";
	 }	 
	 else if(ch == '}') {
	    index -= 3;
	    output << '\n';
	    for(int ix = 0; ix < index; ++ix)
	      output << ' ';
	    output << ch;
	 }	 
	 else if(ch == ']') {
	    index -= 3;
	    output << '\n';
	    for(int ix = 0; ix < index; ++ix)
	      output << ' ';
	    output << ch;
	 }	 
	 else if(ch == ',') {
	    output << ch << '\n';
	    for(int ix = 0; ix < index; ++ix)
	      output << ' ';
	 }
	 else 
	   output << ch;
      }
      return output;      
   }

   void JString::changed(ZString key, ZString value) {}
   
}
