<?php
require_once LIB_ROOT . '/zendasmath/basic/describe.php';
require_once LIB_ROOT . '/zendasmath/distribution/normal.php';
require_once LIB_ROOT . '/zendasmath/distribution/stdnormal.php';
require_once LIB_ROOT . '/dataframe/hypothesis/normaltest.php';
require_once LIB_ROOT . '/dataframe/validatedata.php';
require_once LIB_ROOT . '/dataframe/checkdata.php';
require_once LIB_ROOT . '/dataframe/plotly.php';

class normalTestMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'normalTest';

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

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

    /* Callback for basic statistic method 基本量统计计算回调函数 */
    public static function func($dataframe, $settings)
    {
        global $dasLang;
        global $lang;

        $var      = $settings['var'];
        $method   = $dasLang->normalTest->typeEnum[$settings['type']];
        $percentX = $settings['percentX'];
        $percentY = $settings['percentY'];
        $dataType = self::columnType('var');

        $data = $dataframe->col($var, 'number');
        $name = $dataframe->columns[$var];

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

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

        $normalTest = new normalTest($dataframe, $data->trimdata);

        $n    = $normalTest->n;
        $mean = $normalTest->roundMean;
        $sd   = $normalTest->roundSd;

        /* Table */
        $tableData = array($name, $mean, $sd, $n);
        $columns = array(null, $dasLang->normalTest->mean, $dasLang->normalTest->sd, $dasLang->normalTest->n);

        if($method == 'Anderson-Darling')
        {
            $tableData[] = Describe::ASquare($data->trimdata);
            $tableData[] = Describe::PValue($data->trimdata);
            $columns[]   = $dasLang->normalTest->ad;
            $columns[]   = $dasLang->normalTest->p;
        }

        if($method == 'Ryan-Joiner')
        {
            $rj          = $normalTest->RyanJoiner();
            $tableData[] = $rj;
            $tableData[] = $normalTest->RyanJoinerP($rj, $n);
            $columns[]   = $dasLang->normalTest->rj;
            $columns[]   = $dasLang->normalTest->p;
        }

        if($method == 'Kolmogorov-Smirnov')
        {
            $tableData[] = $normalTest->KolmogorovSmirnov();
            // $tableData[] = $dataframe->KolmogorovSmirnovP($value, $n);
            $columns[]   = $dasLang->normalTest->ks;
            // $columns[]   = $dasLang->normalTest->p;
        }

        $tableResult = new stdclass();
        $tableResult->type  = 'table';
        $tableResult->title = $dataframe->getChartTitle($var, $lang->perfanalysis->methods->hypothesis['normalTest']);

        $tableResult->data            = array();
        $tableResult->data['data']    = array($tableData);
        $tableResult->data['columns'] = $columns;

        /* Chart */
        $normal    = new normal($mean, $sd);
        $cdfPoints  = array();
        $scatters   = $data->notNull();
        $unsortData = $data->notNull();

        $standardNormal = new standardNormal();
        sort($scatters);
        $a = 0.3; // fixed value,https://blog.csdn.net/csdn1b/article/details/119760820
        foreach($scatters as $index => $item)
        {
            $p = ($index + 1 - $a) / ($n + 1 - 2 * $a);
            $cdfPoints[] = $standardNormal->inverse($p);
        }

        $textResult = $dataframe->getTextResult($dataframe->getChartTitle($var, $dasLang->normalTest->normalChart));
        $textResult->children = 1;

        $chartResult        = new stdclass();
        $chartResult->type  = 'chart';
        $chartResult->title = '';

        $chartResult->data         = array();
        $chartResult->data['type'] = 'line';

        $xpoints = array();
        $ypoints = array();

        $startP = 0.01;
        $endP   = 0.99;
        $xpoints[] = $normal->inverse($startP);
        $xpoints[] = $normal->inverse($endP);

        // $standardNormal->inverse($startP);
        $ypoints[] = -2.326347014821;
        // $standardNormal->inverse($endP);
        $ypoints[] = 2.326347014821;

        $chartData[] = array('x' => $xpoints, 'y' => $ypoints, 'name' => $name, 'mode' => 'lines', 'type' => 'scatter');
        $data = array('x' => $scatters, 'y' => $cdfPoints, 'name' => $name, 'mode' => 'markers', 'type' => 'scatter', 'marker' => array('size' => $dataframe->pointSize));
        $data = getHoverTemplateX($data, array('xname' => $dataframe->columns[$var], 'custom' => getDisOrderCustom($scatters, $unsortData)));
        $chartData[] = $data;

        $layout      = array();
        $annotations = array();

        $layout['xaxis'] = array('title' => $name, 'range' => $xpoints);
        $layout['yaxis'] = array('title' => $dasLang->normalTest->percent, 'range' => $ypoints, 'tickmode' => 'array', 'ticktext' => array(1, 5, 10, 20, 50, 80, 90, 95, 99), 'tickvals' => array(-2.3263, -1.6448, -1.2815, -0.8416, 0, 0.8416, 1.2815, 1.6448, 2.3263));

        $incrementX = ($xpoints[1] - $xpoints[0]) / 10.0;
        // ($ypoints[1] - $ypoints[0]) / 10.0;
        $incrementY = 0.4652694029642;

        $beginX = $xpoints[0] - $incrementX;
        $beginY = $ypoints[0] - $incrementY;

        if($percentX && $percentX >= $xpoints[0] && $percentX <= $xpoints[1])
        {
            $x = (float)$percentX;
            $p = $normal->cdf($x);
            $y = $standardNormal->inverse($p);

            $chartData[] = array('x' => array($beginX, $x, $x), 'y' => array($y, $y, $beginY), 'name' => $dasLang->normalTest->percentX, 'mode' => 'lines', 'type' => 'scatter');

            $annotations[] = array('x' => $xpoints[0], 'y' => $y, 'xanchor' => 'left', 'yanchor' => 'bottom', 'text' => round($y, 1), 'showarrow' => false);
            $annotations[] = array('x' => $x, 'y' => $ypoints[0], 'xanchor' => 'left', 'yanchor' => 'bottom', 'text' => $x, 'showarrow' => false);
            $layout['annotations'] = $annotations;
        }

        if($percentY)
        {
            $p = $percentY / 100.0;
            $x = $normal->inverse($p);
            $y = $standardNormal->inverse($p);
            $chartData[] = array('x' => array($beginX, $x, $x), 'y' => array($y, $y, $beginY), 'name' => $dasLang->normalTest->percentY, 'mode' => 'lines', 'type' => 'scatter');

            $annotations[] = array('x' => $xpoints[0], 'y' => $y, 'xanchor' => 'left', 'yanchor' => 'bottom', 'text' => round($y, 1), 'showarrow' => false);
            $annotations[] = array('x' => $x, 'y' => $ypoints[0], 'xanchor' => 'left', 'yanchor' => 'bottom', 'text' => $x, 'showarrow' => false);
            $layout['annotations'] = $annotations;
        }

        $chartResult->data['data'] = json_encode($chartData);
        $chartResult->data['layout'] = $layout;

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

        $funcResult = array($tableResult, $textResult, $chartResult);

        if($percentX && ($percentX < $xpoints[0] || $percentX > $xpoints[1]))
        {
            $textResult->children = 2;
            $errorResult = new stdclass();
            $errorResult->title = '';
            $errorResult->type  = 'text';

            $errorResult->data = new stdclass();
            $errorResult->data->type = 'html';
            $errorResult->data->content = sprintf($dasLang->normalTest->percentXError, $percentX);

            $funcResult[] = $errorResult;
        }

        return $funcResult;
    }

    /**
     * Get settings.
     *
     * @access public
     * @return object
     */
    public static function getSettings()
    {
        global $dasLang;
        $lang = $dasLang->normalTest;

        /* Basic settings */
        self::$settings['var']      = array('name' => 'var',      'label' => $lang->var,  'required' => true);
        self::$settings['type']     = array('name' => 'type',     'label' => $lang->type, 'defaultValue' => 0);
        self::$settings['percentX'] = array('name' => 'percentX', 'label' => $lang->percentX);
        self::$settings['percentY'] = array('name' => 'percentY', 'label' => $lang->percentY);

        /* Type settings */
        self::$settings['var']      += array('type' => 'column', 'columnType' => 'number');
        self::$settings['type']     += array('type' => 'enum', 'enumOptions' => $lang->typeEnum, 'columnType' => 'any', 'defaultValue' => 'Anderson-Darling');
        self::$settings['percentX'] += array('type' => 'number', 'columnType' => 'number');
        self::$settings['percentY'] += array('type' => 'number', 'columnType' => 'number');

        /* Limit settings */
        self::$settings['percentY'] += array('min' => 1, 'max' => 99);

        /* Col grid settings */
        self::$settings['type']     += array('col' => 4);

        /* More settings */
        self::$settings['type']     += array('paramType' => 'more');
        self::$settings['percentX'] += array('paramType' => 'more');
        self::$settings['percentY'] += array('paramType' => 'more');

        /* Data validate */
        self::$settings['var'] += 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 Setting column type.
     *
     * @param string $index
     * @access public
     * @return string
     */
    public static function columnType($index)
    {
        return self::$settings[$index]['columnType'];
    }
}
