<?php
//ini_set('display_errors', 1);
include_once dirname(__FILE__) . '/Report.class.php';
include_once dirname(__FILE__) . "/../../../../include/function.php";
include_once dirname(__FILE__) . '/Cache.class.php';
include_once dirname(__FILE__) . '/util/ArrayOps.class.php';

include_once dirname(__FILE__) . '/ReportLog.class.php';
include_once dirname(__FILE__) . "/util/excel/Excel.class.php";
include_once dirname(__FILE__) . "/../../../../library/excelFileGenerator.php";
require_once dirname(__FILE__) . '/../../../../library/php2Excel.php' ;
require_once dirname(__FILE__) . '/../../../../library/FileCache.class.php' ;

require_once dirname(__FILE__) .'/Authentication.class.php';
include_once __DIR__ . '/../../../../library/Email.class.php';
/**
 * Description of ReportRequestHandler
 *
 * @author deepakmittal
 */
class ReportRequestHandler {
    
    private $input;
    private $cache;
    private $logger;
    private $fieldSeparate;
    private $keyFields;
    private $data;
    private $meta;
    
    function __construct($input) {
        ReportLog::getInstance()->logDebug("ENTERING REPORT REQUEST HANDLER ..");
        $this->input = $input;
	//$this->input["field"][]='impressions_compare';
	//print_r($this->input); echo "<br>=================";
        //ini_set('display_errors',1); 
        //error_reporting(E_ALL);
        $this->logger = ReportLog::getInstance();
        $this->cache = Cache::getInstance(300);
    }
    
    private function generateCacheKey(){
        $key = array(
            "field"     => isset($this->input['field']) ? $this->input['field']:"",
            "filter"    => isset($this->input['filter']) ? $this->input['filter']:"",
            "template"  => isset($this->input['template']) ? $this->input['template']:"",
            "source"    => isset($this->input['source']) ? $this->input['source']:"",
            "mode"      => isset($this->input['mode']) ? $this->input['mode']:""
        ); 
       //var_dump($key);
        //echo "--" . md5(json_encode($key)) . "--";
        return md5(json_encode($key));
    }
    private function getKeyFields($fields) {
  $keyArray = array();    
	foreach($fields as $key=>$value) 
		if($value['type']=='key')
			$keyArray[]=$key;
	return $keyArray;
    
    }
    private function arrangeRow($row,$fields){
      $temp = array();
      foreach($fields as $field){
        $temp[$field] = $row[$field];
      }
      return $temp;
    }
    private function getExcelData($data,$meta) {
	$excel = new ExcelFileGenerator();
	$this->generateXlsHead($excel, $meta);
                $this->keyFields=$this->getKeyFields($meta['field']);
                $this->fieldSeparate=$fieldSeparate=$this->keyFields[0];
                $fieldSeparateLabel=$meta['field'][$fieldSeparate]['label'];
                $topRow=$this->getTopRow($meta,$fieldSeparate);
                $total_row=$this->getTotalRow($meta,$fieldSeparate);
                $previousFieldValue='';
                $firstRow=true;

                if(is_array($data)&&count($data)>0) {
                        usort($data,array($this,'data_cmp'));
                foreach($data as $row) {
                        $row = $this->arrangeRow($row, array_keys($meta['field']));
                        $currentFieldValue=$row[$fieldSeparate];
                        unset($row[$fieldSeparate]);
                        //$row=array_merge(array("extra"=>''),$row);
			if($currentFieldValue==$previousFieldValue) {
				$excel->updateOffSet(1);
				$excel->addRow($row);
				$excel->updateOffSet(0);
                                $total_row=$this->addToTotal($total_row,$row);
                                if($firstRow)
                                $previousFieldValue=$currentFieldValue;
                        }
			else {
				$excel->updateOffSet(0);
				if(!$firstRow) {
					$total_row=$this->getRoundTotalRow($total_row,$meta['field']);
					$excel->addRow(array_merge(array("extra"=>"Total"),$total_row),false);
				}	
                                else $firstRow=false;
				
				$excel->addBlank();
				$excel->addRow(array($fieldSeparateLabel." - ",$currentFieldValue));
				$excel->updateOffSet(1);
                                $excel->addRow($topRow,false);
				$total_row=$this->reset($total_row);
                                $excel->addRow($row);
				$excel->updateOffSet(0);
                                $total_row=$this->addToTotal($total_row,$row);

                                $previousFieldValue=$currentFieldValue;

                        }

                }
			if(!$firstRow) {
				$excel->updateOffSet(0);
				$total_row=$this->getRoundTotalRow($total_row,$meta['field']);
				$excel->addRow(array_merge(array("extra"=>"Total"),$total_row),false);
		}
                }
	
		return $excel;
    }

