<?php




class utility{
	public static function getMaxOfArray($ArrayToCheck){
		$maxValue = 0;
		
		foreach($ArrayToCheck as $temp){
			if(is_array($temp)){
				$maxValue = max($maxValue, utility::getMaxOfArray($temp));
			}
			else{
				$maxValue = max($maxValue, $temp);
			}
		}
		return $maxValue;
	}
}


class gChart{
	
	private $chart;
	
	
	private $baseUrl = "chart.apis.google.com/chart?";
	
	
	protected $values = Array();
	
	
	private $width;
	private function setWidth($width) {
		$this->width = $width;
	}
	public function getWidth() {
		return($this->width);
	}
	
	
	private $height;
	private function setHeight($height) {
		$this->height = $height;
	}
	public function getHeight() {
		return($this->height);
	}	
	
	
	private $precision = 2;
	public function setPrecision($precision) {
		$this->precision = $precision;
	} 
	public function getPrecision() {
		return $this->precision;
	}
	
	
	private $dataCount;
	public function setDataCount($dataCount){
		if (!isset($this->dataCount))
			$this->dataCount = $dataCount;
	}
	public function getDataCount(){
		return $this->dataCount;
	}
	
	
	private $dataEncodingType = 't';
	public function setEncodingType($newEncodeType) {
		$this->dataEncodingType = $newEncodeType;
	}
	public function getEncodingType() {
		return ($this->dataEncodingType);
	}
	protected function encodeData($data, $separator, $encodigData = ''){
		if ($encodigData == 's') {
			$data = $this->simpleEncodeData($data);
			$separator = '';
		} else if ($encodigData == 'e') {
			$data = $this->extendedEncodeData($data);
			$separator = '';
		} else if ($encodigData == 't') {
			$data = $this->textEncodeData($data);
		}
		$retStr = $this->separateData($data, $separator, "|");
		$retStr = trim($retStr, "|");
		return $retStr;                                
	}
	protected function separateData($data, $separator, $datasetSeparator){
		$retStr = "";
		if(!is_array($data))
			return $data;
		foreach($data as $currValue){
			if(is_array($currValue))
				$retStr .= $this->separateData($currValue, $separator, $datasetSeparator);
			else
				$retStr .= $currValue.$separator;
		}
		$retStr = trim($retStr, $separator);
		$retStr .= $datasetSeparator;
		return $retStr;
	}
	
	
	public function addDataSet($data){
		array_push($this->values, $data);
	}
	
	public function addHiddenDataSet($hiddenData){
		$this->setDataCount(count($this->values));
		array_push($this->values, $hiddenData);
	}
	
	public function clearDataSets(){
		$this->values = Array();
	}
	
	private function textEncodeData($data) {
		if (isset($this->chart['chds'])) {
			return $data;
		}
		$encodedData = array();
		$max = utility::getMaxOfArray($data);
		if ($max > 100) {
			$rate = $max / 100;
			foreach ($data as $array) {
				if (is_array($array)) {
					$encodedData2 = array();
					foreach ($array as $elem) {
						array_push($encodedData2, round($elem / $rate, $this->getPrecision()));
					}
					array_push($encodedData, $encodedData2);
				} else {
					array_push($encodedData, round($array / $rate, $this->getPrecision()));
				}
			}
		} else {
			$encodedData = $data;
		}
		return $encodedData;
	}
	
	private function simpleEncodeData($data){
		$encode_string='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
		$max = utility::getMaxOfArray($data);
		$encodedData = array();
		if ($max > 61){
			$rate = $max / 61.0;
			foreach($data as $array) {
				if (is_array($array)) {
					$encodedData2 = array();
					foreach ($array as $elem){
						$index = (int)$elem/$rate;
						array_push($encodedData2, $encode_string[$index]);
					}
					array_push($encodedData, $encodedData2);
				} else {
					$index = (int)$array/$rate;
					array_push($encodedData, $encode_string[$index]);
				}
			}
		} else {
			foreach($data as $array) {
				if (is_array($array)) {
					$encodedData2 = array();
					foreach ($array as $elem){
						array_push($encodedData2, $encode_string[$elem]);
					}
					array_push($encodedData, $encodedData2);
				} else {
					array_push($encodedData, $encode_string[$array]);
				}
			}
		}
		return $encodedData;
	}
	
