<?php

namespace loyalsoft;

/**
 * PHP中的枚举类型,
 * Abstract class that enables creation of PHP enums. All you
 * have to do is extend this class and define some constants.
 * Enum is an object with value from on of those constants
 * (or from on of superclass if any). There is also
 * __default constat that enables you creation of object
 * without passing enum value.
 *
 * @refer Marijan Šuflaj <msufflaj32@gmail.com&gt
 * @author gwang<mail@wanggangzero.cn>
 * @version
 *      2.0.0 修改代码去掉后期绑定的特定用法,改用5.3.0支持的语法.  gwang 2017.03.17
 *      1.0.0 参考了互联网上的资源,比如上面refer中提到的作者,源码的主体部分来自于他,
 *    结合了其他作者的功能, 调整了源码中的部分bug, 最终合并出来这个代码 gwang 2017.3.6
 */
abstract class Enum
{

    /**
     * Constant with default value for creating enum object
     */
    const __default = null;

    private $value;
    private $strict;
    private static $constants = array();

    /**
     * 获取预定义常量
     *
     * @param bool $includeDefault If true, default value is included into return
     * @return array Array with constant values
     */
    public function getConstList($includeDefault = false)
    {
        $class = get_class($this);

        if (!array_key_exists($class, self::$constants)) {
            $this->populateConstants();
        }

        return $includeDefault ? array_merge(self::$constants[$class], array(
                    "__default" => self::__default
                )) : self::$constants[$class];
    }

    /**
     * Creates new enum object. If child class overrides __construct(),
     * it is required to call parent::__construct() in order for this
     * class to work as expected.
     *
     * @param mixed $initialValue Any value that is exists in defined constants
     * @param bool $strict If set to true, type and value must be equal
     * @throws UnexpectedValueException If value is not valid enum value
     */
    public function __construct($initialValue = null, $strict = true)
    {
        $class = get_class($this);

        if (!array_key_exists($class, self::$constants)) {
            $this->populateConstants();
        }

        if ($initialValue === null) {
            $initialValue = self::$constants[$class]["__default"];
        }

        if ($initialValue instanceof Enum) {
            $initialValue = $initialValue->value;
        }

        $temp = self::$constants[$class];

        if (!in_array($initialValue, $temp, $strict)) {
            throw new UnexpectedValueException("Value is not in enum " . $class);
        }

        $this->value = $initialValue;
        $this->strict = $strict;
    }

    private function populateConstants()
    {
        if (self::$constants == NULL) {
            self::$constants = array();
        }
        $class = get_class($this);

        $r = new \ReflectionClass($class);
        self::$constants [$class] = $r->getConstants();
    }

    /**
     * 重载魔术方法: 返回一个枚举值的字符串表示. 如果value的类型是string, 默认返回value, 否则 返回其名称.
     * @return string
     */
    public function __toString()
    {

        if (is_string($this->value)) {
            return $this->value;
        }
        $constants = $this->getConstList();

        $str = array_search($this->value, $constants);
        if (false === $str) {
            return $this->value;
        }
        return $str;
    }

    public function isValidName($name, $strict = false)
    {
        $constants = $this->getConstList();
        if ($strict) {
            return array_key_exists($name, $constants);
        }
        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    }

    public function isValidValue($value, $strict = true)
    {
        $values = array_values($this->getConstList());
        return in_array($value, $values, $strict);
    }

}