    private function getExcelDataSummary($data,$meta) {
		$excel = new ExcelFileGenerator();
		$this->generateXlsHead($excel, $meta);
		$topRow=$this->getTopRow($meta,$fieldSeparate);
		$total_row=$this->getTotalRow($meta,$fieldSeparate);
		
		foreach($total_row as $key=>$value)
			if(isset($meta['aggregation'][$key]) )
				$total_row[$key]=$meta['aggregation'][$key];
		if(is_array($data) && count($data)>0) {
			$excel->updateOffSet(1);
			$excel->addRow($topRow,false);
      //var_dump($row);
			foreach($data as $key=>$row){
        $row = $this->arrangeRow($row, array_keys($meta['field']));
        $excel->addRow($row);
      }
			$excel->updateOffSet(0);
			$excel->addRow(array_merge(array("extra"=>"Total"),$total_row),false);

		}

		return $excel;
    }
    
    public function processOldFormats($data,$meta){
      if( isset($this->input["format"]) && $this->input["format"] == 'json_v0'){
        $out = array(
          'reports'=> $data  
        );
        return $out;
      }
      return false;
    }
    public function processXlsFormat($data, $meta){
	    if( isset($this->input["format"]) && $this->input["format"] == 'excel'){
		    if(strtolower($this->input['field'][0])=='summary')
			    $this->getExcelDataSummary($data,$meta)->download("Report".date("Y-m-d H:i:s"));
		    else $this->getExcelData($data,$meta)->download("Report".date("Y-m-d H:i:s"));
         die;
        }
        if( isset($this->input["format"]) && $this->input["format"] == 'csv'){
          $this->getCSV($data, $meta);
        }
        
    }
    public function getCSV($data, $meta, $fileLocation=""){
      $labels = array();
      $head= $this->getReportHeaders($meta);
      foreach($meta['field'] as $columnKey => $field){
        $name = isset($field['name']) ? $field['name'] : $columnKey;
        $label = isset($field['label']) ? $field['label'] : $columnKey;
        $labels[$name] = $label;
      }
      $excel = new PHP2Excel();
      $prefix= "portal";
      $excel->export2CSV($prefix, $labels, $data, array_values($head), $fileLocation);
      if($fileLocation)
        return true;
      die;
    }
    /**
     * Bad Bad function just using it to clean email parameters, Don't use it, better remove it
     * @param type $key
     * @return type
     */
    private function getInpFilterVal($key){
      $out =  isset($this->input[$key]) ? $this->input[$key]:$this->input['filter'][$key];
      if(is_array($out)){
        $out = implode (" ", $out);
      }
      return $out;
    } 
    public function processEmailFormat($data, $meta,$addTotal=true){
	    if( isset($this->input["format"]) && $this->input["format"] == 'email'){
              
            	$email = $this->getInpFilterVal('email');
                $sub = $this->getInpFilterVal('email_subject');
                $content = $this->getInpFilterVal('email_message');
                $from = $this->getInpFilterVal('email_creator');
                $Cc = $this->getInpFilterVal('Cc');
                $Bcc = $this->getInpFilterVal('Bcc');
    if(strtolower($this->input['field'][0])=='summary')
			$this->getExcelDataSummary($data,$meta)->mailFile('report'.date("Y-m-d H:i:s"),array('recipient'=>$email,'from'=>$from,'Cc'=>$Cc,'Bcc'=>$Bcc,'subject'=>$sub,'message'=>$content,'fromName'=>'Vdopia Report Service'));
		else $this->getExcelData($data,$meta)->mailFile('report'.date("Y-m-d H:i:s"),array('recipient'=>$email,'from'=>$from,'Cc'=>$Cc,'Bcc'=>$Bcc,'subject'=>$sub,'message'=>$content,'fromName'=>'Vdopia Report Service'));
        }
    }
      