	private function extendedEncodeData($data){
		$encode_string='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
		$max = utility::getMaxOfArray($data);
		$encodedData = array();
		if ($max > 4095){
			$rate = $max/4095.0;
			foreach ($data as $array)
				if (is_array($array)) {
					$encodedData2 = array();
					foreach ($array as $elem){
						$toEncode=(int)$elem/$rate;
						$s='';
						for ($i=0;$i<2;++$i){
							$m = $toEncode%64;
							$toEncode/=64;
							$s = $encode_string[$m].$s;
						}
						array_push($encodedData2, $s);
					}
					array_push($encodedData, $encodedData2);
				} else {
					$toEncode=(int)$array/$rate;
					$s='';
					for ($i=0;$i<2;++$i){
						$m = $toEncode%64;
						$toEncode/=64;
						$s = $encode_string[$m].$s;
					}
					array_push($encodedData, $s);	
				}
		} else {
			foreach ($data as $array)
				if (is_array($array)) {
					foreach ($array as $elem){
						$s='';
						$toEncode = $elem;
						for ($i=0; $i<2; ++$i){
							$m = $toEncode%64;
							$toEncode /= 64;
							$s = $encode_string[$m].$s;
						}
						array_push($encodedData2, $s);
					}
					array_push($encodedData, $encodedData2);
				} else {
					$s='';
					$toEncode = $array;
					for ($i=0; $i<2; ++$i){
						$m = $toEncode%64;
						$toEncode /= 64;
						$s = $encode_string[$m].$s;
					}
					array_push($encodedData, $s);
				}
		}
		return $encodedData;
	}
	
	
	public function getApplicableLabels($labels){
		return array_splice($labels, 0, count($this->values));
	}
	
	
	private $serverNum;
	
	public function setServerNumber($newServerNum){
		$this->serverNum = $newServerNum % 10;
	}
	
	public function getServerNumber(){
		return ($this->serverNum);
	}
	
	
	public function setProperty($key, $value, $append = false, $dataSetSeparator = '|') {
		if ($append && isset($this->chart[$key])) {
			$this -> chart[$key] = $this -> chart[$key].$dataSetSeparator.$value;
		} else {
			$this -> chart[$key] = $value;
		}
	}
	
	public function getProperty($key) {
		if (isset($this->chart[$key]))
			return ($this->chart[$key]);
	}
	
	public function setDimensions($width, $height) {
		$this -> setWidth($width);
		$this -> setHeight($height);
		if ($width && $height) {
			$this -> setProperty('chs', $width.'x'.$height);
		}
		else if ($height) 
			$this -> setProperty('chs', $height);
	}
	
	public function setColors($colors) {
		$this -> setProperty('chco', $this->encodeData($this->getApplicableLabels($colors),","));
	}
	
	public function setLegend($labels) {
		$this -> setProperty('chdl', urlencode($this->encodeData($this->getApplicableLabels($labels),"|")));
	}
	
	public function setLegendPosition($position, $order = null) {
		if (isset($order)) {
			$this -> setProperty('chdlp', $position.'|'.$order);
		} else {
			$this -> setProperty('chdlp', $position);
		}
	}
	
	public function setTitle($title) {
		$title = str_replace("\r\n", "|", $title);
		$title = str_replace(" ", "+", $title);
		$this -> setProperty('chtt', $title);
	}
	
	public function setTitleOptions($color, $size) {
		$this -> setProperty('chts', $color.','.$size);
	}
	
	public function setChartMargins($chartMargins, $legendMargins = array()) {
		$this -> setProperty('chma', $this -> encodeData($chartMargins, ','));
		if (!empty($legendMargins))
			$this -> setProperty('chma', $this -> encodeData($legendMargins, ','), true);
	}
	
	public function setVisibleAxes($visibleAxes) {
		$this->setProperty('chxt', $this->encodeData($visibleAxes,','));
	}
	
