<?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.
 *
 */
 
/* <php5> */
if(is_file(dirname(__FILE__)."/Artichow.cfg.php")) { // For PHP 4+5 version
	require_once dirname(__FILE__)."/Artichow.cfg.php";
}
/* </php5> */


/* <php4> */

define("IMAGE_JPEG", 1);
define("IMAGE_PNG", 2);
define("IMAGE_GIF", 3);

/* </php4> */

/*
 * Register a class with the prefix in configuration file
 */
function registerClass($class, $abstract = FALSE) {

	if(ARTICHOW_PREFIX === 'aw') {
		return;
	}
	
	/* <php5> */
	if($abstract) {
		$abstract = 'abstract';
	} else {
		$abstract = '';
	}
	/* </php5> */
	/* <php4> --
	$abstract = '';
	-- </php4> */

	eval($abstract." class ".ARTICHOW_PREFIX.$class." extends aw".$class." { }");

}

/*
 * Register an interface with the prefix in configuration file
 */
function registerInterface($interface) {

	if(ARTICHOW_PREFIX === 'aw') {
		return;
	}

	/* <php5> */
	eval("interface ".ARTICHOW_PREFIX.$interface." extends aw".$interface." { }");
	/* </php5> */

}

// Some useful files
require_once ARTICHOW."/Component.class.php";

require_once ARTICHOW."/inc/Grid.class.php";
require_once ARTICHOW."/inc/Tools.class.php";
require_once ARTICHOW."/inc/Driver.class.php";
require_once ARTICHOW."/inc/Math.class.php";
require_once ARTICHOW."/inc/Tick.class.php";
require_once ARTICHOW."/inc/Axis.class.php";
require_once ARTICHOW."/inc/Legend.class.php";
require_once ARTICHOW."/inc/Mark.class.php";
require_once ARTICHOW."/inc/Label.class.php";
require_once ARTICHOW."/inc/Text.class.php";
require_once ARTICHOW."/inc/Color.class.php";
require_once ARTICHOW."/inc/Font.class.php";
require_once ARTICHOW."/inc/Gradient.class.php";
require_once ARTICHOW."/inc/Shadow.class.php";
require_once ARTICHOW."/inc/Border.class.php";

require_once ARTICHOW."/common.php";
 
/**
 * An image for a graph
 *
 * @package linea21.externals
 * @subpackage artichow
 */
class awImage {

	/**
	 * Graph width
	 *
	 * @var int
	 */
	public $width;

	/**
	 * Graph height
	 *
	 * @var int
	 */
	public $height;
	
	/**
	 * Use anti-aliasing ?
	 *
	 * @var bool
	 */
	protected $antiAliasing = FALSE;
	
	/**
	 * Image format
	 *
	 * @var int
	 */
	protected $format = awImage::PNG;
	
	/**
	 * Image background color
	 *
	 * @var Color
	 */
	protected $background;
	
	/**
	 * GD resource
	 *
	 * @var resource
	 */
	protected $resource;
	
	/**
	 * A Driver object
	 *
	 * @var Driver
	 */
	protected $driver;
	
	/**
	 * Driver string
	 * 
	 * @var string
	 */
	protected $driverString;
		
	/**
	 * Shadow
	 *
	 * @var Shadow
	 */
	public $shadow;
	
	/**
	 * Image border
	 *
	 * @var Border
	 */
	public $border;
	
	/**
	 * Use JPEG for image
	 *
	 * @var int
	 */
	const JPEG = IMG_JPG;
	
	/**
	 * Use PNG for image
	 *
	 * @var int
	 */
	const PNG = IMG_PNG;
	
	/**
	 * Use GIF for image
	 *
	 * @var int
	 */
	const GIF = IMG_GIF;
	
	/**
	 * Build the image
	 */
	public function __construct() {
		
		$this->background = new awColor(255, 255, 255);
		$this->shadow = new awShadow(awShadow::RIGHT_BOTTOM);
		$this->border = new awBorder;
		
	}
	
	/**
	 * Get driver of the image
	 *
	 * @param int $w Driver width (from 0 to 1) (default to 1)
	 * @param int $h Driver height (from 0 to 1) (default to 1)
	 * @param float $x Position on X axis of the center of the driver (default to 0.5)
	 * @param float $y Position on Y axis of the center of the driver (default to 0.5)
	 * @return Driver
	 */
	public function getDriver($w = 1, $h = 1, $x = 0.5, $y = 0.5) {
		$this->create();
		$this->driver->setSize($w, $h);
		$this->driver->setPosition($x, $y);
		return $this->driver;
	}
	