    private function data_cmp($a,$b) {
      foreach($this->keyFields as $key=>$value) {
		if(is_numeric($a[$value])) {
			if($a[$value]==$b[$value])
				continue;
			return $a[$value]-$b[$value];
		}
		else {
			$cmp=strcmp(strtolower($a[$value]),strtolower($b[$value]));
			if(!$cmp)
				continue;
			return $cmp;
		}
	    }
	    return 0;

    	//return strcmp($a[$this->fieldSeparate], $b[$this->fieldSeparate]);
    	
    }
    private function reset($total_row) {
    	foreach($total_row as $key=>$value)
    		if(is_numeric($value))
    			$total_row[$key]=0;
    		return $total_row;
    	
    }
    
    private function addToTotal($total_row,$row) {
    	foreach($row as $key=>$value)
			if(isset($total_row[$key])&&is_numeric($total_row[$key]))   
				$total_row[$key]+=$value;
			return $total_row;
    }
    private function getTopRow($meta,$fieldSeparate) {
    	
    	foreach($meta['field'] as $key=>$metas)
    		if($key!=$fieldSeparate)
    		$newMeta[]=$metas['label'];
    	
    	return $newMeta;
    	    	
    }

    private function getTotalRow($meta,$fieldSeparate) {
	$total_row=array();
	$index=0;
	foreach($meta['field'] as $key=>$field) {
		switch($field['aggregation']) {
			case 'sum':
				$total_row[$key]=0;
				break;
			default:
				$total_row[$key]=''; 
		}
	}
	unset($total_row[$fieldSeparate]);
	return $total_row;
    }

    /**
     * 
     * @param type $filters
     * @return type
     * @todo this function should read from yml and should not have hard coded 'Start Date' / 'End Date' etc
     */
    private function getAboutHeader($filters) {
	   $about = array(
            'Start Date'=>$this->clean($filters, "start_date"),
            'End Date'  =>$this->clean($filters, "end_date"),
            'Channel'   =>$this->clean($filters,"channel"),
            'Country'   =>$this->clean($filters,"country"),
            'State'     =>$this->clean($filters,"state"),
            'Campaign'  =>$this->clean($filters,"campaign"),
            'Ad Format' =>$this->clean($filters,"adtype")
    );
            $countries=getCountryList();
	   $states=getStateList($about['Country']);
            foreach($about as $key=>$value) {
                    switch($key) {
		    case 'Country':
			    	if($value!='' && !is_array($value))
                                $value=$countries[$value];
                                break;
			case 'State':
				if(is_array($value)) {
                                foreach($value as $val)
                                        $state_name[]=$states[$val];
				$value=$state_name;
				}
				else if($value!='')
					$value=$states[$value];
				break;
			case 'Channel':
				if($value!='') {
				$channel_name=getChannelsNames($value);
				foreach($channel_name as $name)
					$channels_names[]=$name['name'];
				$value=$channels_names;
				}
				break;			
			case 'Campaign':
				if($value!='') {
				$campaign_name=getCampaignsNames($value);
				foreach($campaign_name as $name)
					$campaigns_names[]=$name['name'];
				$value=$campaigns_names;
				}
				break;	
		    }
              $about[$key]=($value!='') ? (is_array($value) ? implode(",",$value) : $value) : 'All';
	    }
      //var_dump($about);exit;
		return $about;
    }