	public function addAxisRange($axisIndex, $startVal, $endVal, $step = null) {
		if (is_null($step))
			$axisRange = array($axisIndex, $startVal, $endVal);
		else
			$axisRange = array($axisIndex, $startVal, $endVal, $step);
		$this -> setProperty('chxr', $this->encodeData($axisRange, ',') , true);
	}
	
	public function addAxisLabel($axisIndex, $axisLabel) {
		$this->setProperty('chxl', $this->encodeData(array_merge(array($axisIndex.':'), $axisLabel), '|'), true);
	}
	
	public function addAxisLabelPositions($axisIndex, $labelPositions) {
		$this->setProperty('chxp', $axisIndex.','.$this->encodeData($labelPositions, ','), true);
	}
	
	public function setDataRange($startVal, $endVal) {
		$this->setProperty('chds', $startVal.','.$endVal);
	}
	
	public function addBackgroundFill($fillType, $color) {
		$this->setProperty('chf', $this->encodeData(array($fillType, 's', $color), ','), true);
	}
	
	public function setGradientFill($fillType, $fillAngle, $colors) {
		$this->setProperty('chf', $this->encodeData(array_merge(array($fillType, 'lg', $fillAngle), $colors), ','));
	}
	
	public function setStripFill($fillType, $fillAngle, $colors) {
		$this->setProperty('chf', $this->encodeData(array_merge(array($fillType, 'ls', $fillAngle), $colors), ','));
	}
	
	public function addLineFill($where, $color, $startLineIndex, $endLineIndex) {
		$this->setProperty('chm', $this->encodeData(array($where, $color, $startLineIndex, $endLineIndex, 0),','), true);
	}
	
	public function setGridLines($xAxisStepSize, $yAxisStepSize, $dashLength = 4, $spaceLength = 1, $xOffset = 0, $yOffset = 0) {
		$this->setProperty('chg', $this->encodeData(array($xAxisStepSize, $yAxisStepSize, $dashLength, $spaceLength, $xOffset, $yOffset), ','));
	}
	
	public function addValueMarkers() {
		$args = func_get_args();
		$this->setProperty('chm', $this->encodeData($args, ','), true);
	}
	
	
	protected function setDataSetString() {
		if(!empty($this->values)) {
			$this -> setProperty('chd', $this->getEncodingType().$this->getDataCount().':'.$this->encodeData($this->values,',',$this->getEncodingType()));
		}
	}
	
	
	public function getUrl(){
        $fullUrl = "http://";
        if(isset($this->serverNum))
            $fullUrl .= $this->getServerNumber().".";
        $fullUrl .= $this->baseUrl;
        $this -> setDataSetString();
        $parms = array();
        foreach ($this -> chart as $key => $value) {
            $parms[] = $key.'='.$value;
        }
        return $fullUrl.implode('&amp;', $parms);
}

	
	public function getImgCode(){
		$code = '<img src="';
		$code .= $this->getUrl().'"';
		$code .= 'alt="gChartPhp Chart" width='.$this->width.' height='.$this->height.'>';
		print($code);
	}
	
	public function renderImage($post = false) {
		header('Content-type: image/png');
		if ($post) {
			$this->setDataSetString();
			$url = 'http://chart.apis.google.com/chart?chid=' . md5(uniqid(rand(), true));
			$context = stream_context_create(
				array('http' => array(
 					'method' => 'POST',
					'header' => 'Content-type: application/x-www-form-urlencoded' . "\r\n",
					'content' => urldecode(http_build_query($this->chart, '', '&')))));
				fpassthru(fopen($url, 'r', false, $context));
		} else {
			$url = str_replace('&amp;', '&', $this->getUrl());
			readfile($url);
		}
	}
}

class gPieChart extends gChart{
	function __construct($width = 350, $height = 200) {
		$this -> setProperty('cht', 'p');
		$this -> setDimensions($width, $height);
	}
	public function getApplicableLabels($labels) {
		return array_splice($labels, 0, count($this->values[0]));
	}
	public function set3D($is3d = true, $resize = true){
		if($is3d){
			$this -> setProperty('cht', 'p3');
			if ($resize)
				$this -> setDimensions($this->getWidth() * 1.5, $this->getHeight());
		}
		else {
			$this -> setProperty('cht', 'p');
			if ($resize)
				$this -> setDimensions($this->getWidth() / 1.5, $this->getHeight());
		}
	}
	
