<?php
require_once LIB_ROOT . '/zendasmath/basic/describe.php';
require_once LIB_ROOT . '/dataframe/stat/graph.php';
require_once LIB_ROOT . '/dataframe/validatedata.php';
require_once LIB_ROOT . '/dataframe/checkdata.php';
require_once LIB_ROOT . '/dataframe/plotly.php';

/* Graph Summary method 基本量统计 */
class graphSummaryMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'graphSummary';

    /* Method settings 方法设置参数定义 */
    public static $settings = array();

    /* Diagram config 图表配置项 */
    public static $config = array();

    /* Callback for graph summary method 图形化汇总计算回调函数 */
    public static function func($dataframe, $settings)
    {
        global $dasLang;
        global $lang;

        $xaxis     = $settings['xaxis'];
        $summary   = $settings['summary'];
        $graph     = $settings['graph'];
        $xaxisName = $dataframe->columns[$xaxis];
        $data = array();

        $check = new checkData();
        $check->setData($dataframe->colData($xaxis), $dataframe->columns[$xaxis], self::$settings['xaxis']['validate']);

        $checkRes = $check->check();
        if(is_string($checkRes)) return ValidateData::result($checkRes);

        $result = array();

        $sliceDF = $dataframe->sliceDataframe($dataframe, $settings['xaxis'], 'any');
        $x       = $sliceDF->col($settings['xaxis'], 'number');

        if(!$x->numCount)
        {
            $errorResult = new stdclass();
            $errorResult->type    = 'error';
            $errorResult->data    = new stdclass();
            $errorResult->data->error   = 'illegal';
            $errorResult->data->message = self::$settings['xaxis']['label'] . ': ' . sprintf($dasLang->common->columnNoNumber, 'C' . ($settings['xaxis'] + 1));
            return array($errorResult);
        }

        /* summary  */
        $result[] = self::buildSummaryResult($summary, $x);

        $textResult = $dataframe->getTextResult($dataframe->getChartTitle($xaxis, $lang->perfanalysis->methods->statistic['graphSummary']));
        $textResult->children = $graph === false ? 0 : count($graph);

        $result[] = $textResult;

        /* graph  */
        if($graph !== false)
        {
            if(in_array('normal', $graph)) $result[] = self::buildNormalResult($dataframe, $x, $xaxisName);
            if(in_array('box', $graph))
            {
                list($boxResult, $box) = self::buildBoxResult($dataframe, $x, $xaxisName);
                $result[] = $boxResult;

                if(!empty($box->outliers['y']))
                {
                    $checkData[$xaxisName] = $box->outliers['y'];
                    $rows = array_keys($box->outliers['y']);
                    $rows = array_map(function($v){return $v + 1;}, $rows);
                    $testPoints = array($xaxis => $rows);
                    if(!empty($checkData[$xaxisName])) $result[] = self::buildOutlierResult($checkData, $testPoints);
                }
            }
        }

        return $result;
    }

    public static function buildSummaryResult($summary, $x)
    {
        global $dasLang;

        $result = new stdclass();
        $result->type = 'table';
        $result->title = $dasLang->graphSummary->summary->title;

        $columns = array();
        $data    = array();
        $rank    = 3;
        if($summary !== false)
        {
            $row = array();
            foreach($summary as $item)
            {
                $itemResult = self::getResult($x->notNull(), $item);

                if(count($row) >= $rank * 2)
                {
                    $data[] = $row;
                    $row = array($dasLang->graphSummary->summary->enum[$item], $itemResult);
                }
                else
                {
                    $row[] = $dasLang->graphSummary->summary->enum[$item];
                    $row[] = $itemResult;
                }
            }
            /* Completion row. */
            $leftRowCount = count($row);
            if(count($summary) >= 3)
            {
                for($i = 0; $i < 6 - $leftRowCount; $i ++) $row[] = '';
            }

            $data[] = $row;
        }

        for($index = 0; $index < $rank; $index++)
        {
            $columns[] = null;
            $columns[] = array('label' => $dasLang->graphSummary->summary->columnTitle, 'type' => 'number');
        }
        $result->data = array('data' => $data, 'columns' => $columns, 'header' => false);

        return $result;
    }

    /**
     * Build normal result.
     *
     * @param object $x
     * @param string $xaxisName
     * @access public
     * @return object
     */
    public static function buildNormalResult($dataframe, $x, $xaxisName)
    {
        global $dasLang;

        $result = new stdclass();
        $result->type  = 'chart';
        $result->id    = 'normal';
        $result->title = '';
        $result->data  = array();
        $result->data['type'] = 'histogram';
        $data = array();
        /* Histogram */
        list($min, $max, $unit) = self::getRange($x->min, $x->max, $x->numCount);

        $histogram = array();
        $histogram['name']     = $xaxisName;
        $histogram['x']        = $x->data;
        $histogram['autobinx'] = false;
        $histogram['type']     = 'histogram';
        $histogram['marker']   = array('color' => '#7ba8ed', 'line' => array('color' => '#e3e3e3', 'width' => 1));
        $histogram['xbins']    = array('end' => $max, 'start' => $min, 'size' => $unit);

        $data[] = $histogram;

        /* Line. */
        $graph = new graph($dataframe, $x->trimdata);
        list($xpoints, $ypoints) = $graph->normalFit($min, $max, $unit);

        $line = array();
        $line['name'] = $dasLang->normalDistribution->matching;
        $line['x']    = $xpoints;
        $line['y']    = $ypoints;
        $line['line'] = array('shape' => 'spline');
        $line['mode'] = 'lines';
        $line['type'] = 'scatter';

        $data[] = $line;

        $result->data['data']            = json_encode($data);
        $result->data['layout']          = array();
        $result->data['layout']['xaxis'] = array('title' => $xaxisName);
        $result->data['layout']['yaxis'] = array('title' => $dasLang->graphSummary->frequence);

        $grid   = $dasLang->config->gridConfig;
        $title  = $dasLang->config->titleConfig;
        $legend = $dasLang->config->legendConfig;
        $legend['defaultValue'] = 'true';
        $result->data['config'] = array('grid' => $grid, 'title' => $title, 'legend' => $legend);

        return $result;
    }

    /**
     * Build box result.
     *
     * @param object $dataframe
     * @param object $x
     * @param string $xaxisName
     * @access public
     * @return object
     */
    public static function buildBoxResult($dataframe, $x, $xaxisName)
    {
        global $dasLang;

        $result = new stdclass();
        $result->type  = 'chart';
        $result->id    = 'box';
        $result->title = '';
        $result->data  = array();
        $result->data['type'] = 'box';
        $data = array();

        $box  = Describe::box($x->trimdata, $xaxisName);

        $boxData = array();
        $boxData['hoverinfo'] = 'y';

        $boxData['x']         = array($xaxisName);
        $boxData['q1']        = array($box->q1);
        $boxData['q3']        = array($box->q3);
        $boxData['median']    = array($box->q2);
        $boxData['lowerfence'] = array($box->lowerfence);
        $boxData['upperfence'] = array($box->upperfence);
        $boxData['name']       = $xaxisName;
        $boxData['type']       = 'box';
        $boxData['marker']     = array('symbol' => 'asterisk-open');

        $outliers = getHoverTemplateY(array(), array('yname' => $box->name, 'custom' => getDisOrderCustom(array_values($box->outliers['y']), $box->data)));
        $outliers['name']      = $dasLang->box->outlier;
        $outliers['y']         = array_values($box->outliers['y']);
        $outliers['x']         = array_values($box->outliers['x']);
        $outliers['mode']      = 'markers';
        $outliers['type']      = 'scatter';
        $outliers['marker']    = array('symbol' => 'asterisk-open');

        $data[] = $boxData;
        $data[] = $outliers;

        $result->data['data'] = json_encode($data);
        $grid   = $dasLang->config->gridConfig;
        $legend = $dasLang->config->legendConfig;
        $legend['defaultValue'] = 'true';
        $result->data['config'] = array('grid' => $grid, 'legend' => $legend);

        return array($result, $box);
    }

    public static function buildOutlierResult($checkData, $testPoints)
    {
        global $dasLang;

        $html = '';

        foreach($checkData as $key => $points)
        {
            $ruleName = $key . $dasLang->box->outlier;
            $html .= "<p>{$ruleName}</p>";
            $html .= '<pre>' . implode(', ', $points) . '</pre>';
        }

        $highlight = getHighlight($testPoints);
        $html = "<h4>{$dasLang->box->outlier} $highlight</h4>" . $html;

        $textResult = new stdclass();

        $textResult->type  = 'text';
        $textResult->title = '';
        $textResult->data  = array();
        $textResult->data['content'] = $html;
        $textResult->data['type']    = 'html';
        return $textResult;
    }

    public static function getResult($data, $type)
    {
        $result = null;
        switch($type)
        {
            case 'count':
                $result = count($data);
                break;
            case 'mean':
                $result = Describe::mean($data);
                break;
            case 'max':
                $result = Describe::max($data);
                break;
            case 'min':
                $result = Describe::min($data);
                break;
            case 'sum':
                $result = Describe::sum($data);
                break;
            case 'mid':
                $result = Describe::percentData($data, 0.5);
                break;
            case 'quart1':
                $result = Describe::percentData($data, 0.25);
                break;
            case 'quart3':
                $result = Describe::percentData($data, 0.75);
                break;
            case 'quart3sub1':
                $result = Describe::percentData($data, 0.75) - Describe::percentData($data, 0.25);
                break;
            case 'percent10':
                $result = Describe::percentData($data, 0.1);
                break;
            case 'percent90':
                $result = Describe::percentData($data, 0.9);
                break;
            case 'standard':
                $result = Describe::standard($data);
                break;
            case 'variance':
                $result = Describe::variance($data);
                break;
            case 'skewness':
                $result = Describe::skewness($data);
                break;
            case 'kurtosis':
                $result = Describe::kurtosis($data);
                break;
            case 'Pvalue':
                $result = Describe::Pvalue($data);
                break;
        }

        return $result;
    }

    /**
     * Get settings.
     *
     * @param object $lang
     * @access public
     * @return object
     */
    public static function getSettings()
    {
        global $dasLang;
        global $app;

        $language = $app->getClientLang();
        $lang = $dasLang->graphSummary;

        /* Basic settings */
        self::$settings['xaxis']   = array('name' => 'xaxis',   'label' => $lang->xaxis,          'required' => true);
        self::$settings['summary'] = array('name' => 'summary', 'label' => $lang->summary->label, 'required' => true);
        self::$settings['graph']   = array('name' => 'graph',   'label' => $lang->graph->label,   'required' => true);

        /* Type settings */
        self::$settings['xaxis']   += array('type' => 'column', 'columnType' => 'number');
        self::$settings['summary'] += array('type' => 'list', 'listType' => 'enum', 'enumOptions' => $lang->summary->enum, 'defaultValue' => array_keys($lang->summary->enum), 'ui' => 'checkbox');
        self::$settings['graph']   += array('type' => 'list', 'listType' => 'enum', 'enumOptions' => $lang->graph->enum, 'defaultValue' => array_keys($lang->graph->enum));

        /* Limit settings */

        /* Col grid settings */
        self::$settings['summary'] += array('col' => 4);
        self::$settings['graph']   += array('col' => $language == 'en' ? 6 : 4);

        /* More settings */

        /* Data validate */
        self::$settings['xaxis'] += array('validate' => array('continuous', 'number', 'notCommon', 'N >= 2'));

        return self::$settings;
    }

    /**
     * Get config.
     *
     * @access public
     * @return object
     */
    public static function getConfig()
    {
        global $dasLang;
        $lang = $dasLang->config;
        self::$config['grid']   = $lang->gridConfig;
        self::$config['legend'] = $lang->legendConfig;

        return self::$config;
    }

    /**
     * Get range
     *
     * @param number $min
     * @param number $max
     * @access private
     * @return void
     */
    private static function getRange($min, $max, $n)
    {
        $pad   = $max - $min;
        $sqrtN = round(sqrt($n));
        $unit  = $pad / $sqrtN;

        return array((floor($min/$unit) - 2) * $unit, (ceil($max/$unit) + 2) * $unit, $unit);
    }
}