    private function generateXlsHead($excel,$meta){
	    $filters = $meta['request']['filter'];
	    $about=$this->getAboutHeader($filters);   
	$excel->addAssociativeHeader(array(
		"Report Type" => $meta['request']['label'],
		"Report Generated On" => date("Y-m-d H:i")." GMT",
	));
        //$excel->addAssoArrayVertically(array(
          //      "Report Type" => $this->getTemplateLabel($meta['template'][0])
	    //    ));

        //$excel->addBlankLines(2);
	$excel->addAssociativeHeader($about);

	//$excel->addAssoArrayVertically($about);
	//$excel->addBlankLines(3);
	$excel->addBlank();$excel->addBlank();$excel->addBlank();
	
    }
    private function getRoundTotalRow($total_row,$metaField) {
	    foreach($total_row as $key=>$value) {
		if($metaField[$key]['round']>0)
			$total_row[$key]=roundNumber($value,$metaField[$key]['round']);
		return $total_row;

	    }

 
    }
    private function roundUpData($data,$meta) {
		
    	foreach($data as $k=>$val) {
		foreach($val as $key=>$value)
			if(isset($meta['field'][$key]) && $meta['field'][$key]['round']>0)
				$data[$k][$key]=roundNumber($value,$meta['field'][$key]['round']);
	}		

	return $data;
    }
    private function roundUpTotal($meta) {
	    foreach($meta['aggregation'] as $key=>$value)
		    if($meta['field'][$key]['round']>0)
			    $meta['aggregation'][$key]=roundNumber($value,$meta['field'][$key]['round']);
	    return $meta;
    }



    private function clean($arr,$key){
        return isset($arr[$key]) ? $arr[$key] : "";
    }
    private function filterOutExtraFields(&$data, $meta){
	//echo "<pre>"; print_r($data); print_r($meta);
        $columns = array_keys($meta['field']);
        //var_dump($columns);
        foreach($data as $i => &$row){
            //var_dump($row);
            // ommiting not needed data
            foreach($row as $column => $value){
                //var_dump($column);
                if(!in_array($column, $columns)){
                  unset($row[$column]);
                }
                $column = NULL;
                $value = NULL;
            }
            //arranging data in meta's order
            foreach($meta['field'] as $field => $fieldDetails){
		//echo "<br>" . $field . " ROW : " ;
              if(isset($fieldDetails['name']))
                $field = $fieldDetails['name'];
              if(isset($row[$field])){
                $temp = $row[$field];
		unset($row[$field]);
                $row[$field]=$temp;
              }  
            }
            //var_dump($row); die;
        }
    } 
    private function authenticate(){
      $auth = new Authentication($this->input);
      $auth->authenticate();
    }

    public function process(){ 
      try{
        $this->authenticate();
        $this->cachedCSVCheck();
        $data = $this->getData();
echo "55555"; exit;
        $meta = $this->getMeta();
        $data=$this->roundUpData($data,$meta);
//echo "55555"; exit;
        $meta=$this->roundUpTotal($meta);
        $meta['count'] = sizeof($data);
        $oldFormat = $this->processOldFormats($data, $meta);
        if($oldFormat){
          return $oldFormat;
        }
        $this->filterOutExtraFields($data, $meta);
        $this->cleanData($data, $meta);
        $this->sort($data);
        $this->processXlsFormat($data, $meta);
        $this->processEmailFormat($data, $meta);
        if(isset($this->input["page"]) || isset($this->input["page_begin"])){
            $data = $this->resolvePagination($data, $meta);
        }
        $outArray = array(
            'status' => 'success',
            'data' => $data,
            'meta' => $meta
            );
      }catch(Exception $e){
        $this->unsetCache();
        $outArray = array(
            'status' => 'failed',
            'reason' => $e->getMessage(),
            'data' => array(),
            'meta' => array()
        );
      }
      return $outArray;
    }
    
