#include "Operand.h"
#include "Exception.h"
#include <format>
#include <iostream>
#include <algorithm>
#include <time.h>
#include <chrono>
#include <cmath>

namespace jet {

   Operand::Operand(coreutils::ZString &in, Tag &tag, bool stop) : in(in), tag(tag) {

      doubleValue = 0;
      
      in.skipWhitespace();
      
      if(in.startsWith("$[") || in.startsWith("#[")) {
	 string = tag.getVariable(in);
	 doubleValue = string.asDouble();
	 isNumber = string.eod();
	 string.reset();
	 if((string == "false") || (string == "true"))
	   boolean = true;	 
      } else if(in.ifNext("(")) {
	 Operand op(in, tag);
	 string = op.string;
	 doubleValue = op.doubleValue;
	 isNumber = op.isNumber;
	 if(!in.ifNext(")")) 
	   throw coreutils::Exception("expected ) in expression.");	      
      } else if(in.ifNextIgnoreCase("SUBSTRING")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for SUBSTRING parameters.");
	 Operand parm1(in, tag);
	 if(!in.ifNext(","))
	   throw coreutils::Exception("Expecting , in SUBSTRING expression.");
	 Operand parm2(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.substring(parm2.string.asInteger());
	 } else if(!in.ifNext(","))
	      throw coreutils::Exception("Expecting , in SUBSTRING expression.");
	 Operand parm3(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.substring(parm2.string.asInteger(), parm3.string.asInteger());
	 } else
	   throw coreutils::Exception("Expecting ) at end of substring expression.");
      } else if(in.ifNextIgnoreCase("LEFT")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for LEFT parameters.");
	 Operand parm1(in, tag);
	 if(!in.ifNext(","))
	   throw coreutils::Exception("Expecting , in LEFT expression.");
	 Operand parm2(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.substring(0, parm2.string.asInteger());
	 } else
	   throw coreutils::Exception("Expecting ) at end of LEFT expression.");	 
      } else if(in.ifNextIgnoreCase("EXPR")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for EXPR parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    Operand op(parm1.string, tag);
	    string = op.string;
	    isNumber = op.isNumber;
	    if(isNumber) {		 
	       doubleValue = op.doubleValue;
	       string = std::format("{:.12f}", doubleValue);
	       string.removeTrailingZeros();	 	       
	    }	    
	    boolean = op.boolean;	    
	 } else
	   throw coreutils::Exception("Expecting ) at end of EXPR expression.");	 	 
      } else if(in.ifNextIgnoreCase("RIGHT")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for RIGHT parameters.");
	 Operand parm1(in, tag);
	 if(!in.ifNext(","))
	   throw coreutils::Exception("Expecting , in RIGHT expression.");
	 Operand parm2(in, tag);
	 if(in.ifNext(")")) {
	    int len = parm1.string.getLength();
	    int size = parm2.string.asInteger();
	    int start = len - size;
	    string = parm1.string.substring(start, size);
	 } else
	   throw coreutils::Exception("Expecting ) at end of RIGHT expression.");	 	 
      } else if(in.ifNextIgnoreCase("TRIM")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for TRIM parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.trim();
	 } else
	   throw coreutils::Exception("Expecting ) at end of TRIM expression.");	 	 	 
      } else if(in.ifNextIgnoreCase("TOUPPER")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for TOUPPER parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.toUpper();
	 } else
	   throw coreutils::Exception("Expecting ) at end of TOUPPER expression.");	 	 	 	 
      } else if(in.ifNextIgnoreCase("TOLOWER")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for TOLOWER parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.toLower();
	 } else
	   throw coreutils::Exception("Expecting ) at end of TOLOWER expression.");	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("REVERSE")) {
	 
      } else if(in.ifNextIgnoreCase("CONCAT")) {
	 
      } else if(in.ifNextIgnoreCase("INTEGER")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for INTEGER parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    string = parm1.string.asInteger();
	 } else
	   throw coreutils::Exception("Expecting ) at end of INTEGER expression.");	 	 	 	 
      } else if(in.ifNextIgnoreCase("ROUND")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for ROUND parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = round(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of ROUND expression.");	 	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("RANDOM")) {
	 unsigned int seed = (unsigned int)clock();
	 doubleValue = (double) rand_r(&seed) / (RAND_MAX + 1.0);
	 isNumber = true;
	 string = std::format("{:.12f}", doubleValue);
	 string.removeTrailingZeros();
      } else if(in.ifNextIgnoreCase("ABS")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for ABS parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = abs(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of ABS expression.");	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("MAX")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for MAX parameters.");
	 Operand *parm = new Operand(in, tag);
	 string = parm->string;
	 doubleValue = parm->doubleValue;
	 isNumber = parm->isNumber;
	 delete parm;
	 while(in.ifNext(",")) {
	    parm = new Operand(in, tag);
	    if(isNumber != parm->isNumber)
	      throw coreutils::Exception("All parameters in MAX expression must be same type.");	      
	    if(isNumber) {
	       if(parm->doubleValue > doubleValue) {
		  doubleValue = parm->doubleValue;
		  string = parm->string;
	       }	       
	    } else {
	       if(parm->string > string) {
		  doubleValue = parm->doubleValue;
		  string = parm->string;		  
	       }	       
	    }
	    delete parm;	    
	 }
	 if(!in.ifNext(")")) {
	   throw coreutils::Exception("Expecting ) at end of MAX expression.");	 	 	 	 	 
	 }
	 string.removeTrailingZeros();
      } else if(in.ifNextIgnoreCase("MIN")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for MIN parameters.");
	 Operand *parm = new Operand(in, tag);
	 string = parm->string;
	 doubleValue = parm->doubleValue;
	 isNumber = parm->isNumber;
	 delete parm;
	 while(in.ifNext(",")) {
	    parm = new Operand(in, tag);
	    if(isNumber != parm->isNumber)
	      throw coreutils::Exception("All parameters in MIN expression must be same type.");	      
	    if(isNumber) {
	       if(parm->doubleValue < doubleValue) {
		  doubleValue = parm->doubleValue;
		  string = parm->string;
	       }	       
	    } else {
	       if(parm->string < string) {
		  doubleValue = parm->doubleValue;
		  string = parm->string;		  
	       }	       
	    }
	    delete parm;	    
	 }
	 if(!in.ifNext(")")) {
	   throw coreutils::Exception("Expecting ) at end of MIN expression.");	 	 	 	 	 
	 }	 	 	 
	 string.removeTrailingZeros();
      } else if(in.ifNextIgnoreCase("POW")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for POW parameters.");
	 Operand parm1(in, tag);
	 if(!in.ifNext(","))
	   throw coreutils::Exception("Expecting , in POW expression.");
	 Operand parm2(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = pow(parm1.doubleValue, parm2.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else 
	   throw coreutils::Exception("Expecting ) at end of substring expression.");	 
      } else if(in.ifNextIgnoreCase("SIN")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for SIN parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = sin(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of SIN expression.");	 	 	 	 
      } else if(in.ifNextIgnoreCase("ASIN")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for ASIN parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = asin(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of ASIN expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("COS")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for COS parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = cos(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of COS expression.");	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("ACOS")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for ACOS parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = acos(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of ACOS expression.");	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("ATAN")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for ATAN parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = atan(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of ATAN expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("SQRT")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for SQRT parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = sqrt(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of SQRT expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("DEG")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for DEG parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = parm1.doubleValue * 180 / 3.14159;
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of DEG expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("RAD")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for RAD parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = parm1.doubleValue * 3.14159 / 180;
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of RAD expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("TAN")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for TAN parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = tan(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of TAN expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("TRUNC")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for TRUNC parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = trunc(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of TRUNC expression.");	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("CEIL")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for CEIL parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = ceil(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of CEIL expression.");	 	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("FLOOR")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for FLOOR parameters.");
	 Operand parm1(in, tag);
	 if(in.ifNext(")")) {
	    doubleValue = floor(parm1.doubleValue);
	    isNumber = true;
	    string = std::format("{:.12f}", doubleValue);
	    string.removeTrailingZeros();	 
	 } else
	   throw coreutils::Exception("Expecting ) at end of FLOOR expression.");	 	 	 	 	 	 	 
      } else if(in.ifNextIgnoreCase("UNIXTIME")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for UNIXTIME.");
	 if(in.ifNext(")")) {
	    auto now = std::chrono::system_clock::now();
	    auto duration_since_epoch = now.time_since_epoch();
	    auto seconds_since_epoch = std::chrono::duration_cast<std::chrono::seconds>(duration_since_epoch);
	    int unixtime = seconds_since_epoch.count();	    
	    string << unixtime;
	 } else
	   throw coreutils::Exception("Expecting ) at end of UNIXTIME.");
      }
      else if(in.ifNextIgnoreCase("DATEDIFF")) {
	 if(!in.ifNext("("))
	   throw coreutils::Exception("Expecting ( for DATEDIFF.");
	 Operand parm1(in, tag);
	 if(!in.ifNext(","))
	   throw coreutils::Exception("Expecting , in DATEDIFF expression.");
	 Operand parm2(in, tag);
	 if(in.ifNext(")")) {

	    // TODO: Implement DATEDIFF.

	 } else
	   throw coreutils::Exception("Expecting ) at end of DATEDIFF expression.");	 	       
      } else if(in.ifNextIgnoreCase("true")) {
	 boolean = true;
	 string = "true";
      } else if(in.ifNextIgnoreCase("false")) {
	 boolean = false;
	 string = "false";
      } else if(in.startsWithNumber()) {
	 doubleValue = in.asDouble();
	 string = std::format("{:.12f}", doubleValue);
	 isNumber = true;
      } else if(in.ifNext("'")) {
	 string = in.getTokenExclude("'");
	 in.ifNext("'");
	 isNumber = false;
      } else
	throw coreutils::Exception("operand is not valid.");
      
      in.skipWhitespace();

      if(!stop)
	parseOperator();

      return;
   }   
      
   int Operand::parseOperator() {
	
      if(in.ifNext("!=") || in.ifNext("<>")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue != op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string != op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }      
      if(in.ifNext("<=")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue <= op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string <= op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }            
      if(in.ifNext(">=")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue >= op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string >= op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }      
      if(in.ifNext("=")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue == op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string == op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }      
      if(in.ifNext("<")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue < op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string < op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }      
      if(in.ifNext(">")) {
	 Operand op(in, tag);
	 if(isNumber && op.isNumber) {
	    if(doubleValue > op.doubleValue) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	      
	 } else if(!isNumber && !op.isNumber) {
	    if(string > op.string) {
	       boolean = true;
	       isNumber = false;
	       string = "true";
	    } else {
	       boolean = false;
	       isNumber = false;
	       string = "false";
	    }	    
	 }	   
      }


      if(in.ifNext("+")) {
	 if(isNumber) {	      
	    Operand op(in, tag);
	    if(op.isNumber) {
	       doubleValue += op.doubleValue;
	       string = std::format("{:.12f}", doubleValue);	       
	       string.removeTrailingZeros();
	    } else
	      throw coreutils::Exception("operand is not a number.");
	 } else
	   throw coreutils::Exception("operand is not a number.");	 
      } else if(in.ifNext("-")) {
	 if(isNumber) {	      
	    Operand op(in, tag);
	    if(op.isNumber) {
	       doubleValue -= op.doubleValue;
	       string = std::format("{:.12f}", doubleValue);	       
	       string.removeTrailingZeros();
	    } else
	      throw coreutils::Exception("operand is not a number.");
	 } else
	   throw coreutils::Exception("operand is not a number.");	 
      } else if(in.ifNext("*")) {
	 if(isNumber) {	      
	    Operand op(in, tag, true);
	    if(op.isNumber) {
	       doubleValue *= op.doubleValue;
	       string = std::format("{:.12f}", doubleValue);	       
	       string.removeTrailingZeros();
	       parseOperator();
	    } else
	      throw coreutils::Exception("operand is not a number.");
	 } else
	   throw coreutils::Exception("operand is not a number.");	 
      } else if(in.ifNext("/")) {
	 if(isNumber) {	      
	    Operand op(in, tag, true);
	    if(op.isNumber) {
	       doubleValue /= op.doubleValue;
	       string = std::format("{:.12f}", doubleValue);	       
	       string.removeTrailingZeros();
	       parseOperator();
	    } else
	      throw coreutils::Exception("operand is not a number.");
	 } else
	   throw coreutils::Exception("operand is not a number.");	 
      } 
      return true;
   }
   
}
