<?php

/**
 * ValidateData
 */
class ValidateData
{
    public $dataSet;
    public $result;
    public $message;
    public $status;
    public $dataType;
    public $rowCount;
    public $empty;
    public $number;
    public $int;
    public $text;
    public $numberAndText;
    public $existEmpty;
    public $continuous;
    public $common;
    public $hasCommon;
    public $positive;
    public $unique;
    public $emptyColumn;
    public $hasEmptyCol;
    public $rowCountEqual;
    public $minLength;

    public static $success = 'success';
    public static $fail    = 'fail';

    /**
     * 构造函数
     *
     * @param array $data
     * @access public
     * @return void
     */
    public function __construct($data)
    {
        $this->setResult();
        $this->empty         = true;
        $this->number        = false;
        $this->int           = false;
        $this->text          = false;
        $this->numberAndText = false;
        $this->existEmpty    = true;
        $this->continuous    = false;
        $this->common        = false;
        $this->hasCommon     = false;
        $this->positive      = false;
        $this->unique        = false;
        if(!is_array($data) or count($data) == 0)
        {
            $this->emptyColumn = true;
            return;
        }

        $firstItem = current($data);
        if(is_array($data) and is_array($firstItem))
        {
            $this->dataSet = $data;
        }

        if(is_array($data) and !is_array($firstItem))
        {
            $this->dataSet = array($data);
        }

        $this->toString();

        $this->getDataType();
        $this->getLength();

        $this->rowCountEqual = true;

        $this->empty         = $this->isEmpty();
        $this->number        = !$this->empty && $this->isAllNumber();
        $this->int           = !$this->empty && $this->isInteger();
        $this->text          = !$this->empty && $this->isAllText();
        $this->numberAndText = !$this->empty && $this->isNumberAndText();
        $this->existEmpty    = $this->isExistEmpty();
        $this->continuous    = !$this->existEmpty;
        $this->common        = $this->isCommon();
        $this->hasCommon     = (count(array_filter($this->common)) > 0);
        $this->positive      = $this->isPositive();
        $this->unique        = $this->isUnique();

        $rowValues = array_count_values($this->rowCount);

        $this->minLength     = min($this->rowCount);
        $this->rowCountEqual = (count($rowValues) == 1);
    }

    /**
     * Verify dataset item is no repeat method.
     *
     * @access public
     * @return bool
     */
    public function isUnique()
    {
        foreach($this->dataSet as $name => $data)
        {
            $unique = array_count_values($data);
            if(count($unique) != count($data)) return false;
        }

        return true;
    }

    /**
     * Verify dataset item is all positive method.
     *
     * @access public
     * @return bool
     */
    public function isPositive()
    {
        if(!$this->isAllNumber()) return false;
        foreach($this->dataSet as $name => $data)
        {
            foreach($data as $value)
            {
                if($value <= 0) return false;
            }
        }

        return true;
    }

    /**
     * Group data by group column method.
     *
     * @param string $key
     * @access public
     * @return object
     */
    public function group($key)
    {
        $samples = array();
        foreach($this->dataSet as $name => $data)
        {
            if($name == $key) $group = $data;
            if($name != $key) $samples[] = $data;
        }

        $groupUnique = array_count_values($group);
        $result            = new stdclass();
        $result->length    = array();
        $result->data      = array();
        $result->minLength = array();
        foreach($samples as $name => $sample)
        {
            $length = array();
            $data   = array();

            foreach($sample as $index => $value)
            {
                $groupKey  = $group[$index];
                if(!isset($length[$groupKey])) $length[$groupKey] = 0;
                if(!isset($data[$groupKey])) $data[$groupKey] = array();

                $length[$groupKey] += 1;
                $data[$groupKey][] = $value;
            }

            $result->length[$name]    = $length;
            $result->data[$name]      = $data;
            $result->minLength[$name] = min($length);
        }

        $result->allMinLength = min($result->minLength);

        return $result;
    }

    /**
     * count dataset unique values method.
     *
     * @access public
     * @return array
     */
    public function countValues()
    {
        $count = array();
        foreach($this->dataSet as $name => $data)
        {
            $unique = array_count_values($data);
            $count[$name] = count($unique);
        }

        return $count;
    }

    /**
     * Verify dataset items is common method.
     *
     * @access public
     * @return array
     */
    public function isCommon()
    {
        $common     = array();
        foreach($this->dataSet as $name => $data)
        {
            $unique = array_count_values($data);
            $common[$name] = (count($unique) == 1);
        }

        return $common;
    }

    /**
     * Verify dataset item is exist empty method.
     *
     * @access public
     * @return bool
     */
    public function isExistEmpty()
    {
        $flag = false;
        foreach($this->dataType as $types)
        {
            $flag = ($flag or in_array('empty', $types));
        }
        return $flag;
    }

