#include "Tag.h"
#include "Exception.h"
#include "KeywordValue.h"
#include "Log.h"
#include "__mysql.h"
#include "__sql.h"
#include "__comment.h"
#include "__for.h"
#include "__if.h"
#include "__read.h"
#include "__write.h"
#include "__set.h"
#include "__call.h"
#include "__system.h"
#include "__jet.h"
#include "__while.h"
#include <iostream>

namespace jet {
   
   Tag::Tag(coreutils::ZString &in, coreutils::MString &parent, Global &global, coreutils::ZString splitTagName)
     : ZString(in), parent(parent), global(global) {
	this->splitTagName = splitTagName;
	if(in.ifNext("<")) {
	   name = in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_!");	   
	   if(in.startsWith(" ") || in.startsWith("/") || in.startsWith(">")) {
	      coreutils::Log(coreutils::LOG_DEBUG_1) << "Processing tag: " << name;
	      bool finished = false;
	      while(!finished) {
		 in.skipWhitespace();
		 if(in.ifNext(">")) {
		    hasContainer = true;
		    hasContainer2 = false;
		    finished = true;
		    break;
		 } else if(in.ifNext("/>")) {
		    hasContainer = false;
		    hasContainer2 = false;
		    finished = true;
		    break;
		 }
		 if(!finished) {
		    coreutils::ZString keywordName = in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
		    if(in.ifNext("=\"")) {
		       variables[keywordName] = KeywordValue(in.getTokenExclude("\""), global);
		    }
		    if(!in.ifNext("\"")) {}
		 }
	      }
	      if(variableDefined("filterblanklines")) {
		 filterBlankLines = variables["filterblanklines"] == "true" ? true: false;
	      }	      
	      if(variableDefined("trimlines")) {
		 trimLines = variables["trimlines"] == "true" ? true: false;
	      }	      
	      if(hasContainer) {
		 coreutils::Log(coreutils::LOG_DEBUG_2) << "has Container: " << hasContainer;
		 bool hasSplitTag = splitTagName == "" ? false: true;
		 char *start = in.getCursor();
		 while(!in.eod()) {              
		    char *end = in.getCursor();   
		    if(ifEndTagName(in)) {
		       if(hasContainer2)
			 container2 = coreutils::ZString(start, end - start);
		       else
			 container = coreutils::ZString(start, end - start);
		       break;		       
		    } else if(hasSplitTag && ifSplitTagName(in)) {
		       hasContainer2 = true;
		       container = coreutils::ZString(start, end - start);
		       in.ifNext("<else>");
		       start = in.getCursor();		 
		    } else if(ifNested(in)) {
		    } else
		      in.nextChar();
		 }		 
	      }
	      setZString(in.parsed());
	      if(variableDefined("eval")) {
		 if(variables["eval"] == "yes") {
		    evaluate = true;
		 } else if(variables["eval"] == "no") {
		    evaluate = false;
		 } else 
		    throw coreutils::Exception("Keyword 'eval' must be 'yes' or 'no'.");		       
	      }
	   }
	} else
	  throw coreutils::Exception("First character of (in) must be a <.");
     }
   
   Tag::~Tag() {
      if(evaluate) 
	if(output)
	  copyContainer(out, parent);
      else 
	if(output)
	  copyContainer(container, parent);
   }

   void Tag::processContainer(coreutils::ZString &container) {
      if(hasContainer && evaluate)
	parseContainer(container);      
   }
      