	/**
	 * Sets the driver that will be used to draw the graph
	 * 
	 * @param string $driverString
	 */
	public function setDriver($driverString) {
		$this->driver = $this->selectDriver($driverString);
		
		$this->driver->init($this);
	}
	
	/**
	 * Change the image size
	 *
	 * @var int $width Image width
	 * @var int $height Image height
	 */
	public function setSize($width, $height) {
	
		if($width !== NULL) {
			$this->width = (int)$width;
		}
		if($height !== NULL) {
			$this->height = (int)$height;
		}
	
	}
	
	/**
	 * Change image background
	 * 
	 * @param mixed $background
	 */
	public function setBackground($background) {
		if($background instanceof awColor) {
			$this->setBackgroundColor($background);
		} elseif($background instanceof awGradient) {
			$this->setBackgroundGradient($background);
		}
	}
	
	/**
	 * Change image background color
	 *
	 * @param awColor $color
	 */
	public function setBackgroundColor(awColor $color) {
		$this->background = $color;
	}
	
	/**
	 * Change image background gradient
	 *
	 * @param awGradient $gradient
	 */
	public function setBackgroundGradient(awGradient $gradient) {
		$this->background = $gradient;
	}
	
	/**
	 * Return image background, whether a Color or a Gradient
	 * 
	 * @return mixed
	 */
	public function getBackground() {
		return $this->background;
	}
	
	/**
	 * Turn antialiasing on or off
	 *
	 * @var bool $bool
	 */
	public function setAntiAliasing($bool) {
		$this->antiAliasing = (bool)$bool;
	}
	
	/**
	 * Return the antialiasing setting
	 *
	 * @return bool
	 */
	public function getAntiAliasing() {
		return $this->antiAliasing;
	}
	
	/**
	 * Change image format
	 *
	 * @var int $format New image format
	 */
	public function setFormat($format) {
		if($format === awImage::JPEG or $format === awImage::PNG or $format === awImage::GIF) {
			$this->format = $format;
		}
	}
	
	/**
	 * Returns the image format as an integer
	 *
	 * @return unknown
	 */
	public function getFormat() {
		return $this->format;
	}
	
	/**
	 * Returns the image format as a string
	 *
	 * @return string
	 */
	public function getFormatString() {
		
		switch($this->format) {
			case awImage::JPEG :
				return 'jpeg';
			case awImage::PNG :
				return 'png';
			case awImage::GIF :
				return 'gif';
		}
		
	}

	/**
	 * Create a new awimage
	 */
	public function create() {

		if($this->driver === NULL) {
			$driver = $this->selectDriver($this->driverString);

			$driver->init($this);
			
			$this->driver = $driver;
		}

	}
	
	/**
	 * Select the correct driver
	 *
	 * @param string $driver The desired driver
	 * @return mixed
	 */
	protected function selectDriver($driver) {
		$drivers = array('gd');
		$driver = strtolower((string)$driver);

		if(in_array($driver, $drivers, TRUE)) {
			$string = $driver;
		} else {
			$string = ARTICHOW_DRIVER;
		}

		switch ($string) {
				case 'gd':
					require_once ARTICHOW.'/inc/drivers/gd.class.php';
					$this->driverString = $string;
					return new awGDDriver();
					
				default:
					// We should never get here, unless the wrong string is used AND the ARTICHOW_DRIVER
					// global has been messed with.
					awImage::drawError('Class Image: Unknown driver type (\''.$string.'\')');
					break;
			}
	}
	