	public function setLabels($labels) {
		$this -> setProperty('chl', urlencode($this->encodeData($this->getApplicableLabels($labels),"|")));
	}
	
	public function setRotation($angle, $degree = false) {
		if ($degree)
			$angle = ($angle / 360) * 6.2831;
		$this -> setProperty('chp', $angle);
	}
	
	public function setColors($colors) {
		$this -> setProperty('chco', $this->encodeData($this->getApplicableLabels($colors), "|"), true);
	}
	
	public function addColors($colors) {
		$this -> setProperty('chco', $this->encodeData($colors, "|"), true, ",");
	}
}

class gPie3DChart extends gPieChart {
	function __construct($width = 500, $height = 200) {
		$this -> setProperty('cht', 'p3');
		$this -> setDimensions($width, $height);
	}
}

class gConcentricPieChart extends gPieChart {
	function __construct($width = 350, $height = 200) {
		$this -> setProperty('cht', 'pc');
		$this -> setDimensions($width, $height);
	}
	
	public function getApplicableLabels($labels) {
		return array_splice($labels, 0, count($this->values, COUNT_RECURSIVE));
	}
	
	public function addLegend($labels) {
		$this -> setProperty('chdl', urlencode($this->encodeData($this->getApplicableLabels($labels),"|")), true);
	}
}

class gLineChart extends gChart{
		
	function __construct($width = 200, $height = 200){
		$this -> setProperty('cht', 'lc');
		$this -> setDimensions($width, $height);		
	}
	public function getUrl() {
		$retStr = parent::getUrl();
		return $retStr;	
	}
}

class gBarChart extends gChart{
	
	function __construct($width = 200, $height = 200, $type = 'g', $direction='v'){
		$this -> setChartType($type, $direction);
		$this -> setDimensions($width, $height);	
	}
	protected function setChartType($type, $direction) {
		$this-> setProperty('cht', 'b'.$direction.$type);
	}
	public function getUrl(){
		$retStr = parent::getUrl();
		return $retStr;
	}
	
	public function setBarWidth($barWidth, $spaceBetweenBars = 4,$spaceBetweenGroups = 8){
		$this -> setProperty('chbh', $this->encodeData(array($barWidth, $spaceBetweenBars,$spaceBetweenGroups), ','));
	}
	
	public function setAutoBarWidth() {
		$this -> setProperty('chbh', 'a');
	}
	
	public function setBarScale($barScale = 'a', $spaceBetweenBars = '4',$spaceBetweenGroups = '8') {
		$this -> setProperty('chbh', $this->encodeData(array($barScale, $spaceBetweenBars,$spaceBetweenGroups), ','));
	}
	
	public function addColors($colors) {
		$this -> setProperty('chco', $this->encodeData($colors, "|"), true, ",");
	}
}
class gGroupedBarChart extends gBarChart{
	function __construct($width = 200, $height = 200){
		$this -> setChartType('g', 'v');
		$this -> setDimensions($width, $height);	
	}
	public function setHorizontal($isHorizontal = true){
		if($isHorizontal) {
			$this -> setChartType('g', 'h');
		} else {
			$this -> setChartType('g', 'v');
		}
	}	
}
class gStackedBarChart extends gBarChart{
	function __construct($width = 200, $height = 200){
		$this -> setChartType('s', 'v');
		$this -> setDimensions($width, $height);			
	}
	public function setHorizontal($isHorizontal = true){
		if($isHorizontal){
			$this -> setChartType('s', 'h');
		}
		else{
			$this -> setChartType('s', 'v');
		}
	}
}
class gOverlappedBarChart extends gBarChart{
	function __construct($width = 200, $height = 200){
		$this -> setChartType('o', 'v');
		$this->setDimensions($width, $height);			
	}
	public function setHorizontal($isHorizontal = true){
		if($isHorizontal){
			$this -> setChartType('o', 'h');
		}
		else{
			$this -> setChartType('o', 'v');
		}
	}
}