   void Tag::parseContainer(coreutils::ZString &in) {
      char *start = in.getCursor();
      while(!in.eod()) {
	 if(in.startsWith("<")) {
	    if(ifTagName(in, "mysql")) {
	       __mysql _mysql(in, out, global);
	       continue;
	    } else if(ifTagName(in, "comment")) {
	       __comment _comment(in, out, global);
	       continue;
	    } else if(ifTagName(in, "sql")) {
	       __sql _sql(in, out, global);
	       continue;
	    } else if(ifTagName(in, "for")) {
	       __for _for(in, out, global);
	       continue;
	    } else if(ifTagName(in, "if")) {
	       __if _if(in, out, global);
	       continue;
	    } else if(ifTagName(in, "jet")) {
	       __jet _jet(in, out, global);
	       continue;
	    } else if(ifTagName(in, "read")) {
	       __read _read(in, out, global);
	       continue;
	    } else if(ifTagName(in, "write")) {
	       __write _write(in, out, global);
	       continue;
	    } else if(ifTagName(in, "set")) {
	       __set _set(in, out, global);
	       continue;
	    } else if(ifTagName(in, "call")) {
	       __call _call(in, out, global);
	       continue;
	    } else if(ifTagName(in, "system")) {
	       __system _system(in, out, global);
	       continue;
	    } else if(ifTagName(in, "while")) {
	       __while _while(in, out, global);
	       continue;
	    } else {
	       out.write(in.charAt(0));
	       in.nextChar();
	    }  
	 } else if(in.ifNext("#[")) {
	    coreutils::MString varName = in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
	    if(in.ifNext("]")) 
	      out.write(variables[varName]);
	    else
	      throw coreutils::Exception("Local variable name has a syntax error.");
	 } else if(in.startsWith("$[")) {	    
	    out.write(global.getVariable(in));
	 } else {
	    out.write(in.charAt(0));
	    in.nextChar();
	 }	 
      }
   }
   
   void Tag::scanContainer(coreutils::ZString &in) {
      while(!in.eod()) {
	 if(ifEndTagName(in)) 
	   return;
	 else if(ifNested(in)) {}	 
	 else in.nextChar();
      }
   }
   
   bool Tag::ifTagName(coreutils::ZString &in, const char *tag) {
      in.push();
      if(in.ifNext("<"))
        if(in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_").equals(tag)) {       
	   if(in.ifNext(" ")) {
	      in.pop();
	      return true;
	   } else if(in.ifNext("/>")) {
	      in.pop();
	      return true;
	   } else if(in.ifNext(">")) {
	      in.pop();
	      return true;
	   }	  
	}
      in.pop();
      return false;
   }
   
   bool Tag::ifTagName(coreutils::ZString &in) {
      in.push();
      if(in.ifNext("<"))
        if(in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_").equals(name)) {
	   in.push();
	   if(in.ifNext(" ")) {
	      in.pop();
	      return true;
	   } else if(in.ifNext("/>")) {
	      in.pop();
	      return true;
	   } else if(in.ifNext(">")) {
	      in.pop();
	      return true;
	   }	   
	   in.pop();
	}
      in.pop();
      return false;
   }
   
   bool Tag::ifEndTagName(coreutils::ZString &in) {
      in.push();
      if(in.ifNext("</"))
        if(in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_").equals(name)) {
	   if(in.ifNext(">")) 
	     return true;
	}      
      in.pop();
      return false;
   }

   bool Tag::ifSplitTagName(coreutils::ZString &in) {
      in.push();
      if(in.ifNext("<"))
        if(in.getTokenInclude("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_").equals(splitTagName)) {
	   if(in.ifNext(">"))
	     in.pop();
	     return true;
	}      
      in.pop();
      return false;
   }
   
   int Tag::skipBlankLine(coreutils::ZString in) {
      ZString temp = in.getTokenInclude(" \t");
      if(ifNext("\n"))
        return temp.getLength() + 1;
      return 0;
   }
   
   void Tag::copyContainer(coreutils::ZString &in, coreutils::MString &out) {
      while(!in.eod()) {	 
         if(filterBlankLines) {
	    if(!in.lineIsWhitespace()) {
	       if(trimLines) 
		 out.write(in.goeol().trim());
	       else	       
		 out.write(in.goeol());
	       out.write('\n');
	    }
	    else {
	       in.goeol();
	    }	    
	 }
         else {     
	    out.write(in.charAt(0));
	    in.nextChar();
	 }	 
      }
   }
   
   bool Tag::variableDefined(coreutils::ZString keyword) {
      return variables.find(keyword) != variables.end();
   }
   
   bool Tag::ifNested(coreutils::ZString &in) {
      bool hasContainer = false;
      if(ifTagName(in)) {
	 while(!in.eod()) {
	    in.skipWhitespace();
	    if(in.ifNext(">")) {
	       hasContainer = true;
	       break;
	    } else if(in.ifNext("/>")) {
	       hasContainer = false;
	       return true;
	    } else if(in.getTokenExclude("=").getLength() != 0) {
	       if(in.ifNext("=\"")) {
		  while(1) {
		     if(in.ifNext("\"")) {
			break;
		     }
		     in.nextChar();
		  }
	       }
	    }
	 }
	 if(hasContainer)
	   scanContainer(in);
      }
      return false;
   }
}