    /**
     * Verify dataset item is all number method.
     *
     * @access public
     * @return bool
     */
    public function isAllNumber()
    {
        $flag = true;
        foreach($this->dataType as $types)
        {
            $types = array_filter($types, function($t)
            {
                return $t != 'empty';
            });
            $countValues = array_count_values($types);
            $flag &= (count($countValues) == 1 and current(array_keys($countValues)) == 'number');
        }
        return $flag;
    }

    /**
     * Verify dataset item is all text method.
     *
     * @access public
     * @return bool
     */
    public function isAllText()
    {
        $flag = true;
        foreach($this->dataType as $types)
        {
            $types = array_filter($types, function($t)
            {
                return $t != 'empty';
            });
            $countValues = array_count_values($types);
            $flag &= (count($countValues) == 1 and current(array_keys($countValues)) == 'text');
        }
        return $flag;
    }

    /**
     * Verify dataset item is all integer method.
     *
     * @access public
     * @return bool
     */
    public function isInteger()
    {
        if(!$this->isAllNumber()) return false;
        foreach($this->dataSet as $name => $data)
        {
            $data = array_filter($data, function($d)
            {
                return $d !== '';
            });
            foreach($data as $value)
            {
                if(floor($value) != $value) return false;
            }
        }

        return true;
    }

    /**
     * Verify dataset item is number and text method.
     *
     * @access public
     * @return bool
     */
    public function isNumberAndText()
    {
        $flag = true;
        foreach($this->dataType as $types)
        {
            $types = array_filter($types, function($t)
            {
                return $t != 'empty';
            });
            $countValues = array_count_values($types);
            $flag &= (in_array('number', $types) and in_array('text', $types));
        }
        return $flag;
    }

    /**
     * Verify dataset is empty (does not contain any other datatype).
     *
     * @access public
     * @return bool
     */
    public function isEmpty()
    {
        $flag = true;
        foreach($this->dataType as $types)
        {
            if(!(count(array_unique($types)) == 1 && current($types) == 'empty'))
            {
                $flag = false;
                break;
            }
        }
        return $flag;
    }

    /**
     * Parse dataset to data type array method.
     *
     * @access private
     * @return void
     */
    private function getDataType()
    {
        foreach($this->dataSet as $name => $data)
        {
            $dataType = array();
            foreach($data as $value)
            {
                $type = '';
                if(is_numeric($value)) $type = 'number';
                if(!is_numeric($value) and !empty($value)) $type = 'text';
                if(!is_numeric($value) and empty($value)) $type = 'empty';

                $dataType[] = $type;
            }

            $this->dataType[$name] = $dataType;
        }
    }

    /**
     * Covert data item to string method.
     *
     * @access private
     * @return void
     */
    private function toString()
    {
        $dataSet = array();
        foreach($this->dataSet as $name => $data)
        {
            $dataSet[$name] = array();
            foreach($data as $value)
            {
                if(is_numeric($value) or (!is_numeric($value) and !empty($value)))
                {
                    $dataSet[$name][] = "$value";
                }
                else
                {
                    $dataSet[$name][] = '';
                }
            }
        }

        $this->dataSet = $dataSet;
    }

    /**
     * Count all data to unique method.
     *
     * @access public
     * @return int
     */
    public function countAllValues()
    {
        $allData = array();
        foreach($this->dataSet as $data)
        {
            $allData = array_merge($allData, $data);
        }

        $values = array_count_values($allData);
        return count($values);
    }

    /**
     * Get dataset length method.
     *
     * @access public
     * @return void
     */
    public function getLength()
    {
        $this->hasEmptyCol = false;
        foreach($this->dataType as $name => $types)
        {
            $count = 0;
            $last  = null;
            foreach($types as $index => $type)
            {
                if($type != 'empty') continue;
                if($type == 'empty')
                {
                    if($last !== 'empty') $count = $index;
                    $last = $type;
                }
            }

            if(end($types) != 'empty') $count = count($types);

            if($count < count($types)) array_splice($this->dataType[$name], $count);

            if($count == 0) $this->hasEmptyCol = true;
            $this->rowCount[$name] = $count;
        }
    }

    /**
     * Set result method.
     *
     * @param string $message
     * @access public
     * @return void
     */
    public function setResult($message = null)
    {
        if($message == null)
        {
            $result  = self::$success;
            $message = '';
            $success = true;
        }
        else
        {
            $result  = self::$fail;
            $message = $message;
            $success = false;
        }
    }

    /**
     * Error result method.
     *
     * @param string $message
     * @access public
     * @return object
     */
    public static function result($message)
    {
        $convert = array('<=', '>=');
        $to      = array('{lt}', '{gt}');

        $message = preg_replace("/\r|\n/", "", $message);
        $message = str_replace($convert, $to, $message);
        $message = strip_tags($message);
        $message = str_replace($to, $convert, $message);

        return array('result' => self::$fail, 'message' => $message);
    }
}
