<?php
//vim: ts=2 sw=2

  class DataFormatter {
    function __construct() {
      $this->fp = null;
      $this->file_path = null;
      $this->save_to_local = false;
      $this->zip_threshold = 100; # when file's size > 100KB, gzip it 
      $this->row_count = 0;
      $this->append_row_count = false;
    }

    function __destruct() {
      if (!is_null($this->fp)) {
        fclose($this->fp);
        $this->fp = null;
      }
    }

    public function isEmpty() {
      return $this->row_count == 0;
    }
    
    public function saveToLocal($file_name) {
      $this->save_to_local = $file_name;
    }
    
    public function setZipThreshold($zip_threshold) {
      $this->zip_threshold = $zip_threshold;
    }
    
    public function setAppendRowCount($append) {
      $this->append_row_count = $append;
    }

    public function writeHeader($download_file_name) {}

    public function writeRow($row) {
      $this->row_count += 1;
    }

    public function writeFooter() {}

    public function done() {
      if (empty($this->save_to_local) || is_null($this->file_path))
        return;

      $temp_file_path = $this->file_path;
      if (filesize($this->file_path) >= $this->zip_threshold*1024) {
        fclose($this->fp); 
        $this->fp = null;
        $cmd = "/bin/gzip -f " . $this->file_path;
        exec(escapeshellcmd($cmd))
        $temp_file_path = $this->file_path . '.gz';
      }
      
      if ($this->append_row_count)
        //NOTE: the column titles will be the first, here we need the number
        //of data rows, so need to substract the 1 row
        echo $temp_file_path . ',' . max(0, $this->row_count-1);
      else
        echo $temp_file_path;
      return;
    }
     
    public function localFilePath($extname) {
      $parts = explode(DIRECTORY_SEPARATOR, __FILE__);
      array_pop($parts);
      array_pop($parts);
      array_push($parts, "user_data");
      array_push($parts, "exported_data");
      $dir = implode(DIRECTORY_SEPARATOR, $parts);
      if (!file_exists($dir))
          mkdir($dir, 0777, true);

      $path = $dir . DIRECTORY_SEPARATOR . $this->save_to_local . $extname;
      return $path;
    }

    protected function genDownloadFileName($basename, $extname) {
      if ($basename == "1" || $basename == "true") {
        $now = new DateTime("now", new DateTimeZone("UTC"));
        return "data_" . $now->format("YmdHi") . $extname;
      } else {
        return $basename . $extname;
      }
    }
  };

  class HtmlDataFormatter extends DataFormatter {
    function __construct() {
      parent::__construct();

      $this->is_first = true;
    }

    public function writeHeader($download_file_name) {
      $path = null;
      if (!empty($this->save_to_local))
      {
        $this->file_path = $this->localFilePath(".html");
        $path = $this->file_path;
      }
      else
      {
        if (!empty($download_file_name))
        {
          header('Content-Type: text/html');
          $filename = $this->genDownloadFileName($download_file_name, '.html');
          header('Content-Disposition: attachment; filename="' . $filename . '"')
        }

        $path = 'php://output';
      }

      $this->fp = fopen($path, 'w');
      fwrite($this->fp, '<table style="border-spacing: 5px;">');
    }

    public function writeRow($row) {
      parent::writeRow($row);

      fwrite($this->fp, '<tr>');
      $col_tag = 'td';
      if ($this->is_first)  {
        $col_tag = 'th';
        $this->is_first = false;
      }

      foreach($row as $col) {
        fwrite($this->fp, '<' . $col_tag . '>' . $col . '</' . $col_tag . '>');
      }
      fwrite($this->fp, '</tr>');
    }

    public function writeFooter() {
      fwrite($this->fp, '</table>');
    }
  };

  class JsonDataFormatter extends DataFormatter {
    function __construct() {
      parent::__construct();

      $this->is_first = true;
      $this->row_written = false;
    }

    public function writeHeader($download_file_name) {
      $path = null;
      if (!empty($this->save_to_local))
      {
        $this->file_path = $this->localFilePath(".json");
        $path = $this->file_path;
      }
      else
      {
        if (!empty($download_file_name)) {
          header('Content-Type: application/json');
          $filename = $this->genDownloadFileName($download_file_name, '.json');
          header('Content-Disposition: attachment; filename="' . $filename . '"')
        }

        $path = 'php://output';
      }

      $this->fp = fopen($path, 'w');
      fwrite($this->fp, '{"data":{');
    }
    
    public function writeRow($row) {
      parent::writeRow($row);

      if ($this->is_first)
        fwrite($this->fp, '"columns":');
      
      $values = array();
      foreach($row as $col) {
        // if (is_string($col))
        //     $col = addcslashes($col,"\\\"\n\r");
        $values[] = '"' . $col . '"';
      }

      if ($this->row_written)
        fwrite($this->fp, ',');

      fwrite($this->fp, '['.implode(',', $values).']');
      if ($this->is_first) {
        fwrite($this->fp, ',"rows":[');
        $this->is_first = false;
      } else {
        $this->row_written = true;
      }
    }
    
    public function writeFooter() {
      if (!$this->isEmpty())
        fwrite($this->fp, ']');
      fwrite($this->fp, '}}');
    }
  };

  class CsvDataFormatter extends DataFormatter {
    public function writeHeader($download_file_name) {
      $path = null;
      if (!empty($this->save_to_local))
      {
        $this->file_path = $this->localFilePath(".csv");
        $path = $this->file_path;
      }
      else
      {
        if (!empty($download_file_name)) {
          header('Content-Type: text/csv');
          $filename = $this->genDownloadFileName($download_file_name, '.csv');
          header('Content-Disposition: attachment; filename="' . $filename . '"')
        }
        $path = 'php://output';
      }
      $this->fp = fopen($path, 'w');
    }

    public function writeRow($row) {
      parent::writeRow($row);
      fputcsv($this->fp, $row);
    }
  };
  
  function build_formatter($format='html') {
    if (empty($format))
      $format = 'html';

    if ($format == 'csv')
      return new CsvDataFormatter();
    elseif ($format == 'html')
      return new HtmlDataFormatter();
    elseif ($format == 'json')
      return new JsonDataFormatter();
    else
      return null;
  };

?>