
<?php
/*
* epapi => Eduporta Api - https://eduporta.net/api
 * Version - 1.0.0
 * Licensed under the Taquatech license - http://taquatech.com/license
 * Copyright (c) 2019 Eduporta Dev Team - Taquatech Limited
*/

//epapi class
class epapi{
    //Constructor Error Holder
    var $Error = FALSE;
    var $Server = "";
    var $Config = [];
    var $EpVersion = "";
    var $ErrorRedirectURL = "";
    var $ErrorWriteMemory = [];
    var $WriteMemory = [];
    var $ErrorWriteStatus = FALSE; //default no error occur
    var $LastRequestResult = NULL;
    var $Controls = "";
    var $UseCURLs = true;//the communication  if to use curl or file_get_contents
    //PrintPDF Markup
public static $PrintPDFHTML = "";

  public function __construct($configfile=""){
      //check if config file is set
      if(is_string($configfile) && trim($configfile) == ""){
        $this->Error = ["Code" => -1, "Message"=>"Api Config File Not Set"];
      }else{
        
        if(is_string($configfile)){
             //check if file not exist
          if(file_exists($configfile)){
            //get the config file
          $config = file_get_contents($configfile);
           $config = substr($config,strpos($config,"{")) ;
          //get the settings as an array
          $config = json_decode($config,true);
        }else{
            $this->Error = ["Code" => -2, "Message"=>"Api Config File Not Found - ".$configfile];
            $config = $configfile;
            //exit('not exist');
          }
        }else{
            $config = $configfile;
        }
         
          if(is_array($config)){
              //get the error redirect url
            if(isset($config['ErrorRedirectURL']) && trim($config['ErrorRedirectURL']) != "")$this->ErrorRedirectURL = $config['ErrorRedirectURL'];

            //get the controls markups
            if((isset($config['ControlMarkup']) && trim($config['ControlMarkup']) != "")){
                $ggg = $config['ControlMarkup'];
                if(!file_exists($config['ControlMarkup'])){ //if the control file not exist try check in config directory
                    $controlsarr = explode("/",$config['ControlMarkup']);
                    if(is_string($configfile)){
                        $configsarr = explode("/",$configfile);
                        
                        $configsarr[count($configsarr) - 1] = $controlsarr[count($controlsarr) - 1];
                        $config['ControlMarkup'] = implode("/",$configsarr);
                    }else{ //assume name to be 
                      //just check in the file directory
                      $config['ControlMarkup'] = $controlsarr[count($controlsarr) - 1];
                    }
                    
                }
                
            
               // 
                    //
              // $cntrurl = ($controls != "" && file_exists($controls))?$controls:$config['ControlMarkup'];
              if(file_exists($config['ControlMarkup'])){ 
                $this->Controls = file_get_contents($config['ControlMarkup']);
                
                //set the printer markup
                self::$PrintPDFHTML = $this->Control("Printer");
                
                if(isset($config['Version']) && trim($config['Version']) != "")$this->EpVersion = $config['Version'];
                $protocol = isset($config['Protocol'])?$config['Protocol']:"";
            //check if required settings are done
            if(isset($config['Server']) && trim($config['Server']) != ""){
                
              $this->Server = $config['Server'];
            }else{
                
                $ServerRoot = isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:$_SERVER['SERVER_NAME'];
                if(trim($protocol) == ""){
                    $protocol = !empty($_SERVER['HTTPS'])?'https://' : 'http://';
                }
                
                $this->Server = $protocol.$ServerRoot;
                //$this->Error = ["Code" => -4, "Message"=>"Invalid or No server URL Specified"];
            }

            if(isset($config['UseCURLs']) && strtolower(trim($config['UseCURLs'])) != "true"){
                $this->UseCURLs = false;
            }


            
            $EpVersion = $this->EpVersion == ""?"":$this->EpVersion."/";
            if(!$this->URLExists($this->Server."/".$EpVersion.'epcore/api/engine/request.php')){ //if 
                $this->Error = ["Code" => -4, "Message"=>"Invalid or No server URL Specified, Check your config file (Server and Version)"];
            }

            //set up the communication type
            if(isset($config['ControlMarkup']))
            
            $this->Config = $config;
            
        }else{
            $this->Error = ["Code" => -6, "Message"=>"Invalid Controls File - ".$config['ControlMarkup']];
        }
            }else{
                //no control set
                $this->Error = ["Code" => -5, "Message"=>"Wrong Control Setup"];
            }
   
            
          }else{ //Invalid Json File
            $this->Error = ["Code" => -3, "Message"=>"Invalid Config File Structure (must be a valid json structure) - ".$config];
          }
          
          
      }

      
      
      
  }

  private function Control($CuntrName){
      $markup = "";
      if($CuntrName == "Abstract")return '<_??_>';
      //get the opening tag
      $otag = "<".trim($CuntrName).">";
      $opnin = strpos($this->Controls,$otag);
      
      if($opnin !== FALSE){ //if Control Exist
        
        $ctag = "</".trim($CuntrName).">";
        $mkupStartIndex = $opnin + strlen($otag);
        
         //get the closing tag
         $cpnin = strpos($this->Controls,$ctag,$mkupStartIndex);
         
         if($cpnin !== FALSE){ //if closing exist
              $mkuplen = $cpnin - $mkupStartIndex;
              //return (string)($cpnin - $mkupStartIndex);
               //get the markup
               $markup = substr($this->Controls,$mkupStartIndex,$mkuplen);
         }else{
            $markup = substr($this->Controls,$mkupStartIndex);
         }
      }else{
       
      }
      return $markup;
  }

