<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category  Zend
 * @package   Zend_Measure
 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 * @version   $Id: Abstract.php 12060 2008-10-21 17:23:55Z thomas $
 */

/**
 * @see Zend_Locale
 */
require_once 'Zend/Locale.php';

/**
 * @see Zend_Locale_Math
 */
require_once 'Zend/Locale/Math.php';

/**
 * @see Zend_Locale_Format
 */
require_once 'Zend/Locale/Format.php';

/**
 * Abstract class for all measurements
 *
 * @category   Zend
 * @package    Zend_Measure
 * @subpackage Zend_Measure_Abstract
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Measure_Abstract
{
    /**
     * Plain value in standard unit
     *
     * @var string $_value
     */
    protected $_value;

    /**
     * Original type for this unit
     *
     * @var string $_type
     */
    protected $_type;

    /**
     * Locale identifier
     *
     * @var string $_locale
     */
    protected $_locale = null;

    /**
     * Unit types for this measurement
     */
    protected $_units = array();

    /**
     * Zend_Measure_Abstract is an abstract class for the different measurement types
     *
     * @param  $value  mixed  - Value as string, integer, real or float
     * @param  $type   type   - OPTIONAL a Zend_Measure_Area Type
     * @param  $locale locale - OPTIONAL a Zend_Locale Type
     * @throws Zend_Measure_Exception
     */
    public function __construct($value, $type = null, $locale = null)
    {
        if (($type !== null) and (Zend_Locale::isLocale($type, null, false))) {
            $locale = $type;
            $type = null;
        }

        if (empty($locale)) {
            require_once 'Zend/Registry.php';
            if (Zend_Registry::isRegistered('Zend_Locale') === true) {
                $locale = Zend_Registry::get('Zend_Locale');
            }
        }

        if ($locale === null) {
            $locale = new Zend_Locale();
        }

        if (!Zend_Locale::isLocale($locale, true, false)) {
            if (!Zend_Locale::isLocale($locale, false, false)) {
                require_once 'Zend/Measure/Exception.php';
                throw new Zend_Measure_Exception("Language (" . (string) $locale . ") is unknown");
            }

            $locale = new Zend_Locale($locale);
        }

        $this->_locale = (string) $locale;

        if ($type === null) {
            $type = $this->_units['STANDARD'];
        }

        if (isset($this->_units[$type]) === false) {
            require_once 'Zend/Measure/Exception.php';
            throw new Zend_Measure_Exception("Type ($type) is unknown");
        }

        $this->setValue($value, $type, $this->_locale);
    }

    /**
     * Returns the internal value
     *
     * @param integer $round (Optional) Rounds the value to an given precision,
     *                                  Default is 2, -1 returns without rounding
     */
    public function getValue($round = 2)
    {
        if ($round < 0) {
            return $this->_value;
        }

        return Zend_Locale_Math::round($this->_value, $round);
    }

    /**
     * Set a new value
     *
     * @param  integer|string      $value   Value as string, integer, real or float
     * @param  string              $type    OPTIONAL A Zend_Measure_Acceleration Type
     * @param  string|Zend_Locale  $locale  OPTIONAL Locale for parsing numbers
     * @throws Zend_Measure_Exception
     */
    public function setValue($value, $type = null, $locale = null)
    {
        if (($type !== null) and (Zend_Locale::isLocale($type, null, false))) {
            $locale = $type;
            $type = null;
        }

        if ($locale === null) {
            $locale = $this->_locale;
        }

        if (!Zend_Locale::isLocale($locale, true, false)) {
            if (!Zend_Locale::isLocale($locale, false, false)) {
                require_once 'Zend/Measure/Exception.php';
                throw new Zend_Measure_Exception("Language (" . (string) $locale . ") is unknown");
            }

            $locale = new Zend_Locale($locale);
        }

        $locale = (string) $locale;
        if ($type === null) {
            $type = $this->_units['STANDARD'];
        }

        if (empty($this->_units[$type])) {
            require_once 'Zend/Measure/Exception.php';
            throw new Zend_Measure_Exception("Type ($type) is unknown");
        }

        try {
            $value = Zend_Locale_Format::getNumber($value, array('locale' => $locale));
        } catch(Exception $e) {
            require_once 'Zend/Measure/Exception.php';
            throw new Zend_Measure_Exception($e->getMessage());
        }

        $this->_value = $value;
        $this->setType($type);
    }

    /**
     * Returns the original type
     *
     * @return type
     */
    public function getType()
    {
        return $this->_type;
    }

    /**
     * Set a new type, and convert the value
     *
     * @param  string  $type  New type to set
     * @throws Zend_Measure_Exception
     */
    public function setType($type)
    {
        if (empty($this->_units[$type])) {
            require_once 'Zend/Measure/Exception.php';
            throw new Zend_Measure_Exception("Type ($type) is unknown");
        }

        if (empty($this->_type)) {
            $this->_type = $type;
        } else {

            // Convert to standard value
            $value = $this->getValue(-1);
            if (is_array($this->_units[$this->getType()][0])) {
                foreach ($this->_units[$this->getType()][0] as $key => $found) {
                    switch ($key) {
                        case "/":
                            if ($found != 0) {
                                $value = @call_user_func(Zend_Locale_Math::$div, $value, $found, 25);
                            }
                            break;
                        case "+":
                            $value = call_user_func(Zend_Locale_Math::$add, $value, $found, 25);
                            break;
                        case "-":
                            $value = call_user_func(Zend_Locale_Math::$sub, $value, $found, 25);
                            break;
                        default:
                            $value = call_user_func(Zend_Locale_Math::$mul, $value, $found, 25);
                            break;
                    }
                }
            } else {
                $value = call_user_func(Zend_Locale_Math::$mul, $value, $this->_units[$this->getType()][0], 25);
            }
            
            // Convert to expected value
            if (is_array($this->_units[$type][0])) {
                foreach (array_reverse($this->_units[$type][0]) as $key => $found) {
                    switch ($key) {
                        case "/":
                            $value = call_user_func(Zend_Locale_Math::$mul, $value, $found, 25);
                            break;
                        case "+":
                            $value = call_user_func(Zend_Locale_Math::$sub, $value, $found, 25);
                            break;
                        case "-":
                            $value = call_user_func(Zend_Locale_Math::$add, $value, $found, 25);
                            break;
                        default:
                            if ($found != 0) {
                                $value = @call_user_func(Zend_Locale_Math::$div, $value, $found, 25);
                            }
                            break;
                    }
                }
            } else {
                $value = @call_user_func(Zend_Locale_Math::$div, $value, $this->_units[$type][0], 25);
            }

            $this->_value = $value;
            $this->_type = $type;
        }
    }

    /**
     * Compare if the value and type is equal
     *
     * @param  Zend_Measure_Detailtype  $object  object to compare
     * @return boolean
     */
    public function equals($object)
    {
        if ((string) $object == $this->toString()) {
            return true;
        }

        return false;
    }

    /**
     * Returns a string representation
     *
     * @param  integer  $round  OPTIONAL rounds the value to an given exception
     * @return string
     */
    public function toString($round = -1)
    {
        return $this->getValue($round) . ' ' . $this->_units[$this->getType()][1];
    }

    /**
     * Returns a string representation
     *
     * @return string
     */
    public function __toString()
    {
        return $this->toString();
    }

    /**
     * Returns the conversion list
     *
     * @return array
     */
    public function getConversionList()
    {
        return $this->_units;
    }

    /**
     * Alias function for setType returning the converted unit
     *
     * @param $type   type
     * @param $round  integer  OPTIONAL rounds the value to a given precision
     * @return string
     */
    public function convertTo($type, $round = 2)
    {
        $this->setType($type);
        return $this->toString($round);
    }

    /**
     * Adds an unit to another one
     *
     * @param $object  object of same unit type
     * @return  Zend_Measure object
     */
    public function add($object)
    {
        $object->setType($this->getType());
        $value  = $this->getValue(-1) + $object->getValue(-1);

        $this->setValue($value, $this->getType(), $this->_locale);
        return $this;
    }

    /**
     * Substracts an unit from another one
     *
     * @param $object  object of same unit type
     * @return  Zend_Measure object
     */
    public function sub($object)
    {
        $object->setType($this->getType());
        $value  = $this->getValue(-1) - $object->getValue(-1);

        $this->setValue($value, $this->getType(), $this->_locale);
        return $this;
    }

    /**
     * Compares two units
     *
     * @param $object  object of same unit type
     * @return boolean
     */
    public function compare($object)
    {
        $object->setType($this->getType());
        $value  = $this->getValue(-1) - $object->getValue(-1);

        if ($value < 0) {
            return -1;
        } else if ($value > 0) {
            return 1;
        }

        return 0;
    }
}
