<?php
/*
 * This work is hereby released into the Public Domain.
 * To view a copy of the public domain dedication,
 * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
 * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
 *
 */

require_once dirname(__FILE__)."/Component.class.php";
 
/* <php4> */

define("PIE_DARK", 1);
define("PIE_COLORED", 2);
define("PIE_AQUA", 3);
define("PIE_EARTH", 4);

/* </php4> */
 
/**
 * Pie
 *
 * @package Artichow
 */
class awPie extends awComponent {

	/**
	 * A dark theme for pies
	 *
	 *
	 * @var int
	 */
	

	/**
	 * A colored theme for pies
	 *
	 * @var int
	 */
	

	/**
	 * A water theme for pies
	 *
	 * @var int
	 */
	

	/**
	 * A earth theme for pies
	 *
	 * @var int
	 */
	
	
	/**
	 * Pie values
	 *
	 * @var array
	 */
	var $values;
	
	/**
	 * Pie colors
	 *
	 * @var array
	 */
	var $colors;
	
	/**
	 * Pie legend
	 * 
	 * @var array
	 */
	var $legendValues = array();
	
	/**
	 * Intensity of the 3D effect
	 *
	 * @var int
	 */
	var $size;
	
	/**
	 * Border color
	 *
	 * @var Color
	 */
	var $border;
	
	/**
	 * Pie explode
	 *
	 * @var array
	 */
	var $explode = array();
	
	/**
	 * Initial angle
	 *
	 * @var int
	 */
	var $angle = 0;
	
	/**
	 * Labels precision
	 *
	 * @var int
	 */
	var $precision;
	
	/**
	 * Labels number
	 *
	 * @var int
	 */
	var $number;
	
	/**
	 * Labels minimum
	 *
	 * @var int
	 */
	var $minimum;
	
	/**
	 * Labels position
	 *
	 * @var int
	 */
	var $position = 15;
	
	/**
	 * Labels of your pie
	 *
	 * @var Label
	 */
	var $label;
	
	/**
	 * Build the plot
	 *
	 * @param array $values Pie values
	 */
	 function awPie($values, $colors = PIE_COLORED) {
		
		$this->setValues($values);
		
		if(is_array($colors)) {
			$this->colors = $colors;
		} else {
		
			switch($colors) {
			
				case PIE_AQUA :
					$this->colors = array(
						new awColor(131, 220, 215),
						new awColor(131, 190, 215),
						new awColor(131, 160, 215),
						new awColor(160, 140, 215),
						new awColor(190, 131, 215),
						new awColor(220, 131, 215)
					);
					break;
			
				case PIE_EARTH :
					$this->colors = array(
						new awColor(97, 179, 110),
						new awColor(130, 179, 97),
						new awColor(168, 179, 97),
						new awColor(179, 147, 97),
						new awColor(179, 108, 97),
						new awColor(99, 107, 189),
						new awColor(99, 165, 189)
					);
					break;
			
				case PIE_DARK :
					$this->colors = array(
						new awColor(140, 100, 170),
						new awColor(130, 170, 100),
						new awColor(160, 160, 120),
						new awColor(150, 110, 140),
						new awColor(130, 150, 160),
						new awColor(90, 170, 140)
					);
					break;
					
				default :
					$this->colors = array(
						new awColor(187, 213, 151),
						new awColor(223, 177, 151),
						new awColor(111, 186, 132),
						new awColor(197, 160, 230),
						new awColor(165, 169, 63),
						new awColor(218, 177, 89),
						new awColor(116, 205, 121),
						new awColor(200, 201, 78),
						new awColor(127, 205, 177),
						new awColor(205, 160, 160),
						new awColor(190, 190, 190)
					);
					break;
			
			}
		
		}
	
		parent::awComponent();
		
		$this->label = new awLabel;
		$this->label->setCallbackFunction('callbackPerCent');
		
	}
	
	/**
	 * Change legend values
	 *
	 * @param array $legend An array of values for each part of the pie
	 */
	 function setLegend($legend) {
	
		$this->legendValues = (array)$legend;
	
	}
	
	/**
	 * Set a border all around the pie
	 *
	 * @param $color A color for the border
	 */
	 function setBorderColor($color) {
		$this->border = $color;
	}
	
	/**
	 * Set a border all around the pie
	 *
	 * @param $color A color for the border
	 */
	 function setBorder($color) {
		if(ARTICHOW_DEPRECATED === TRUE) {
			awImage::drawError('Class Pie: Method setBorder() has been deprecated since Artichow 1.0.9. Please use setBorderColor() instead.');
		} else {
			$this->setBorderColor($color);
		}
	}
	
	/**
	 * Change 3D effect intensity
	 *
	 * @param int $size Effect size
	 */
	 function set3D($size) {
		$this->size = (int)$size;
	}
	
	/**
	 * Change initial angle
	 *
	 * @param int $angle New angle in degrees
	 */
	 function setStartAngle($angle) {
		$this->angle = (int)$angle;
	}
	