class gVennDiagram extends gChart{
	
	private $sizes;
	private $intersections;
	private $numData;
		
	function __construct($width = 200, $height = 200){
		$this -> setProperty('cht', 'v');
		$this -> setDimensions($width, $height);
		$this -> sizes = array(0,0,0);	
		$this -> intersections = array(0,0,0,0);
		$this->numData = 2;	
	}
	public function setSizes($A=0, $B=0, $C=0){
		if ($C)
			$this->numData = 3;
		$this->sizes = array($A, $B, $C);
	}
	public function setIntersections($AB=0, $AC=0, $BC=0, $ABC=0){
		$this->intersections = array($AB, $AC, $BC, $ABC);
	}
	public function setDataSetString(){
		$fullDataSet = array_merge($this->sizes, $this->intersections);
		$this -> setProperty('chd', $this->getEncodingType().":".$this->encodeData($fullDataSet, ',', $this->getEncodingType()));
	}
	public function getApplicableLabels($labels) {
		return array_splice($labels, 0, $this->numData);
	}
}

class gFormula extends gChart{
	
	function __construct($width = 0, $height = 0){
		$this -> setDimensions($width, $height);
		$this -> setProperty('cht','tx');
	}
	
	public function setLatexCode($latexCode){
		$this -> setProperty('chl', urlencode($latexCode));
	}
	public function setTextColor($textColor){
		$this -> setProperty('chco', $textColor);
	}
	
	public function getImgCode(){
		$code = '<img src="';
		$code .= $this->getUrl().'"';
		$code .= 'alt="gChartPhp Chart"'; 
		if($this->width)
			$code .= ' width='.$this->width;
		if($this->height)
			$code .= ' height='.$this->height;
		$code .= '>';
		print($code);
	}
}

class gQRCode extends gChart{
	
	function __construct($width = 150, $height = 150){
		$this -> setDimensions($width, $height);
		$this -> setProperty('cht','qr');
	}
	
	public function setQRCode($QRCode){
		$this -> setProperty('chl', urlencode($QRCode));
	}
	
	public function setOutputEncoding($newOutputEncoding){
		$this -> setProperty('choe', $newOutputEncoding);
	}
	
	public function setErrorCorrectionLevel($newErrorCorrectionLevel, $newMargin){
		$this -> setProperty('chld', $newErrorCorrectionLevel.'|'.$newMargin);
	}
}

class gMeterChart extends gChart{
	
	function __construct($width = 200, $height = 200){
		$this -> setDimensions($width, $height);
		$this -> setProperty('cht','gom');
	}
	public function getApplicableLabels($labels) {
		return array_splice($labels, 0, count($this->values[0]));
	}
	
	public function setLabels($labels) {
		$this -> setProperty('chl', urlencode($this->encodeData($this->getApplicableLabels($labels),"|")));
	}
}

class gMapChart extends gChart {
	
	function __construct($width = 440, $height = 220){
		$this -> setDimensions($width, $height);
		$this -> setProperty('cht','t');
	}
	
	
	public function setZoomArea($zoomArea) {
		$this -> setProperty('chtm', $zoomArea);
	}
	
	public function setStateCodes($stateCodes){
		$this -> setProperty('chld', $this->encodeData($stateCodes, ''));
	}
	
	public function setColors($defaultColor) {
		$gradientColors = (func_num_args() > 1) ? func_get_arg(1) : array('0000FF', 'FF0000');
		$this -> setProperty('chco', $this->encodeData(array_merge(array($defaultColor), $gradientColors), ','));
	}

	public function getApplicableLabels($labels) {
		return array_splice($labels, 0, count($this->values[0]));
	}
}
class gScatterChart extends gChart{
	function __construct($width = 200, $height = 200){
		$this -> setDimensions($width, $height);
		$this -> setProperty('cht','s');
	}
	
	public function getApplicableLabels($labels) {
		return $labels;
	}
	
	public function setColors($colors) {
		$this -> setProperty('chco', $this->encodeData($this->getApplicableLabels($colors),"|"));
	}
}
?>
