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

/* Linear regression method 线性回归 */
class linearRegressionMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'linearRegression';

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

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

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

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

        $mlr = new MultipleRegression($dataframe, array($x->notNull()), $y->notNull());

        $data   = array();
        $result = array();

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

        $result[] = self::buildFormulaResult($dataframe, $xaxisName, $yaxisName, $mlr);
        $result[] = self::buildModelResult($mlr);
        $result[] = self::buildTableResult($dataframe, $mlr);

        $test = $mlr->getTest();
        if($test->hasTest) $result[] = self::buildTestResult($dataframe, $yaxisName, $xaxis, $yaxis, $test);

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

        $result[] = $chartTitle;
        $result[] = self::buildChartResult($dataframe, $mlr, $x, $y, $xaxisName, $yaxisName);

        if($settings['fourInOne'] == 'true') $result = array_merge($result, self::buildFourInOneResult($dataframe, $mlr, $y));

        return $result;
    }

    public static function checkData($x, $y)
    {
        $countX = array_unique($x);
        $countY = array_unique($y);

        return count($countX) != 1 and count($countY) != 1;
    }

    /**
     * Build formula result.
     *
     * @param object $dataframe
     * @param string $xName
     * @param string $yName
     * @param object $mlr
     * @access public
     * @return object
     */
    public static function buildFormulaResult($dataframe, $xName, $yName, $mlr)
    {
        global $dasLang;
        $lang = $dasLang->lineArregression;

        $formula = $mlr->getFourmula(array($xName), $yName);

        $result        = new stdclass();
        $result->type  = 'text';
        $result->title = $lang->textTitle;
        $result->data  = array('content' => $formula);

        return $result;
    }

    /**
     * Build model result.
     *
     * @param object $mlr
     * @access public
     * @return object
     */
    public static function buildModelResult($mlr)
    {
        global $dasLang;
        $lang = $dasLang->lineArregression;

        $model = $mlr->getModel();

        $result = new stdclass();
        $result->type  = 'table';
        $result->title = $lang->modelTitle;

        $result->data = array();

        $row = array($model->s, $model->rsq . ' %', $model->rsqAdj . ' %');

        $result->data['data'] = array($row);

        $result->data['columns'] = array();
        $result->data['columns'][] = array('label' => 'S', 'type' => 'number');
        $result->data['columns'][] = array('label' => 'R-Sq', 'type' => 'number');
        $result->data['columns'][] = array('label' => $lang->RsqAdjust, 'type' => 'number');

        return $result;
    }

    /**
     * Build table result.
     *
     * @param object $dataframe
     * @param object $mlr
     * @access public
     * @return object
     */
    public static function buildTableResult($dataframe, $mlr)
    {
        global $dasLang;
        $lang = $dasLang->lineArregression;

        $result = new stdclass();
        $result->type  = 'table';
        $result->title = $lang->tableTitle;

        $table = $mlr->getAnova();

        $result->data = array();
        $arr = array();
        $arr[] = array($lang->regression, $table->df, $table->ssRegr, $table->msRegr, $table->fRegr, $table->pRegr);
        $arr[] = array($lang->deviation, $table->dfError, $table->ssError, $table->msError, '', '');
        $arr[] = array($lang->total, $table->dfTotal, $table->ssTotal, '', '', '');
        $result->data['data'] = $arr;

        $result->data['columns'] = array();
        $result->data['columns'][] = array('label' => $lang->source, 'type' => 'text');
        $result->data['columns'][] = array('label' => $lang->freedom, 'type' => 'number');
        $result->data['columns'][] = array('label' => 'SS', 'type' => 'number');
        $result->data['columns'][] = array('label' => 'MS', 'type' => 'number');
        $result->data['columns'][] = array('label' => 'F', 'type' => 'number');
        $result->data['columns'][] = array('label' => 'P', 'type' => 'number');

        return $result;
    }

    /**
     * Build test result.
     *
     * @param object $dataframe
     * @param string $yName
     * @param object $mlr
     * @access public
     * @return object
     */
    public static function buildTestResult($dataframe, $yName, $xaxis, $yaxis, $test)
    {
        global $dasLang;
        $lang = $dasLang->multipleRegression;

        $result        = new stdclass();
        $result->type  = 'table';
        $result->title = $lang->testTitle;

        if(!empty($test->observed)) $result->extra = getHighlight(array($xaxis => $test->observed, $yaxis => $test->observed));

        $result->data = array();
        $arr = array();
        foreach($test->observed as $index => $value)
        {
            $arr[] = array($value, $test->value[$index], $test->fit[$index], $test->residual[$index], $test->stdResid[$index]);
        }
        $result->data['data'] = $arr;

        $result->data['columns']   = array();
        $result->data['columns'][] = array('label' => $lang->observed, 'type' => 'number');
        $result->data['columns'][] = array('label' => $yName, 'type' => 'number');
        $result->data['columns'][] = array('label' => $lang->fitValue, 'type' => 'number');
        $result->data['columns'][] = array('label' => $lang->residual, 'type' => 'number');
        $result->data['columns'][] = array('label' => $lang->sdResidual, 'type' => 'number');

        return $result;
    }

    public static function buildChartResult($dataframe, $mlr, $x, $y, $xaxis, $yaxis)
    {
        global $dasLang;
        $lang = $dasLang->lineArregression;

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

        $model = $mlr->getModel();

        $datas = array();
        $data = array('name' => $lang->data, 'x' => $x->trimdata, 'y' => $y->trimdata, 'mode' => 'markers', 'type' => 'scatter', 'marker' => array('size' => $dataframe->pointSize));
        $data = getHoverTemplateXY($data, array('xname' => $x->name, 'yname' => $y->name, 'custom' => getOrderCustom($x->trimdata)));
        $datas[] = $data;

        $xpoints = $x->data;
        $ypoints = $mlr->fitY();
        $datas[] = array('name' => $lang->matching, 'x' => $xpoints, 'y' => $ypoints, 'mode' => 'lines', 'type' => 'scatter');
        $test = $mlr->getTest();
        if($test->hasTest)
        {
            $xOutliers   = array();
            $yOutliers   = array();
            $orderCustom = array();

            foreach($test->observed as $item)
            {
                $xOutliers[]   = $x->trimdata[$item - 1];
                $yOutliers[]   = $y->trimdata[$item - 1];
                $orderCustom[] = $item;
            }
            $outlier = array('x' => $xOutliers, 'y' => $yOutliers, 'mode' => 'markers', 'name' => $lang->outlier, 'marker' => array('color' => '#F2470C', 'line' => array('width' => 3), 'opacity' => 0.5, 'size' => $dataframe->pointSize, 'symbol' => 'circle-open'));
            $outlier = getHoverTemplateXY($outlier, array('xname' => $x->name, 'yname' => $y->name, 'custom' => $orderCustom));
            $datas[] = $outlier;
        }

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

        $xrange = self::getRange($x->min, $x->max);
        $yrange = self::getRange($y->min, $y->max);
        $fourmula = self::buildFormulaResult($dataframe, $xaxis, $yaxis, $mlr);

        $result->data['layout'] = array('xaxis' => array('range' => $xrange, 'title' => $xaxis), 'yaxis' => array('range' => $yrange, 'title' => $yaxis));
        $result->data['layout']['title'] = array('text' => $fourmula, 'xref' => 'paper');
        $row = array($model->s, $model->rsq . ' %', $model->rsqAdj . ' %');
        $modelText = "S=$model->s    R-Sq=$model->rsq%    {$lang->RsqAdjust}=$model->rsqAdj%";
        $result->data['layout']['annotations'] = array(array('showarrow' => false, 'text' => $modelText, 'yref' => 'paper', 'y' => 1.1));

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

    public static function buildFourInOneResult($dataframe, $mlr, $y)
    {
        global $dasLang;
        $result = array();
        $fourInOneText   = $dataframe->getTextResult($dasLang->lineArregression->residual);

        $fittedResult = $mlr->fitY();

        $fourInOne = new fourInOne($fittedResult, $y->notNull(), $dasLang->fourInOne);

        if($fourInOne->checkSD())
        {
            $fourInOneText->children = 1;
            $result[] = $fourInOneText;
            $result[] = $fourInOne->getSubplot();
        }
        else
        {
            $errorResult        = new stdclass();
            $errorResult->type  = 'text';
            $errorResult->data  = array('content' => $dasLang->fourInOne->errorSD);

            $fourInOneText->children = 1;
            $result[]  = $fourInOneText;
            $result[]  = $errorResult;
        }

        return $result;
    }

    /**
     * Get range
     *
     * @param number $min
     * @param number $max
     * @access private
     * @return void
     */
    private static function getRange($min, $max)
    {
        $left  = $min - abs($max - $min)/10;
        $right = $max + abs($max - $min)/10;

        return array($left, $right);
    }

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

        /* 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['fourInOne'] = array('name' => 'fourInOne', 'label' => $lang->fourInOne);

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

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

        /* Col grid settings */
        self::$settings['fourInOne'] += 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'];
    }
}