  //Verify URL
  public function URLExists($url = ""){
     $exist = FALSE;
     if(trim($url) != ""){
        $headers = @get_headers($url);
        if(strpos($headers[0],'404') === false)
        {
          $exist = TRUE;
        }
     }
     return $exist;
  }

  //Error Handler
  //redirect to error Page - Send error code and messge via GET Method
  public function OnErrorRedirect($Evaluatee = NULL,$redirectpath = ""){
    if(!is_null($this->LastRequestResult) && is_null($Evaluatee)){ //if last request exist
        $Evaluatee = $this->LastRequestResult;
    }

    $ErrorObj = NULL;
    if(is_null($Evaluatee)){
        
        $ErrorObj = $this->Error !== FALSE?$this->Error:NULL;
        
    }else{
        if(isset($Evaluatee) && ((is_array($Evaluatee) && isset($Evaluatee["Error"]) && $Evaluatee["Error"] !== FALSE) || (is_object($Evaluatee) && isset($Evaluatee->Error) && $Evaluatee->Error !== FALSE) )){ 
            $ErrorObj =is_array($Evaluatee)?$Evaluatee['Error']:$Evaluatee->Error;
        }
    }
    //var_dump($Evaluatee);
      if(!is_null($ErrorObj)){ //if resource contain error
        $Code = $ErrorObj['Code'];
        $Msg = $ErrorObj['Message'];
        
        if(trim($redirectpath) == ""){
                if(trim($this->ErrorRedirectURL) != ""){
                    $redirectpath =  $this->ErrorRedirectURL; 
                }else{
                    exit(json_encode($ErrorObj));
                }
            }

            //exit($redirectpath);
            //exit($redirectpath);
            header('location:'.$redirectpath.'?Error_Code='.urlencode($Code).'&Error_Message='.urlencode($Msg));
            exit();
      }
     
  }


  public function OnErrorWriteStart($Evaluatee = NULL){
    $ErrorObj = NULL;
    if(!is_null($this->LastRequestResult) && is_null($Evaluatee)){ //if last request exist
        $Evaluatee = $this->LastRequestResult;
    }
    //reset erro status and memory
    //$this->ErrorWriteStatus = FALSE;
    $this->ErrorWriteMemory = [];

    if(is_null($Evaluatee)){
        $ErrorObj = $this->Error !== FALSE?$this->Error:NULL;   
    }else{
        if(isset($Evaluatee) && ((is_array($Evaluatee) && isset($Evaluatee["Error"]) && $Evaluatee["Error"] !== FALSE) || (is_object($Evaluatee) && isset($Evaluatee->Error) && $Evaluatee->Error !== FALSE) )){ 
            $ErrorObj =is_array($Evaluatee)?$Evaluatee['Error']:$Evaluatee->Error;
        }
    }
    //var_dump($Evaluatee);
      if(!is_null($ErrorObj)){ //if resource contain error
        $this->ErrorWriteMemory = $ErrorObj;
      }
      ob_start();
  }

  public function OnErrorWriteEnd(){
      $done = false;
    $errmsg= ob_get_contents();
     ob_clean();
     //exit("ttt".count($this->ErrorWriteMemory));
    if(count($this->ErrorWriteMemory) > 0){
        //check if a class name is set for the error (for special operation, like auto reload)
        if(!isset($this->ErrorWriteMemory['PageClass']))$this->ErrorWriteMemory['PageClass'] = "";
        if(!isset($this->ErrorWriteMemory['ErrorImage']))$this->ErrorWriteMemory['ErrorImage'] = "";
        $errmsg = str_replace(array('{{Error_Code}}','{{Error_Message}}','{{Error_Class}}','{{Error_Image}}'),array($this->ErrorWriteMemory['Code'],$this->ErrorWriteMemory['Message'],$this->ErrorWriteMemory['PageClass'],$this->ErrorWriteMemory['ErrorImage']),$errmsg);
        unset($this->ErrorWriteMemory['Message']);
        unset($this->ErrorWriteMemory['Code']);
        unset($this->ErrorWriteMemory['ErrorImage']);
        $errmsg = str_replace('{{Error_Data}}',json_encode($this->ErrorWriteMemory),$errmsg);

        echo $errmsg;
        $done = true;
    }
    $this->ErrorWriteMemory = [];
    return $done;
  }

  public function OnErrorWriteExit(){
    $rtn = $this->OnErrorWriteEnd();
   
    if($rtn){
        exit();
    }
  }

  private function PerformPost($url,$fields){
    /*   */
     //$etr = $this->UseCURLs?$this->CurlPost($url,$fields):$this->FileGetContentsPost($url,$fields);
     
     $etr = $this->ObPost($url,$fields);
   
     //return $etr;
      
    $data = json_decode($etr,true);
    return is_null($data)?["Error"=>["Message"=>$etr,"Code"=>0]]:$data;
  }

  //making request using ob
  private function ObPost($url,$fields){
      
    ob_start();
    $_POST = array_merge($_POST,$fields);  
       
   // return $url ;       
     
    require $url;
    
    //
    $output =  ob_get_clean();
    //$output = ob_get_clean();
   //if($output === false){
       return $output;
  // }
  // return $output;
  }

  //make request using curls
  private function CurlPost($url,$fields){
    $fields_string = "";
	//url-ify the data for the POST
	foreach($fields as $key=>$value) { $fields_string .= $key.'='.urlencode($value).'&'; }
	
	$fields_string=rtrim($fields_string, '&');
      	//open connection
	$ch = curl_init();
	//echo $ch;
	//print_r($ch);
	//set the url, number of POST vars, POST data
	curl_setopt($ch,CURLOPT_URL, $url);
	curl_setopt($ch,CURLOPT_POST, count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
    //Tell cURL that it should only spend 10 seconds
//trying to connect to the URL in question.
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 8);
//A given cURL operation should only take
//30 seconds max..
curl_setopt($ch, CURLOPT_TIMEOUT, 13);
	//curl_setopt_array($ch, array(CURLOPT_RETURNTRANSFER => 1));
	
	//execute post
	$rst = curl_exec($ch);
	if (curl_error($ch)) {
			$error_msg = curl_error($ch);
			//return ["Error"=>$error_msg];
            return ["Error"=>["Message"=>$error_msg,"Code"=>0]];
	}
    $rst = trim($rst);
	curl_close($ch);
    return $rst;
  }

