<?php
require_once LIB_ROOT . '/dataframe/regression/polynomial.php';
require_once LIB_ROOT . '/dataframe/checkdata.php';
require_once LIB_ROOT . '/dataframe/validatedata.php';
require_once LIB_ROOT . '/dataframe/plotly.php';

/* Polynomial regression method 多项式回归 */
class polynomialRegressionMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'polynomialRegression';

    /* 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;

        $xaxis  = $settings['xaxis'];
        $yaxis  = $settings['yaxis'];
        $degree = $settings['degree'];

        $yaxisName = $dataframe->columns[$yaxis];
        $xaxisName = $dataframe->columns[$xaxis];

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

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

        $x = $dataframe->col($xaxis, 'number');
        $y = $dataframe->col($yaxis, 'number');
        $points = array($x->trimdata, $y->trimdata);

        $regr   = new Polynomial($dataframe, $points, $degree);
        // if(!$regr->regr->isInvertible) return array('result' => 'fail', 'message' => $dasLang->common->invertibleError);
        // $params = $regr->regr->getParameters();

        $data = array();

        /* Text. */
        $formula = $regr->getFormula($xaxisName, $yaxisName);

        $textResult = new stdclass();
        $textResult->type  = 'text';
        $textResult->title = $dasLang->polynomialRegression->textTitle;
        $textResult->data  = array('content' => $formula);

        /* Model. */
        $model = $regr->getModel();
        $modelResult = new stdclass();
        $modelResult->type  = 'table';
        $modelResult->title = $dasLang->polynomialRegression->modelTitle;

        $modelResult->data = array();

        $row = array($model->errorSd, $model->rsq . '%', $model->req . '%');

        $modelResult->data['data']    = array($row);
        $modelResult->data['columns'] = array('S', 'R-Sq', $dasLang->polynomialRegression->RsqAdjust);

        /* Table. */
        $degree = $settings['degree'];
        $anova  = $regr->getAnova($degree);
        $tableResult = new stdclass();
        $tableResult->type  = 'table';
        $tableResult->title = $dasLang->polynomialRegression->tableTitle;

        $tableResult->data = array();
        $arr = array();
        $arr[] = array($dasLang->polynomialRegression->regression, $anova->freedom, $anova->ss, $anova->ms, $anova->f, $anova->p);
        $arr[] = array($dasLang->polynomialRegression->deviation, $anova->freedomError, $anova->ssError, $anova->msError, '');
        $arr[] = array($dasLang->polynomialRegression->total, $anova->freedomTotal, $anova->ssTotal, '', '');
        $tableResult->data['data'] = $arr;

        $tableResult->data['columns'] = array(array('label' => $dasLang->polynomialRegression->source, 'type' => 'text'), $dasLang->polynomialRegression->freedom, 'SS', 'MS', 'F', 'P');

        $chartTitle = $dataframe->getTextResult($dasLang->polynomialRegression->chartTitle);
        $chartTitle->children = 1;

        /* Chart. */
        $chartResult = new stdclass();
        $chartResult->type  = 'chart';
        $chartResult->title = '';
        $chartResult->data = array();
        $chartResult->data['type'] = 'scatter';

        $datas = array();
        list($xsort, $ysort) = sortArray($x->trimdata, $y->trimdata);
        $data = array('name' => $dasLang->polynomialRegression->data, 'x' => $xsort, 'y' => $ysort, 'mode' => 'markers', 'type' => 'scatter', 'marker' => array('size' => $dataframe->pointSize));
        $data = getHoverTemplateXY($data, array('xname' => $x->name, 'yname' => $y->name, 'custom' => getDisOrderCustom($xsort, $x->trimdata)));
        $datas[] = $data;

        $xpoints = array();
        $ypoints = array();
        for($point = $x->min; $point <= $x->max; $point += ($x->max - $x->min) / 100)
        {
            $xpoints[] = $point;
            $ypoints[] = $regr->evaluate($point);
        }
        $datas[] = array('name' => $dasLang->polynomialRegression->matching, 'x' => $xpoints, 'y' => $ypoints, 'line' => array('shape' => 'spline'), 'mode' => 'lines', 'type' => 'scatter');

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

        $chartResult->data['layout'] = array('xaxis' => array('title' => $xaxisName), 'yaxis' => array('title' => $yaxisName));

        $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);

        $titleResult = $dataframe->getTextResult($dataframe->getChartTitle(array('x' => $xaxis, 'y' => $yaxis), $lang->perfanalysis->methods->analysis['polynomialRegression'], 'versus'));

        return array($titleResult, $textResult, $modelResult, $tableResult, $chartTitle, $chartResult);
    }

    /**
     * Get range
     *
     * @param number $min
     * @param number $max
     * @access private
     * @return void
     */
    private static function getRange($min, $max)
    {
        $pad = $max - $min;
        $margin = 1;
        $unit   = 1;
        for($n = -2; $n < 100; $n++)
        {
            if(pow(10, $n) > $pad)
            {
                $margin = pow(10, $n);
                $unit = $margin / 10;
                break;
            }

            if(2 * pow(10, $n) > $pad)
            {
                $margin = 2 * pow(10, $n);
                $unit = $margin / 2;
                break;
            }

            if(5 * pow(10, $n) > $pad)
            {
                $margin = 5 * pow(10, $n);
                $unit = $margin / 5;
                break;
            }
        }

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

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

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

        /* Type settings */
        self::$settings['xaxis']  += array('type' => 'column', 'columnType' => 'number');
        self::$settings['yaxis']  += array('type' => 'column', 'columnType' => 'number');
        self::$settings['degree'] += array('type' => 'enum', 'enumOptions' => $lang->degreeEnum, 'defaultValue' => '2');

        /* Limit settings */
        self::$settings['yaxis']  += array('distinct' => 'xaxis');

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

        /* More settings */

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

        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'];
    }
}