	/**
	 * Change label precision
	 *
	 * @param int $precision New precision
	 */
	 function setLabelPrecision($precision) {
		$this->precision = (int)$precision;
	}
	
	/**
	 * Change label position
	 *
	 * @param int $position New position in pixels
	 */
	 function setLabelPosition($position) {
		$this->position = (int)$position;
	}
	
	/**
	 * Change label number
	 *
	 * @param int $number New number
	 */
	 function setLabelNumber($number) {
		$this->number = is_null($number) ? $number : (int)$number;
	}
	
	/**
	 * Change label minimum
	 *
	 * @param int $minimum New minimum
	 */
	 function setLabelMinimum($minimum) {
		$this->minimum = is_null($minimum) ? $minimum : (int)$minimum;
	}
	
	/**
	 * Change Pie explode
	 *
	 * @param array $explode
	 */
	 function explode($explode) {
		$this->explode = (array)$explode;
	}
	
	 function drawEnvelope($driver) {
	
	}
	
	 function drawComponent($driver, $x1, $y1, $x2, $y2, $aliasing) {
		
		$count = count($this->values);
		$sum = array_sum($this->values);
		
		$width = $x2 - $x1;
		$height = $y2 - $y1;
		
		if($aliasing) {
			$x = $width / 2;
			$y = $height / 2;
		} else {
			$x = $width / 2 + $x1;
			$y = $height / 2 + $y1;
		}
		
		$position = $this->angle;
		$values = array();
		$parts = array();
		$angles = 0;
		
		if($aliasing) {
			$side = new awSide(0, 0, 0, 0);
		}
		
		foreach($this->values as $key => $value) {
		
			$angle = ($value / $sum * 360);
			
			if($key === $count - 1) {
				$angle = 360 - $angles;
			}
			
			$angles += $angle;
			
			if(array_key_exists($key, $this->explode)) {
				$middle = 360 - ($position + $angle / 2);
				$posX = $this->explode[$key] * cos($middle * M_PI / 180);
				$posY = $this->explode[$key] * sin($middle * M_PI / 180) * -1;
				
				if($aliasing) {
					$explode = new awPoint(
						$posX * 2,
						$posY * 2
					);
					$side->set(
						max($side->left, $posX * -2),
						max($side->right, $posX * 2),
						max($side->top, $posY * -2),
						max($side->bottom, $posY * 2)
					);
				} else {
					$explode = new awPoint(
						$posX,
						$posY
					);
				}
				
			} else {
				$explode = new awPoint(0, 0);
			}
			
			$values[$key] = array(
				$position, ($position + $angle), $explode
			);
			
			$color = $this->colors[$key % count($this->colors)];
			$parts[$key] = new awPiePart($color);
			
			// Add part to the legend
			$legend = array_key_exists($key, $this->legendValues) ? $this->legendValues[$key] : $key;
			$this->legend->add($parts[$key], $legend, LEGEND_BACKGROUND);
			
			$position += $angle;
		
		}
		
		if($aliasing) {
		
			$mainDriver = $driver;
			
			$x *= 2;
			$y *= 2;
			$width *= 2;
			$height *= 2;
			$this->size *= 2;
			
			$image = new awImage;
			$image->border->hide();
			
			// Adds support for antialiased pies on non-white background
			$background = $this->getBackground();
			
			if(is_a($background, 'awColor')) {
				$image->setBackgroundColor($background);
			}
//			elseif(is_a($background, 'awGradient')) {
//				$image->setBackgroundColor(new White(100));
//			}
			
			$image->setSize(
				$width + $side->left + $side->right,
				$height + $side->top + $side->bottom + $this->size + 1 /* bugs.php.net ! */
			);
			
			$driver = $image->getDriver(
				$width / $image->width,
				$height / $image->height,
				($width / 2 + $side->left) / $image->width,
				($height / 2 + $side->top) / $image->height
			);
			
		}
		
		// Draw 3D effect
		for($i = $this->size; $i > 0; $i--) {
		
			foreach($values as $key => $value) {
			
				$color = $this->colors[$key % count($this->colors)];
				$color->brightness(-50);
				
				list($from, $to, $explode) = $value;
				
				$driver->filledArc($color, $explode->move($x, $y + $i), $width, $height, $from, $to);
				
				unset($color);
				
				if(is_a($this->border, 'awColor')) {
				
					$point = $explode->move($x, $y);
					
					if($i === $this->size) {
				
						$driver->arc($this->border, $point->move(0, $this->size), $width, $height, $from, $to);
						
					}
				
				}
			
			}
			
		}
		
		foreach($values as $key => $value) {
			
			$color = $this->colors[$key % count($this->colors)];
			
			list($from, $to, $explode) = $value;
			
			$driver->filledArc($color, $explode->move($x, $y), $width, $height, $from, $to);
			
			if(is_a($this->border, 'awColor')) {
			
				$point = $explode->move($x, $y);
				$driver->arc($this->border, $point, $width, $height, $from, $to);
			}
		
		}
		
		if($aliasing) {
		
			$x = $x / 2 + $x1;
			$y = $y / 2 + $y1;
			$width /= 2;
			$height /= 2;
			$this->size /= 2;
			
			foreach($values as $key => $value) {
				$old = $values[$key][2];
				$values[$key][2] = new awPoint(
					$old->x / 2, $old->y / 2
				);
			}
			
			$mainDriver->copyResizeImage(
				$image,
				new awPoint($x1 - $side->left / 2, $y1 - $side->top / 2),
				new awPoint($x1 - $side->left / 2 + $image->width / 2, $y1 - $side->top / 2 + $image->height/ 2),
				new awPoint(0, 0),
				new awPoint($image->width, $image->height),
				TRUE
			);
			
			$driver = $mainDriver;
		
		}
		
		// Get labels values
		$pc = array();
		foreach($this->values as $key => $value) {
			$pc[$key] = round($value / $sum * 100, $this->precision);
		}
		if($this->label->count() === 0) { // Check that there is no user defined values
			$this->label->set($pc);
		}
		
		$position = 0;
		
		foreach($pc as $key => $value) {
		
			// Limit number of labels to display
			if($position === $this->number) {
				break;
			}
			
			if(is_null($this->minimum) === FALSE and $value < $this->minimum) {
				continue;
			}
			
			$position++;
			
			list($from, $to, $explode) = $values[$key];
			
			$angle = $from + ($to - $from) / 2;
			$angleRad = (360 - $angle) * M_PI / 180;
			
			$point = new awPoint(
				$x + $explode->x + cos($angleRad) * ($width / 2 + $this->position),
				$y + $explode->y - sin($angleRad) * ($height / 2 + $this->position)
			);
			
			$angle %= 360;
			
			// We don't display labels on the 3D effect
			if($angle > 0 and $angle < 180) {
				$point = $point->move(0, -1 * sin($angleRad) * $this->size);
			}
			
			if($angle >= 45 and $angle < 135) {
				$this->label->setAlign(LABEL_CENTER, LABEL_BOTTOM);
			} else if($angle >= 135 and $angle < 225) {
				$this->label->setAlign(LABEL_RIGHT, LABEL_MIDDLE);
			} else if($angle >= 225 and $angle < 315) {
				$this->label->setAlign(LABEL_CENTER, LABEL_TOP);
			} else {
				$this->label->setAlign(LABEL_LEFT, LABEL_MIDDLE);
			}
			
			$this->label->draw(
				$driver,
				$point,
				$key
			);
			
		}
		
	}
	