    private function createCachedCSVObj(){
      $config = ReportConfigs::getInstance($this->input['mode'])->getMainConfig();
      return new \Vdopia\Common\Library\FileCache(
              $config['cached_csv']['folder_path'], 
              $config['cached_csv']['web_path'], 
              "" 
            );
    }
    /**
     *This function is forwarded to FileCache so that it can create csv 
     * @author deepakmittal
     */ 
    private function getCreateCsvFunction(){
      $reportHandler = $this;
      return function($filePath) use($reportHandler){
                    $data = $reportHandler->getData();
                    $meta = $reportHandler->getMeta();
                    $reportHandler->getCSV($data, $meta, $filePath);
                    return true;
                  };  
    }
    private function createKeyFromInput(){
      $filters = $this->input['filter'];
      if($filters)ksort($filters);
      $fields = $this->input['field'];
      if($fields) sort($fields);
      $key = "";
      $key .= isset($this->input['template']) ? $this->input['template'][0] : "";
      $key .= "_" . isset($this->input['field']) ? implode("-",$fields) : "";
      $key .= "_" . md5(isset($this->input['filter']) ? implode("-",  array_values($filters)  ) : "" );
      $key = preg_replace("/[\s,]+/", "", $key);
      return $key;
    }
    private function cachedCSVCheck(){
      if(in_array($this->input["format"], array( 'cached_csv','cache_csv' ) )){
         $csvCache = $this->createCachedCSVObj(); 
         if($this->input["format"] === 'cache_csv'){
           $csvCache->disableCache();
         }
         $key = $this->createKeyFromInput() . ".csv" ;//* @todo Make key more discriptive
         $createCsvFunction = $this->getCreateCsvFunction();
         if(!$this->input["email"]){         
            $csvCache->forward($key,$createCsvFunction);
         }
         else{
            $attachment = $csvCache->get($key, $createCsvFunction);
            $this->sendReportAttachment($this->input["email"], $attachment);
         }
      }
    }
    
    /**
     * 
     * @param String $email
     * @param FileContent $attachment
     */
    private function sendReportAttachment($email,$attachment){
      $mail = new \Vdopia\Common\Library\Email(
                    $email, 
                    'portal@vdopia.com', 
                    'Vdopia Report Service', 
                    "", 
                    "", 
                    "Portal Report", 
                    "PFA, report you have requested", 
                    $attachment, 
                    "portal_report.csv"
            );
      try{
      $mail->send();
      }catch(Exception $e){
        throw $e;
      }
    }
    
    private function cleanData(&$data, $meta){
      foreach($data as &$row){
        foreach($row as $column=>$cell){
         $fieldMeta = ( isset($meta['field']) && isset($meta['field'][$column]) ) ?
                           $meta['field'][$column] : array();
        }
        $row[$column] = $this->cleanCell($cell, $fieldMeta);
      }
    }
    private function cleanCell($val,$fieldMeta){
      $val=preg_replace("/\t/", ' ', $val);
      $val=preg_replace("/\n/", ' ', $val);
      $val = trim($val);
      return $val;
      
    }
    public function getMeta(){
      return $this->meta;
        /*
        $report = new Report($this->input);
        $meta = $report->getMeta($data);
        $meta['template'] = isset($this->input['template']) ? $this->input['template']:array();
        return $meta;
         * 
         */
    }
    
