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

/* General linear model method 基本量统计 */
class generalLinearModelMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'general linear model';

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

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

    /* Callback for general linear model method 一般线性回归模型计算回调函数 */
    public static function func($dataframe, $settings)
    {
        global $dasLang;
        $lang = $dasLang->glm;
        $result = array();

        $response  = $settings['response'];
        $factor    = $settings['factor'];
        $covariate = $settings['covariate'];

        $emptyFactor    = is_array($factor) && (empty($factor) || (count($factor) == 1 && $factor[0] == ''));
        $emptyCovariate = is_array($covariate) && (empty($covariate) || (count($covariate) == 1 && $covariate[0] == ''));

        $check = new checkData();
        $check->setData($dataframe->colData($response), $dataframe->columns[$response], self::$settings['response']['validate']);
        if(!$emptyFactor)    $check->setDataList($dataframe->colsData($factor), $dataframe->colsName($factor), self::$settings['factor']['validate']);
        if(!$emptyCovariate) $check->setDataList($dataframe->colsData($covariate), $dataframe->colsName($covariate), self::$settings['covariate']['validate']);

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

        if($emptyFactor && $emptyCovariate) return array('result' => 'fail', 'message' => $lang->atLeastOneCol);
        $allColumn = array($response);
        if(!$emptyFactor)    $allColumn = array_merge($allColumn, $factor);
        if(!$emptyCovariate) $allColumn = array_merge($allColumn, $covariate);

        $sliceDF = $dataframe->sliceDataframe($dataframe, $allColumn, 'any');
        $responseData  = $sliceDF->col($response, 'number');
        $factorData    = !$emptyFactor ? $sliceDF->cols($factor, 'any') : array();
        $covariateData = !$emptyCovariate ? $sliceDF->cols($covariate, 'number') : array();

        $factorValues = GeneralLinearModel::factorValues($factorData);
        $covariateValues = GeneralLinearModel::covariateValues($covariateData);

        $glm = new GeneralLinearModel($sliceDF, $factorValues, $covariateValues, $responseData->data);

        if(!$glm->checkInvertible()) return array('result' => 'fail', 'message' => $dasLang->common->invertibleError);

        if($glm->hasFactor) $result[] = self::buildFactorResult($glm->factorLevel);

        $result[] = self::buildCoefResult($glm);
        $result[] = self::buildAnovaResult($glm);
        $result[] = self::buildFormulaResult($glm, $sliceDF->columns[$response]);
        if($glm->hasCovariate and $glm->hasFactor) $result[] = self::buildSingleFormulaResult($glm, $sliceDF->columns[$response]);
        $result[] = self::buildModelResult($glm);
        $test = $glm->getTest();
        $cols = array($response);
        if(!$emptyFactor)    $cols = array_merge($cols, $factor);
        if(!$emptyCovariate) $cols = array_merge($cols, $covariate);
        if($test->hasTest) $result[] = self::buildTestResult($test, $sliceDF->columns[$response], $cols);

        return $result;
    }

    public static function buildFactorResult($factors)
    {
        global $dasLang;
        $lang = $dasLang->glm;
        $result = new stdclass();
        $result->title = $lang->factorInfo;
        $result->type  = 'table';

        $result->data = new stdclass();
        $result->data->columns = array($lang->factor, $lang->type, $lang->level, $lang->value);
        $result->data->data    = array();
        foreach($factors as $name => $factor)
        {
            $result->data->data[] = array($name, $lang->fixed, $factor->level, implode(',', $factor->items));
        }

        return $result;
    }

    public static function buildCoefResult($glm)
    {
        global $dasLang;
        $lang = $dasLang->glm;

        $factor      = $glm->getFactor();

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

        $result->data = array();
        $arr = array();
        $arr[] = array($lang->common, $factor->coef[0], $factor->se[0], $factor->t[0], $factor->p[0]);

        $count = 1;

        if($glm->hasCovariate)
        {
            foreach($glm->covariate as $name => $values)
            {
                $arr[] = array($name, $factor->coef[$count], $factor->se[$count], $factor->t[$count], $factor->p[$count]);
                $count += 1;
            }
        }

        if($glm->hasFactor)
        {
            $factorLevel = $glm->factorLevel;
            foreach($factorLevel as $name => $fLv)
            {
                $arr[] = array($name, null, null, null, null);
                $items = $fLv->items;
                array_pop($items);
                foreach($items as $item)
                {
                    $arr[] = array($item, $factor->coef[$count], $factor->se[$count], $factor->t[$count], $factor->p[$count]);
                    $count += 1;
                }
            }
        }

        $result->data['data'] = $arr;

        $result->data['columns'] = array();
        $result->data['columns'][] = array('label' => $lang->item, 'type' => 'text');
        $result->data['columns'][] = array('label' => $lang->factor, 'type' => 'number');
        $result->data['columns'][] = array('label' => $lang->factorError, 'type' => 'number');
        $result->data['columns'][] = array('label' => 'T', 'type' => 'number');
        $result->data['columns'][] = array('label' => 'P', 'type' => 'number');
        // $result->data['columns'][] = array('label' => $lang->factorAnova, 'type' => 'number');

        return $result;
    }

    public static function buildAnovaResult($glm)
    {
        global $dasLang;
        $lang = $dasLang->glm;

        $table = $glm->getAnova();

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

        $result->data = array();
        $arr = array();
        $arr[] = array($lang->regression, $table->df, $table->ssRegr, $table->msRegr, $table->fRegr, $table->pRegr);

        $count = 0;

        if($glm->hasCovariate)
        {
            foreach($glm->covariate as $name => $values)
            {
                $arr[] = array($name, $table->itemDf[$count], $table->itemSS[$count], $table->itemMS[$count], $table->itemF[$count], $table->itemP[$count]);
                $count += 1;
            }
        }

        if($glm->hasFactor)
        {
            foreach($glm->factors as $name => $factor)
            {
                $arr[] = array($name, $table->itemDf[$count], $table->itemSS[$count], $table->itemMS[$count], $table->itemF[$count], $table->itemP[$count]);
                $count += 1;
            }
        }

        $arr[] = array($lang->deviation, $table->dfError, $table->ssError, $table->msError, '', '');

        if($table->hasSame)
        {
            $arr[] = array($lang->lostFit, $table->dfLOF, $table->ssLOF, $table->msLOF, $table->fLOF, $table->pLOF);
            $arr[] = array($lang->pureError, $table->dfPE, $table->ssPE, $table->msPE, '', '');
        }

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

    public static function buildFormulaResult($glm, $yaxis)
    {
        global $dasLang;
        $lang = $dasLang->glm;

        $formulas = $glm->getFormula($yaxis);
        $result = new stdclass();
        $result->type  = 'table';
        $result->title = $lang->textTitle;
        $result->data  = array();
        $arr = array();
        foreach($formulas as $name => $formula)
        {
            $item = array();
            if($glm->hasFactor and $glm->hasCovariate)
            {
                $item[] = $name;
            }
            $item[] = $formula;
            $arr[]  = $item;
        }

        $result->data['data'] = $arr;
        $result->data['columns'] = array();
        if($glm->hasFactor and $glm->hasCovariate)
        {
            $result->data['columns'][] = array('label' => $lang->levelGroup, 'type' => 'text');
        }
        $result->data['columns'][] = array('label' => $lang->regression, 'type' => 'text');

        return $result;
    }

    public static function buildSingleFormulaResult($glm, $yaxis)
    {
        global $dasLang;
        $lang = $dasLang->glm;

        $formula = $glm->getSingleFormula($yaxis);
        $result = new stdclass();
        $result->type  = 'table';
        $result->data  = array();
        $arr = array(array($formula));

        $result->data['data'] = $arr;
        $result->data['columns'] = array();
        $result->data['columns'][] = array('label' => $lang->regression, 'type' => 'text');

        return $result;
    }

    public static function buildModelResult($glm)
    {
        global $dasLang;
        $lang = $dasLang->glm;

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

        $result->data = array();
        $model = $glm->getModel();

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

        $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');
        $result->data['columns'][] = array('label' => $lang->RsqPre, 'type' => 'number');

        return $result;
    }

    public static function buildTestResult($test, $yName, $cols)
    {
        global $dasLang;
        $lang = $dasLang->glm;

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

        if(!empty($test->observed))
        {
            $points = array();
            foreach($cols as $col) $points[$col] = $test->observed;
            $result->extra = getHighlight($points);
        }

        $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 checkData($response, $factor, $covariate)
    {
        global $dasLang;
        $lang = $dasLang->glm;

        $count = count($response->notNull());
        $result = new stdclass();
        $result->success = true;

        foreach($factor as $data)
        {
            /* check row length is equal or not */
            $dataCount = count($data->notNull());
            if($count != $dataCount)
            {
                $result->success = false;
                $result->result = array('result' => 'fail', 'message' => $lang->errorRowEqual);
                return $result;
            }

            if($dataCount != count($data->data))
            {
                $result->success = false;
                $result->result = array('result' => 'fail', 'message' => $lang->errorNull);
                return $result;
            }

            /* check factor have at least two classify */
            $classify = array_count_values($data->data);
            if(count($classify) <= 1)
            {
                $result->success = false;
                $result->result = array('result' => 'fail', 'message' => $lang->errorClassify);
                return $result;
            }
        }

        foreach($covariate as $data)
        {
            /* check row length is equal or not */
            $dataCount = count($data->originData);
            if($count != $dataCount)
            {
                $result->success = false;
                $result->result = array('result' => 'fail', 'message' => $lang->errorRowEqual);
                return $result;
            }

            /* check is all number */
            if($data->numCount != $dataCount)
            {
                $result->success = false;
                $result->result  = array('result' => 'fail', 'message' => sprintf($lang->errorIsNumber, $lang->covariateCol));
                return $result;
            }
        }

        return $result;
    }

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

        /* Basic settings */
        self::$settings['response']  = array('name' => 'response',  'label' => $lang->response, 'required' => true);
        self::$settings['factor']    = array('name' => 'factor',    'label' => $lang->factor);
        self::$settings['covariate'] = array('name' => 'covariate', 'label' => $lang->covariate);

        /* Type settings */
        self::$settings['response']  += array('type' => 'column', 'columnType' => 'number');
        self::$settings['factor']    += array('type' => 'list', 'listType' => 'column', 'columnType' => 'any');
        self::$settings['covariate'] += array('type' => 'list', 'listType' => 'column', 'columnType' => 'number');

        /* Limit settings */
        self::$settings['response']  += array('distinct' => 'factor,covariate');
        self::$settings['factor']    += array('distinct' => 'response,covariate');
        self::$settings['covariate'] += array('distinct' => 'factor,response');

        /* Col grid settings */

        /* More settings */

        /* Data validate */
        self::$settings['response']  += array('validate' => array('continuous', 'number', 'notCommon', 'rowEqual'));
        self::$settings['factor']    += array('validate' => array('continuous', 'notCommon', 'columnUnique >= 2', 'rowEqual'));
        self::$settings['covariate'] += 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;

        //return self::$config;
    }
}