	/**
	 * Return margins around the component
	 *
	 * @return array Left, right, top and bottom margins
	 */
	 function getMargin() {
		
		// Get axis informations
		
		$leftAxis = $this->padding->left;
		$rightAxis = $this->padding->right;
		$topAxis = $this->padding->top;
		$bottomAxis = $this->padding->bottom;
		
		return array($leftAxis, $rightAxis, $topAxis, $bottomAxis);
		
	}
	
	
	/**
	 * Change values of Y axis
	 * This method ignores not numeric values
	 *
	 * @param array $values
	 */
	 function setValues($values) {
	
		$this->checkArray($values);
		$this->values = $values;
		
	}
	
	
	/**
	 * Return values of Y axis
	 *
	 * @return array
	 */
	 function getValues() {
		return $this->values;
	}
	
	 function checkArray(&$array) {
	
		if(is_array($array) === FALSE) {
			awImage::drawError("Class Pie: You tried to set values that are not an array.");
		}
		
		foreach($array as $key => $value) {
			if(is_numeric($value) === FALSE) {
				unset($array[$key]);
			}
		}
		
		if(count($array) < 1) {
			awImage::drawError("Class Pie: Your graph must have at least 1 value.");
		}
	
	}

}

registerClass('Pie');

/**
 * Pie
 *
 * @package Artichow
 */
class awPiePart {

	/**
	 * Pie part color
	 *
	 * @var Color
	 */
	var $color;

	/**
	 * Build a new awPiePart
	 *
	 * @param $color Pie part color
	 */
	 function awPiePart($color) {
	
		$this->color = $color;
	
	}

	/**
	 * Get the background color or gradient of an element of the component
	 *
	 * @return Color, Gradient
	 */
	 function getLegendBackground() {
		return $this->color;
	}

	/**
	 * Get the line thickness
	 *
	 * @return NULL
	 */
	 function getLegendLineThickness() {
	}

	/**
	 * Get the line type
	 *
	 * @return NULL
	 */
	 function getLegendLineStyle() {
	}

	/**
	 * Get the color of line
	 *
	 * @return NULL
	 */
	 function getLegendLineColor() {
	}

	/**
	 * Get a mark object
	 *
	 * @return NULL
	 */
	 function getLegendMark() {
	}

}

registerClass('PiePart');

function callbackPerCent($value) {
	return $value.'%';
}
?>