  private function FileGetContentsPost($url,$fields){
    $postdata = http_build_query(
        $fields
     );
    $opts = array('http' =>
    array(
        'method'  => 'POST',
        'header'  => 'Content-type: application/x-www-form-urlencoded',
        'content' => $postdata
    )
);

$context = stream_context_create($opts);

$etr = file_get_contents($url, false, $context);
return $etr;
  }

  //function to send post request
public function Post($url,$fields){

    $maxretry = 3;
    $rst = ["Error"=>["Message"=>"No request Done","Code"=>0]];
    while($maxretry > 0){
       
        $rst = $this->PerformPost($url,$fields);
        
        if(!isset($rst['Error']))return $rst;
        $maxretry--;
    }
	
    return $rst;
	
	//$rst = rtrim($rst,"</html>");
	
	//$etr = urldecode($rst);
    
    /* foreach($fields as $key=>$value) { $_POST[$key] = $value; }
    
    ob_start();
     include $url;
    $output = ob_get_clean();
    //exit('jj');
    $data = json_decode($output,true); */

     
	
}

public function Request($param = [],$baseurl=""){

    $param = !is_array($param)?["RequestID"=>$param]:$param;
    $EpVersion = $this->EpVersion == ""?"":$this->EpVersion."/";
    $param['Version'] = (!isset($param['Version']) || trim($param['Version']) == "")?"v1":$param['Version'];
    
    //if(isset($param['Param']))$param['Param'] = urlencode($param['Param']);
    
    //include the config to $param
    $param['Config'] = json_encode($this->Config);
    //exit(json_encode($param)."kk");
    
    //exit($param['Param']);
    if($baseurl == ""){
        //$baseurl = rtrim($this->Server,"/")."/".$EpVersion;
        $baseurl = __DIR__.'/../../';

    }
    $param['Base'] = $baseurl;
  
    $this->LastRequestResult = $this->Post($baseurl.'epcore/api/engine/request.php',$param);
    
    //return $baseurl.'epcore/api/engine/request.php';
    return $this->LastRequestResult;
    
}

public function EpMarkup($str = "",$search = array('<#','#>','<[',']>','<%','=>','%>'),$repl = array('<img src="','" />','<span class="appcolor">','</span>','<a target="_new" href="','">','</a>')){
	 
	 return str_replace($search,$repl,$str);
}



//write the $data in the $key placeholder withen $markup
private function Writer($markup,$key,$data){
    return str_replace("{{".$key."}}",$this->EpMarkup($data),$markup);
    //echo $errmsg;
    //$done = true;
}

private function GetIterateRegion($markup,$key){
    $rg = "";
   //get the startpos
   $startpos = strpos($markup,"{{".$key.':');
   $endpos = -1;
   if($startpos !== FALSE){ //if start exist
    $keylen = strlen("{{".$key.':');
      //get the endpos
      $endpos = strpos($markup,":".$key.'}}',$startpos + $keylen);
      if($endpos !== FALSE){
          //get the iteratable region
          
          $rgstartpos = $startpos + $keylen;
          $rg = substr($markup,$rgstartpos,$endpos-$rgstartpos);
      }
   }
   return [$rg,$startpos,$endpos];
}

private function ProcessArrayWriter($dataArr,$IterRegion,$parentkey){
    //run the loop
    $foemMarkup = "";
    $editIterRegion = $IterRegion;
    
    $numericvaluearrcont = [];
    //print_r(htmlentities($foemMarkup));
    $cnt = 0;
    foreach($dataArr as $itrkey => $itrval){
        $cnt++;
        if(is_numeric($itrkey)){
            if(is_array($itrval)){
                
               $foemMarkup .= str_replace('{{$}}',$cnt."",$this->ProcessArrayWriter($itrval,$editIterRegion,$parentkey));
            }else{ //if ordinary value
                if($editIterRegion == "{{".$parentkey."}}"){
                    $numericvaluearrcont[] = $itrval;
                }else{
                    $foemMarkup .= str_replace('{{$}}',$cnt."",$this->Writer($editIterRegion,$parentkey,$itrval));
                    //exit(htmlentities(str_replace("{{".$parentkey."}}",$this->EpMarkup($itrval),$editIterRegion)));
                }
               // $foemMarkup = str_replace('{{$}}',$cnt."",$foemMarkup);
            }
        }else{
            $foemMarkup = trim($foemMarkup) == ""?$IterRegion:$foemMarkup;
            if(is_bool($itrval)){
                if($itrval){
                    $foemMarkup = $this->ArrayWriter($foemMarkup,"?".$itrkey,[1]);
                    $foemMarkup = $this->ArrayWriter($foemMarkup,"?!".$itrkey,[]);
                }else{
                    $foemMarkup = $this->ArrayWriter($foemMarkup,"?".$itrkey,[]);
                    $foemMarkup = $this->ArrayWriter($foemMarkup,"?!".$itrkey,[1]);
                }
                
            }else{
               $foemMarkup = is_array($itrval)?$this->ArrayWriter($foemMarkup,$itrkey,$itrval):$this->Writer($foemMarkup,$itrkey,$itrval);  
            }
        }
       
    }
   
    if(count($numericvaluearrcont) > 0)$foemMarkup = $this->Writer(trim($foemMarkup) == ""?$IterRegion:$foemMarkup,$parentkey,implode(",",$numericvaluearrcont));
    
    return $foemMarkup;
}

//get the $key loop section(basemarkup) from markup, loop tru the $dataArr and call Writer or ArrayWriter as required
private function ArrayWriter($markup,$key,$dataArr=[]){
   //perform direct writting first
   //var_dump($dataArr);
       //$data = implode(",",$dataArr);
       //$markup = $this->Writer($markup,$key,$data);

       //get the array markup (iteration region)
       
       $IterRegionDet = $this->GetIterateRegion($markup,$key);
       $NotIterRegionDet = $this->GetIterateRegion($markup,"!".$key);
      
       $IterRegion = $IterRegionDet[0];
       $NotIterRegion = $NotIterRegionDet[0];
       $foemMarkup = ""; $noregion = false;
       if(trim($IterRegion) == ""){
         //look 
         $IterRegion = "{{".$key."}}";
         $noregion = true;
       }
       if(trim($IterRegion) != ""){
           do{
               $foemMarkup = $this->ProcessArrayWriter($dataArr,$IterRegion,$key);
               
              //update in main markup
              if($noregion == false){
                  $markup = str_replace("{{".$key.":".$IterRegion.":".$key."}}",$foemMarkup,$markup);
              $IterRegionDet = $this->GetIterateRegion($markup,$key);
              $IterRegion = $IterRegionDet[0];
              }else{
                $markup = str_replace("{{".$key."}}",$foemMarkup,$markup);
                $IterRegion = ""; 
              }
              
           }while(trim($IterRegion) != "");
       }else{ //check for direct 
        //still thinking on a beter way
        $markup = $this->Writer($markup,$key,"");
       }

       //Not Markup
       if(trim($NotIterRegion) != ""){
        do{
            $markup = str_replace("{{!".$key.":".$NotIterRegion.":!".$key."}}",count($dataArr) == 0?$NotIterRegion:"",$markup);
            $NotIterRegionDet = $this->GetIterateRegion($markup,$key,"!");
            $NotIterRegion = $IterRegionDet[0];
        }while(trim($NotIterRegion) != "");
       }
   
       return $markup;
}

public function WriteStart($lookupPlaceholders=[],$Evaluatee=NULL){
    //if lookplace holder not exist in data, set it to empty string
    //if lookup place holder not set (or empty array) use all $data elements
    //$ErrorObj = NULL;
    if(!is_null($this->LastRequestResult) && is_null($Evaluatee)){ //if last request exist
        $Evaluatee = $this->LastRequestResult;
    }
    

    if(is_null($Evaluatee)){
        $Evaluatee = new $this;   
    }
    $Evaluatee = is_array($Evaluatee)?$Evaluatee:(array)$Evaluatee;
    $this->WriteMemory = [$lookupPlaceholders,$Evaluatee];
    //var_dump($Evaluatee);
      
      ob_start();
   }