    public function getData(){
      $report = new Report($this->input);
      //@todo comment this to enable caching
      $this->data = $report->getData();
//print_r($this->data);
echo "77777"; exit;
      $this->meta = $report->getMeta();
      return $this->data; 
	 $this->logger->logDebug("FETCHING DATA FROM REQUEST HANDLER");
        if(isset($this->input['fresh']) && $this->input['fresh']==="true"){
            $this->logger->logDebug("FETCHING FRESH DATA");
            return $this->fetchAndCacheData();
        }
        $cachedData = $this->cache->get($this->generateCacheKey());
        if($cachedData && $cachedData!=="IN_PROCESS"){//resetting the cache to maintain cache while navigating pages
            $this->logger->logDebug("FOUND DATA IN CACHE");
            $this->cache->set($this->generateCacheKey(), $cachedData);
            $this->data = $cachedData['data'];
            $this->meta = $cachedData['meta'];
            return $this->data;
        }
        $this->logger->logDebug("DATA NOT IN CACHE, HITTING DB ..");
        return $this->fetchAndCacheData();
    }
    private function sort(&$data){
      if(isset($this->input['sort_on'])){
        ArrayOps::sortTableData(
                $data,
                $this->input['sort_on'], 
                isset($this->input['sort_desc']) && in_array($this->input['sort_desc'], array('y','yes','true','1')),
                (
                    isset($this->meta['field'][$this->input['sort_on']])
                    && isset($this->meta['field'][$this->input['sort_on']]['datatype'])    
                ) ? $this->meta['field'][$this->input['sort_on']]['datatype'] : ''
          );
      }
    }
    private function resolvePagination($data, &$meta){
        if(isset($this->input["page"])) $pageNum = $this->input["page"];
        else if(isset($this->input["page_begin"])) $offset = $this->input["page_begin"];
        $pageSize = isset($this->input["page_size"]) ? $this->input["page_size"]: "50";
        $meta['page'] = $pageSize;
        $meta['page_count'] = ceil(sizeof($data)/$pageSize);
        return ArrayOps::slicePage($data, $pageNum, $pageSize,$offset);
    }
    private function fetchAndCacheData(){
        $report = new Report($this->input);
        $this->data= array();
        $this->meta = array();
        if($this->waitIfInProcess()){
            $result = $this->cache->get($this->generateCacheKey());
            $this->data = $result['data'];
            $this->meta = $result['meta'];
      	}
        $this->logger->logDebug($this->generateCacheKey() . "CACHE SET TO IN_PROCESS");
        $this->cache->set($this->generateCacheKey(), "IN_PROCESS");
	//die;
        if(! $this->data){
            $this->data = $report->getData();
            $this->meta = $report->getMeta();
        }
        if($this->data){
            $this->cache->set($this->generateCacheKey(), array(
                'data' => $this->data,
                'meta' => $this->meta    
                    ));
        }else{
            $this->cache->set($this->generateCacheKey(), "");
        }
        $this->logger->logDebug($this->generateCacheKey() . "CACHE SET DATA");
        $this->logger->logDebug($this->generateCacheKey() . " IN_PROCESS FINISHED");
        return $this->data;
    }
    
    /**
     * 
     * @return boolean TRUE is process did waited
     */
    private function waitIfInProcess(){
        $isWaiting = false;
        $this->logger->logDebug($this->generateCacheKey() . " WAITING STARTS");
        while($this->cache->get($this->generateCacheKey()) === "IN_PROCESS"){
            $this->logger->logDebug("Waiting");
            $isWaiting = true;
            sleep(2);
        }
        $this->logger->logDebug($this->generateCacheKey() . " WAITING ENDS");
        return $isWaiting;
    }
    
    private function unsetCache(){
      $this->cache->set($this->generateCacheKey(), "");
    }
    
    private function getReportHeaders($meta){
      $head = array();
      
      if(isset($meta['request']['label'])){
        $head['report_type'] = array(
            'label' => 'Report Type',
            'value' => $meta['request']['label']
        );
      }
      $head['generated_on'] = array(
            'label' => 'Report Generated On',
            'value' => date("Y-m-d H:i")." GMT"
      );
      $head[] = "";
      //var_dump($meta); die;
      if(isset($meta["request"]['timezone'])){
          $head['timezone'] = array(
            'label' => 'Timezone',
            'value' => $meta["request"]['timezone']
      ); 
      }
      foreach ($meta['filter'] as $filter => $filterDetails) {
        if(isset($filterDetails['type']) && $filterDetails['type'] == 'primary'){
          $value = is_array($filterDetails['value']) ?
              implode(",",$filterDetails['value'])
              :$filterDetails['value'];
          $value = "$value";
	  if(DateTime::createFromFormat('Y-m-d', $value) !== FALSE){
		  $value="'$value'";
	  }

          $head[$filter] = array(
              'label' => $filterDetails['label'],
              'value' => $value
          );
        }
        
      }
      return $head;
    }
}