	/**
	 * Draw a component on the image
	 *
	 * @var awComponent $component A component
	 */
	public function drawComponent(awComponent $component) {
		
		$shadow = $this->shadow->getSpace(); // Image shadow
		$border = $this->border->visible() ? 1 : 0; // Image border size
	
		$driver = clone $this->driver;
		$driver->setImageSize(
			$this->width - $shadow->left - $shadow->right - $border * 2,
			$this->height - $shadow->top - $shadow->bottom - $border * 2
		);
	
		// No absolute size specified
		if($component->w === NULL and $component->h === NULL) {
		
			list($width, $height) = $driver->setSize($component->width, $component->height);
	
			// Set component size in pixels
			$component->setAbsSize($width, $height);
			
		} else {
		
			$driver->setAbsSize($component->w, $component->h);
		
		}
		
		if($component->top !== NULL and $component->left !== NULL) {
			$driver->setAbsPosition(
				$border + $shadow->left + $component->left,
				$border + $shadow->top + $component->top
			);
		} else {
			$driver->setPosition($component->x, $component->y);
		}
		
		$driver->movePosition($border + $shadow->left, $border + $shadow->top);
		
		list($x1, $y1, $x2, $y2) = $component->getPosition();
		
		$component->init($driver);
		
		$component->drawComponent($driver, $x1, $y1, $x2, $y2, $this->antiAliasing);
		$component->drawEnvelope($driver, $x1, $y1, $x2, $y2);
		
		$component->finalize($driver);
	
	}
	
	protected function drawShadow() {
	
		$driver = $this->getDriver();
		
		$this->shadow->draw(
			$driver,
			new awPoint(0, 0),
			new awPoint($this->width, $this->height),
			awShadow::IN
		);
	
	}
	
	/**
	 * Send the image into a file or to the user browser
	 *
	 */
	public function send() {
		$this->driver->send($this);
	}
	
	/**
	 * Return the image content as binary data
	 *
	 */	
	public function get() {
		return $this->driver->get($this);
	}
	
	/**
	 * Send the correct HTTP header according to the image type
	 *
	 */
	public function sendHeaders() {

		if(headers_sent() === FALSE) {
			
			switch ($this->driverString) {
				case 'gd' :
					header('Content-type: image/'.$this->getFormatString());
					break;
				
			}

		}
	
	}
	
	/* <php5> */
	private static $errorWriting = FALSE;
	/* </php5> */

	/*
	 * Display an error image and exit
	 *
	 * @param string $message Error message
	 */
	public static function drawError($message) {
	
		/* <php4> -- 
		static $errorWriting;
		-- </php4> */
	
		if(self::$errorWriting) {
			return;
		}
	
		self::$errorWriting = TRUE;
	
		$message = wordwrap($message, 40, "\n", TRUE);
		
		$width = 400;
		$height = max(100, 40 + 22.5 * (substr_count($message, "\n") + 1));
		
		$image = new awImage();
		$image->setSize($width, $height);
		$image->setDriver('gd');
		
		$driver = $image->getDriver();
		$driver->init($image);
		
		// Display title
		$driver->filledRectangle(
			new awWhite,
			new awLine(
				new awPoint(0, 0),
				new awPoint($width, $height)
			)
		);
		
		$driver->filledRectangle(
			new awRed,
			new awLine(
				new awPoint(0, 0),
				new awPoint(110, 25)
			)
		);
		
		$text = new awText(
			"Artichow error",
			new awFont3,
			new awWhite,
			0
		);
		
		$driver->string($text, new awPoint(5, 6));
		
		// Display red box
		$driver->rectangle(
			new awRed,
			new awLine(
				new awPoint(0, 25),
				new awPoint($width - 90, $height - 1)
			)
		);
		
		// Display error image
		$file = ARTICHOW_IMAGE.DIRECTORY_SEPARATOR.'error.png';
		
		$imageError = new awFileImage($file);
		$driver->copyImage(
			$imageError,
			new awPoint($width - 81, $height - 81),
			new awPoint($width - 1, $height - 1)
		);
		
		// Draw message
		$text = new awText(
			strip_tags($message),
			new awFont2,
			new awBlack,
			0
		);
		
		$driver->string($text, new awPoint(10, 40));
		
		$image->send();
		
		exit;
	
	}
	
	/*
	 * Display an error image located in a file and exit
	 *
	 * @param string $error Error name
	 */
	public static function drawErrorFile($error) {
	
		$file = ARTICHOW_IMAGE.DIRECTORY_SEPARATOR.'errors'.DIRECTORY_SEPARATOR.$error.'.png';
		
		header("Content-Type: image/png");
		readfile($file);
		exit;
	
	}

}

registerClass('Image');

 
/**
 * Load an image from a file
 *
 * @package linea21.externals
 * @subpackage artichow
 */
class awFileImage extends awImage {

	/**
	 * Build a new awimage
	 *
	 * @param string $file Image file name
	 */
	public function __construct($file) {
	
		$driver = $this->selectDriver($this->driverString);
		
		$driver->initFromFile($this, $file);
		
		$this->driver = $driver;
	
	}

}

registerClass('FileImage');

?>