   public function WriteEnd($echo=true){
     $markup = ob_get_contents();
     ob_clean();
     //check if Write Resource exist in memory
     if(count($this->WriteMemory) == 2){
        $Evaluatee = $this->WriteMemory[1];
        $lookupPlaceholders = $this->WriteMemory[0];
        if(is_string($lookupPlaceholders))$lookupPlaceholders = [$lookupPlaceholders];
        if(is_object($lookupPlaceholders))$lookupPlaceholders = (array)$lookupPlaceholders;
        if(!is_array($lookupPlaceholders))$lookupPlaceholders = [];
        $keyfoundinEvaluatee = false;
        if(count($lookupPlaceholders) < 1){ //if lookup content exist
            arsort($Evaluatee);
            $lookupPlaceholders = array_keys($Evaluatee);
           
        }
        //var_dump($lookupPlaceholders);
        //exit();

        
          foreach($lookupPlaceholders as $key=>$val){
              //check if numeric key
              if(is_numeric($key)){ //i.e the $val is the key in evaluatee
                  //get write value from $Evaluatee
                  if(isset($Evaluatee[$val])){
                    $keyfoundinEvaluatee = true; //at least one lookup placehoder exist in Evaluatee
                    $whattowrite = $Evaluatee[$val];
                    
                    //update markup as requied
                    //$val represent the placeholder key
                    $markup = is_array($whattowrite)?$this->ArrayWriter($markup,$val,$whattowrite):$this->Writer($markup,$val,$whattowrite);
                  }else{//if data doesnot exist set placeholder to nothing
                    $markup = $this->Writer($markup,$val,"");
                  }

              }
          }
        
        if($keyfoundinEvaluatee){ //if supplied keys atleast one is found
           if($echo){
               echo $markup;
            }else{
                return $markup;
            }
        }
     }
   }

   //send the #PrintPDF #Markup to client
  public function PrintPDF(){
    
    $printerColor = 'w3-red';
    $printerTextColor = 'w3-text-red';
      if(isset($this->Config['aim-printer']) && $this->Config['aim-printer']['aim-printer-color']){
      $printerColor =  $this->Config['aim-printer']['aim-printer-color'];
      }
      if(isset($this->Config['aim-printer']) && $this->Config['aim-printer']['aim-printer-text-color']){
        $printerTextColor =  $this->Config['aim-printer']['aim-printer-text-color'];
        }
      //get all setup paper types
      $printerPaperType =  $this->Config['aim-printer']['aim-printer-paper-size'];
      $options = "";
      foreach($printerPaperType as $key => $val){
       $options .= '<option value="'.$key.'">'.$key.'</option>';
      }

     // echo self::$Config['aim-printer']['aim-printer-paper-size'];
      
      //$rtn = array("Markup"=>str_replace("-red","-".$printerColor,str_replace("{{PaperType}}",$options,self::$PrintPDFHTML)));
      //echo "hshdh}";
     // echo json_encode($rtn);
     return str_replace(array("{{aim-printer-color}}","{{aim-printer-text-color}}","{{PaperType}}"),array($printerColor,$printerTextColor,$options),self::$PrintPDFHTML);
     //self::$PrintPDFHTML;
  }

  //Bulider param
   var $Open = ["CheckBoxes"=>false,"GroupBox"=>false,"Column"=>false,"Page"=>false];
   var $ControlMarkup = [];
   var $Order = [];
  /*var $ControlMarkup = ["Page"=>['
  <div class="bounceInRight animate-normal">
  <div class="menu-bx-cont-title appcolor">{{title}}</div>
         <div class="menu-bx-cont-descr">{{data}}</div>
         <div class="tcolline-line-outer-h" style="position:relative;min-width:100%;margin-bottom:10px"><div class="tcolline-line-inner-h" style="width: 70%;
  left: 10%;"></div></div>
         <div class="w3-row">
          <form name="payreq" id="{{id}}" action="javascript:void(0)" onsubmit="{{action}}" >
      ','</form>
      </div>
      </div>'],
    "Column"=>['
    <div class="w3-col m6">','</div>'],
   "GroupBox"=>['<div class ="bbwa-groupbox" id="">','</div>'],
"ComboBox"=>['<div class="custom-select bbwa-textbox">
<i class="bbwa-textbox-logo {{logo}}"></i>
<select name="{{id}}" onchange="{{action}}" id="{{id}}">{{data}}</select>
</div>',''],
"TextBox"=>['<div class="bbwa-textbox">
<i class="bbwa-textbox-logo {{logo}}"></i><input name="{{id}}" id="{{id}}" placeholder="{{title}}" class="bbwa-textbox-input gen-text-shadow" value="{{data}}" readonly />
</div>'],
"CheckBoxes"=>['<div class="bbwa-checkbox-group">
<div class="w3-row">','</div></div>'],
"CheckBox"=>['<div class="w3-col" style="{{style}}">
<div class="bbwa-checkbox">
<input type="radio" id="{{id}}" onchange="" name="{{id}}" checked />
 <div class="bbwa-checkbox-logo"><i class="fas fa-check"></i></div><div class="bbwa-checkbox-text w3-center"><i class="bbwa-textbox-logo {{logo}} w3-medium" style="vertical-align:middle"></i> <span style="vertical-align:middle">{{data}}</span></div>
</div>
 </div>']]; */

var $currCheckboxes = ["Length"=>1,"Type"=>"H"];
var $allattr = [
    "all"=>["id"=>"","style"=>"","action"=>"","logo"=>"","title"=>"","attr"=>"","data"=>"","class"=>"","name"=>""],
    "Passport" => ["minsize"=>"0","maxsize"=>"0","accept"=>"","default"=>"images/bbwa/logo.png","required"=>"","type"=>""],
    "FileUpload" => ["minsize"=>"0","maxsize"=>"0","accept"=>"","multiple"=>"true","required"=>""],
    "CheckBox" => ["marker"=>"","value"=>"","istyle"=>""],
    "Filler" => ["container"=>""],
    "Column" => ["size"=>"m6"],
    "TabButton" => ["size"=>"s6"],
    "TabAction" => ["size"=>"s6"],
    "Button" => ["timeout"=>"","type"=>""],
    "AppButton" => ["type"=>""],
    "BoxButton" => ["marker"=>""],
    "Browser" => ["onload"=>""],
    "PaymentLabel" => ["status"=>"NotPaid","level"=>"","semester"=>"","amount"=>"","policy"=>""],
    "Page" => ["banner"=>"","type"=>"","fclass"=>"","request"=>["pre"=>"","post"=>""],"cache"=>"none","clear"=>[],"indicator"=>""],
    "Link"=>["target"=>"_self"],
    "TextBox"=>["iclass"=>"","type"=>"text","min"=>"","max"=>"","step"=>"","oninput"=>"","istyle"=>""],
    "ComboBox"=>["drop"=>"down"],
    "Card"=>["image"=>"","time"=>""],
    "ListBox"=>["ulclass"=>""],
    "Spreadsheet"=>["column"=>"auto","row-gap"=>"5px","col-gap"=>"5px"],

];



  public function BuildForm($obj = [], $ret = false,$parentcontr=[]){
      $formmk = "";
      
    foreach($obj as $contr){
        if(!isset($contr['control']) || $contr['control'] == "Comment"  || isset($contr['comment']))continue; //if invalid control or comment
        $markup = $this->Control($contr['control']);
        //Wrapper
        if(isset($contr['wrapper'])){
            if(is_string($contr['wrapper'])){ //if iterator
              //convert to wrapper array
              $contr['wrapper'] = ["{{".$contr['wrapper'].":",":".$contr['wrapper']."}}"];
            }
            if(is_array($contr['wrapper']) && count($contr['wrapper']) > 1){
                $formmk .= $contr['wrapper'][0];
            }
            
        }
        //List Box
        if(isset($parentcontr['control']) && $parentcontr['control'] == "ListBox"){
            $contr['class'] = isset($contr['class'])?$contr['class']:"";
            $contr['id'] = isset($contr['id'])?$contr['id']:mt_rand(20000,999999);
            $formmk .= '<li id="listbox_li_'.$contr['id'].'"  class="listbox_li_'.$contr['class'].'">';
            if($contr['control'] == "CheckBox"){
                $markup = str_replace('type="radio"','type="checkbox"',$markup);
            }
        }

        if((isset($contr['control']) && $contr['control'] == "ListBox") && isset($contr['filter']) && $contr['filter'] == true ){
            $contr['class'] .= ' filterable';
            if(!isset($contr['content']))$contr['content']=[];
            array_unshift($contr['content'],json_decode('{
                "control": "TextBox",
                "title": "Search",
                "data": "",
                "id": "PayReg",
                "logo": "mbri-search",
                "iclass": "",
                "attr":"autofocus",
                "class":"fadeInUp animated",
                "oninput":"epapi.FilterListBox(this)",
                "style":""
            }',true));
        }

        if($markup != ""){
            $markuparr = explode("<_??_>",$markup);//for container controls
            if(count($markuparr) > 1){ //if it has opening and closing tag
                //register it in Open Array
                if(!isset($this->Open[$contr['control']]))$this->Open[$contr['control']] = false;
            }
            $curmarkup = $markuparr[0];
            if(isset($this->Open[$contr['control']]) && $this->Open[$contr['control']] !== false && isset($markuparr[1])){ //if aready open close it
                $formmk .= $markuparr[1];
            }else{ //if not open add it to list
               $this->Order[] = $contr['control'];
            }

//process special data
if(($contr['control'] == "Button" || $contr['control'] == "AppButton") && (isset($contr['action']) && trim($contr['action']) != "")){
    $contr['type'] = "button";
}

 //process special data
 if($contr['control'] == "BoxButton" && (!isset($contr['title']) || trim($contr['title']) == "")){
    if(isset($contr['data']))$contr['title'] = $contr['data'];
    $contr['data'] = "";
}

if($contr['control'] == "Card" && (!isset($contr['image']) || trim($contr['image']) == "")){
    $contr['image'] = $this->Config['Core']."images/General/image.png";
}

            //process special data
            if($contr['control'] == "ComboBox" && isset($contr['data'])){
                $contr['data'] = $this->ComboData($contr['data']);
                if(isset($contr['title']))$contr['data'] = '<option value="0">'.$contr['title'].'</option>'.$contr['data'];
            }

            if($contr['control'] == "CheckBoxes"){
                $this->currCheckboxes["Type"] = isset($contr['type'])?$contr['type']:"H";
                $this->currCheckboxes["Length"] = isset($contr['data'])?(int)$contr['data']:1;
            }

            
            if($contr['control'] == "CheckBox"){
                /* if(isset($contr['style'])){
                   $contr['style'] = $contr['style'].'width:100%'; 
                }else{ */
                    $contr['style'] = 'width:100%;';
                // }
                
                //$currCheckboxes["Type"] = isset($contr['type'])?$contr['type']:"H";
                if(isset($this->currCheckboxes["Length"]) && (int)$this->currCheckboxes["Length"] > 0 && $this->currCheckboxes["Type"] == "H"){
                   $w = 100/(int)$this->currCheckboxes["Length"];
                   /* if(isset($contr['style'])){
                   $contr['style'] = $contr['style'].';width:'.$w.'%;';
                   }else{ */
                    $contr['style'] = 'width:'.$w.'%;';
                //    }
                }else if($this->currCheckboxes["Type"] != "H"){
                $formmk .= '</div><div class="w3-row">';
            }
           
            }

            //if the control is filler and its container is popup then hide it
            if($contr['control'] == "Filler" && strtolower(trim($contr['container'])) == "popup"){
                $contr['class'] = "w3-hide ".$contr['control'];
            }

            if($contr['control'] == "Page"){
                if(isset($contr['banner']) && trim($contr['banner']) != ""){
                   $contr['type'] = "banner";
                $contr['banner'] = $this->Config['Core'].$contr['banner'];  
                }

                if(isset($contr['clear'])){
                    if(is_array($contr['clear']) && count($contr['clear']) > 0){
                        $contr['clear'] = implode(",",$contr['clear']);
                    }else if(is_string($contr['clear']) && trim($contr['clear']) == "all"){
                        $contr['clear'] = "all";
                    }else{
                        $contr['clear'] = "";
                    }
                    
                }else{
                    $contr['clear'] = "";
                }
               
            }

            //Manage FileUpload and Passport required attr
            if($contr['control'] == "FileUpload" || $contr['control'] == "Passport"){
                //$contr['type'] = isset($contr['veiwonly']) && $contr['veiwonly'] == "true"?"":"";
            }

            //Manage textbox
            if($contr['control'] == "TextBox" && isset($contr['split'])){
                $seperator = isset($contr['split']['by']) && $contr['split']['by'] != ''?$contr['split']['by']:'-';
                $digits = isset($contr['split']['unit']) && (int)$contr['split']['unit'] > 0?(int)$contr['split']['unit']:4;
                $len = isset($contr['split']['length']) && (int)$contr['split']['length'] > 0?(int)$contr['split']['length']:0;
                $contr['oninput'] = isset($contr['oninput'])?"Control.TextBox.AddSeperator(this,'$seperator',$digits,$len);".$contr['oninput']:"Control.TextBox.AddSeperator(this,'$seperator',$digits,$len);";
            }

            //Action Shorthand
            if(isset($contr['action'])){
//action special character
$ac = $contr['action'];
if((is_string($ac) && $ac != "") || is_array($ac)){
    $prefix = is_string($ac)?substr($ac,0,2):'';
    if($prefix == "=>" || is_array($ac)){ //SaveLoadNext Operation
        $dataop = is_string($ac)?substr($ac,2):$ac;
        if(is_string($dataop) && trim($dataop) == ""){
            $dataop = "{}";
        }else{
           $dataobj = is_string($dataop)?json_decode($dataop,true):$dataop;
           //$submitRID = $dataobj['SubmitRID'];
          $submitRID = "{{SubmitRID}}";
           if(isset($dataobj['SubmitRID'])){
             $submitRID = $dataobj['SubmitRID'];
            //$submitRID = 'ddd';
            unset($dataobj['SubmitRID']);
           }

           $ApplyID = "{{ApplyID}}";
           if(isset($dataobj['ApplyID'])){
            $ApplyID = $dataobj['ApplyID'];
            unset($dataobj['ApplyID']);
           }
           
           $NextPageNum = "{{NextPageNum}}";
           if(isset($dataobj['NextPageNum'])){
            $NextPageNum = $dataobj['NextPageNum'];
            unset($dataobj['NextPageNum']);
           }

           $ApplyGroupID = "{{ApplyGroupID}}";
           if(isset($dataobj['ApplyGroupID'])){
            $ApplyGroupID = $dataobj['ApplyGroupID'];
            unset($dataobj['ApplyGroupID']);
           }

           $PageObj = "this";
           if(isset($dataobj['PageID'])){
            $PageObj = "_('".$dataobj['PageID']."')";
            unset($dataobj['PageID']);
           }

           $runjs = "";
           if(isset($dataobj['Run'])){
            $runjs = ";".$dataobj['Run'];
            unset($dataobj['Run']);
           }
          // $NextPageNum = isset($dataobj['NextPageNum'])?$dataobj['NextPageNum']:"{{NextPageNum}}";
          if(!is_string($dataop))$dataop = $this->toJSObject($dataop);
        }
        
     $contr['action'] = "Application.SaveLoadNextPage($PageObj,'$submitRID',$ApplyID,$NextPageNum,".$dataop.",'',$ApplyGroupID)$runjs";
    }elseif($prefix == "=^"){ //Popup
        $dataop = substr($ac,2);
        $contr['action'] = "Control.Popup.Show('$dataop')";
    }
}
            }
            //Action Shorthand Ends
            //if control specific attr exist marge with main
    $attrset = isset($this->allattr[$contr['control']])?array_merge($this->allattr[$contr['control']],$this->allattr['all']):$this->allattr['all'];

   

    if(isset($parentcontr['control']) && $parentcontr['control'] == "Filler" && trim($attrset['action'])==""){ //if parent control is a filler reset the action to fill
        $attrset['action'] = "Control.Filler.Fill(this,'".$parentcontr['id']."')";
    }
    if($contr['control'] == "DynamicBox"){
        $contr['id'] = isset($contr['id'])?$contr['id']:"";
        if(isset($contr['attr'])){
            $contr['attr'] .= ' data-id="'.$contr['id'].'"';
        }else{
            $contr['attr'] = ' data-id="'.$contr['id'].'"';
        }
        $contr['id'] = "";
    }
    //if control parent element is a dynamic box
    if(isset($parentcontr['control']) && $parentcontr['control'] == "DynamicBox"){
        if(isset($attrset['iclass'])){
            if(isset($contr['iclass'])){
               $contr['iclass'] .= " DynamicBoxItem"; 
            }else{
                $contr['iclass'] = " DynamicBoxItem";
            }
            
        }else{
            $contr['class'] .= " DynamicBoxItem";
        }
        $contr['id'] = isset($contr['id'])?$contr['id']:"";
        if(isset($contr['attr'])){
            $contr['attr'] .= ' data-id="'.$contr['id'].'"';
        }else{
            $contr['attr'] = ' data-id="'.$contr['id'].'"';
        }
        
        $contr['id'] = "";
    }
    
    foreach($attrset as $attr=>$val){
        //make sure id is set
        if($attr == "id" && trim($val) == ""){
            if($contr['control'] != "DynamicBox")$val = $contr['control']."_".time()."_".mt_rand(10000,99999);
        }
        // if($attr ==  "content")continue;
        if(isset($contr[$attr])) $val = $contr[$attr];
if(!is_string($val))continue;
            $curmarkup = str_replace('{{'.$attr.'}}',nl2br($val),$curmarkup);
        }
            
            $formmk .= $curmarkup;
            //set the control to open
            if(isset($this->Open[$contr['control']])){
                //checking conditional content
                if(isset($contr['content']) && count($contr['content']) > 0){
                    if($this->isAssoc($contr['content'])){
                       /*  if(isset($contr['content']['condition']) && filter_var($contr['content']['condition'],FILTER_VALIDATE_BOOLEAN) && isset($contr['content']['true'])){
                            $contr['content'] = $contr['content']['true'];
                        }else if(isset($contr['content']['condition']) && !filter_var($contr['content']['condition'],FILTER_VALIDATE_BOOLEAN) && isset($contr['content']['false'])){
                            $contr['content']['false'][0]['data'] = str_replace(":","-",$contr['content']['condition']);
                            $contr['content'] = $contr['content']['false'];
                        }else{
                            $contr['content'] = [];
                        } */
                        if(isset($contr['content']['condition'])){
                            if(!isset($contr['content']['true']))$contr['content']['true']=[];
                            if(!isset($contr['content']['false']))$contr['content']['false']=[];
                            if(is_bool($contr['content']['condition'])){ //static boolean
                                $contr['content'] = ($contr['content']['condition'])?$contr['content']['true']:$contr['content']['false'];
                            }else{
                                $cond = trim($contr['content']['condition']."");
                                if(substr($cond,0,2) == "{{" && substr($cond,strlen($cond) - 2,2) == "}}"){ //dynamic content
                                    $newContent = [];
                                    if(count($contr['content']['true'])> 0){
                                        $newContent[] =  [
                                            "wrapper"=>"?".ltrim(rtrim($cond,"}}"),"{{"),
                                            "control"=>"Abstract",
                                            "content"=>$contr['content']['true']
                                            ];
                                    }
                                    if(count($contr['content']['false'])> 0){
                                        $newContent[] =[
                                            "wrapper"=>"?!".ltrim(rtrim($cond,"}}"),"{{"),
                                            "control"=>"Abstract",
                                            "content"=>$contr['content']['false']
                                            ];
                                    }
                                    $contr['content'] = $newContent;
                                   /*  $contr['content'] = [
                                        [
                                            "wrapper"=>"?".ltrim(rtrim($cond,"}}"),"{{"),
                                            "control"=>"Abstract",
                                            "content"=>$contr['content']['true']
                                            ],
                                            [
                                                "wrapper"=>"?!".ltrim(rtrim($cond,"}}"),"{{"),
                                                "control"=>"Abstract",
                                                "content"=>$contr['content']['false']
                                            ]
                                    ]; */
                                }else{
                                    $contr['content'] = filter_var($cond,FILTER_VALIDATE_BOOLEAN)?$contr['content']['true']:$contr['content']['false'];
                                }
                            } 
                                
                        }else{
                            $contr['content'] = [];
                        }
                    }
                }



                //check if the content exist
if(isset($contr['content']) && count($contr['content']) > 0){
    //remove the content and send the atrr as parent element
    $content = $contr['content'];
    unset($contr['content']);
    $formmk .= $this->BuildForm($content,true,$contr);
}
$formmk .= $markuparr[1];
                //$this->Open[$contr['control']] = $markuparr[1];
            }
        }
        if(isset($parentcontr['control']) && $parentcontr['control'] == "ListBox"){
            $formmk .= "</li>";
        }
        //Wrapper
        if(isset($contr['wrapper']) && is_array($contr['wrapper']) && count($contr['wrapper']) > 1){
            $formmk .= $contr['wrapper'][1];
        }
    }

    /* foreach($this->Open as $ContrName=>$OStatus){
        if($OStatus !== false){
            $formmk .= $OStatus;
            $this->Open[$ContrName] = false; 
        }
    } */
    //close page
    /* if(isset($this->$Open['Page']) && $this->$Open['Page'] == true){
        $formmk .= $this->ControlMarkup['Page'][1];
        $this->$Open['Page'] = false;
    } */
    //$formmk = str_replace("<{{??}}>","",$formmk);
    if($ret){
        return $formmk;
    }else{
        echo $formmk;
    }
   

  }

  private function toJSObject($phparr=[]){
      if(count($phparr) < 1)return '{}';
      $jsobj = [];
      foreach($phparr as $k=>$v){
          $fv = '';
          if(is_string($v) || is_numeric($v)){
             $fv = is_numeric($v)?$v:"'$v'"; 
          }else if(is_array($v)){
            if(isset($v['datatype'])){
                $v['value'] = isset($v['value'])?$v['value']:'';
                switch ($v['datatype']){
                    case "string":
                        $fv = !is_array($v['value'])?"'".(string)$v['value']."'":"'".implode(", ",$v['value'])."'";
                        break;
                    case "integer":
                        $fv = intval($v['value']);
                        break;
                    case "float":
                        $fv = floatval($v['value']);
                        break;
                    case "boolean":
                        $fv = filter_var($v['value'],FILTER_VALIDATE_BOOLEAN)?'true':'false';
                        break;
                    case "object":
                        $fv = $this->toJSObject(is_array($v['value'])?$v['value']:[$v['value']]);
                        break;
                    case "script":
                        $fv = $v['value'];
                        break;
                    case "control":
                        $id = isset($v['value']['id'])?"'".$v['value']['id']."'":'this';
                            $attr = isset($v['value']['attr'])?$v['value']['attr']:'value';
                            $fv = "_($id).$attr";
                        break;
                    default:
                    $fv = is_array($v['value'])?$this->toJSObject($v['value']):"'".$v['value']."'";  
                }
            }else{
                $fv = $this->toJSObject($v);
            }
          }else{
              $fv = $v;
          }

          
          
          $jsobj[] = "'$k':$fv";
      }
      return "{".implode(",",$jsobj)."}";
  }

  public function isAssoc(array $arr)
  {
      if (array() === $arr) return false;
      return array_keys($arr) !== range(0, count($arr) - 1);
  }

  public function ComboData($data = []){
      $nwdata = "";
     if(is_array($data)){
         if(isset($data['requestId'])){
             if(isset($data['value']) && isset($data['text'])){
                $nwdata = '{{'.$data['requestId'].':<option value="{{'.$data['value'].'}}">{{'.$data['text'].'}}</option>:'.$data['requestId'].'}}';
             }
         }else{
            foreach($data as $key=>$indata){
             $val = ""; $txt = "";
             if(is_array($indata)){
                if(isset($indata['value'])){
                    $val = $indata['value'];
                }else if(isset($indata[0])){
                    $val = $indata[0];
                }

                if(isset($indata['text'])){
                    $txt = $indata['text'];
                }else if(isset($indata[1])){
                    $txt = $indata[1];
                }else if(isset($indata[0])){
                    $txt = $indata[0];
                }
             }else{
                $val = $key;
                $txt = $indata;
             }
            

            
            $nwdata .= '<option value="'.$val.'">'.$txt.'</option>';
         } 
         }
         
     }
     return $nwdata;
  }

  



  
}

//if request pdf printer markup
if(isset($_POST['PDFMarkup']) && $_POST['PDFMarkup'] == "true"){
    
    $config = json_decode(urldecode($_POST['Config']),true);
    //exit($config['SubDir']);
  $pdfepapi = new epapi($config);
  //exit(json_encode($pdfepapi->Error));
  exit($pdfepapi->PrintPDF());
}



?>