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

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

    /* 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;
        $sample      = $settings['sample'];
        $sort        = $settings['sort'];
        $exceptLost  = $settings['exceptLost'];
        $exceptTotal = $settings['exceptTotal'];

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

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

        $data     = $dataframe->col($sample, 'number');
        $sortData = $dataframe->col($sort, 'any');
        $chartText = array('xname' => $dataframe->columns[$sort], 'yname' => $dataframe->columns[$sample], 'unsortX' => $sortData->trimdata);

        asort($sortData->trimdata);
        $sampleSortData = array();
        foreach($sortData->trimdata as $key => $value)
        {
            $sampleSortData[] = $data->data[$key];
        }

        $gom = new GompertzModel($dataframe, $sampleSortData);
        $describe = $gom->describe($exceptLost / 100, $exceptTotal);
        $chart    = $gom->chartData();

        if(!empty($exceptTotal))
        {
            if($exceptTotal != (int)$exceptTotal or $exceptTotal < 0) return self::error(sprintf($dasLang->gompertz->mustBeInteger, $dasLang->gompertz->exceptTotal));
        }
        $chartTitle = $dataframe->getTextResult($dasLang->gompertz->chartTitle);
        $chartTitle->children = 2;

        $result = array();
        $result[] = self::buildMethodResult();
        $result[] = self::buildStatisticResult($describe, $dataframe->columns[$sort], $dataframe->columns[$sample]);
        $result[] = self::buildKabResult($gom);
        $result[] = self::buildInflectionResult($describe);
        $result[] = self::buildTargetResult($describe, $exceptLost);
        $result[] = self::buildPredictedResult($describe);
        if(!empty($exceptTotal)) $result[] = self::buildCustomResult($gom, $describe);
        $result[] = $chartTitle;
        $result[] = self::buildCumulantChartResult($chart);
        $result[] = self::buildSingleChartResult($chart, $chartText);

        return $result;
    }

    public static function checkData($sort, $sample)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

        $sortCount = count($sort->notNull());
        $sampleCount = count($sample->notNull());

        /* sample column must be number */
        if(count($sample->trimdata) !== $sample->numCount) return self::error($lang->notNumberError);

        /* sort column must be not null */
        if(count($sort->trimdata) !== $sortCount) return self::error($lang->notNullError);

        /* sample column must be integer */
        foreach($sample->trimdata as $value)
        {
            if((int)$value != $value or $value < 0) return self::error($lang->notIntegerError);
        }

        /* sort column must be unique and can be sorted */
        $countValues = array_count_values($sort->trimdata);
        if(max($countValues) > 1) return self::error($lang->notUniqueError);

        /* check row is equal */
        if($sortCount != $sampleCount) return self::error($lang->rowNotEqualError);

        /* row count must >= 3 */
        if($sortCount < 3) return self::error($lang->lessThan3);

        return null;
    }

    public static function buildMethodResult()
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

        $result = new stdclass();
        $result->title = $lang->method;
        $result->type  = 'text';

        $result->data = new stdclass();
        $result->data->type = 'html';
        $result->data->content = $lang->LMType . $lang->gompertzType;

        return $result;
    }

    public static function buildStatisticResult($gom, $sort, $sample)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

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

        $result->data = new stdclass();
        $result->data->columns = array($lang->variable, $lang->N, $lang->cumulant, $lang->round);
        $result->data->data = array();
        $result->data->data[] = array($sample, $gom->N, $gom->total, '-');
        $result->data->data[] = array($sort, $gom->N, '-', $gom->N);

        return $result;
    }

    public static function buildKabResult($gom)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

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

        $result->data = new stdclass();
        $result->data->columns = array('Kab', $lang->estimated);
        $result->data->data = array();

        $result->data->data[] = array('K', $gom->K);
        $result->data->data[] = array('a', $gom->a);
        $result->data->data[] = array('b', $gom->b);

        return $result;
    }

    public static function buildInflectionResult($gom)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

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

        $result->data = new stdclass();
        $result->data->columns = array($lang->describe, $lang->value);
        $result->data->data = array();

        $result->data->data[] = array($lang->t, $gom->t);
        $result->data->data[] = array($lang->y, $gom->y);
        $result->data->data[] = array($lang->test, $gom->N);

        return $result;
    }

    public static function buildTargetResult($gom, $ratio)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

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

        $result->data = new stdclass();
        $result->data->columns = array($lang->describe, $lang->value);
        $result->data->data = array();

        $result->data->data[] = array($lang->wantLeave, $ratio . '%');
        $result->data->data[] = array($lang->found, $gom->total);
        $result->data->data[] = array($lang->shouldLeave, $gom->leave);

        return $result;
    }

    public static function buildPredictedResult($gom)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

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

        $result->data = new stdclass();
        $result->data->columns = array($lang->describe, $lang->value);
        $result->data->data = array();

        if(strtolower($gom->needTest) == 'nan') $gom->needTest = $lang->canNotPredict;

        $result->data->data[] = array($lang->shouldFound, $gom->target);
        $result->data->data[] = array($lang->targetDiff, $gom->diff);
        $result->data->data[] = array($lang->needTest, $gom->needTest);

        return $result;
    }

    public static function buildCustomResult($gom, $describe)
    {
        global $dasLang;
        $lang = $dasLang->gompertz;

        $result = new stdclass();
        $result->title = $lang->custom;
        $result->type  = 'table';
        $result->desc  = sprintf($lang->limitError, $describe->total, round($gom->K));

        $result->data = new stdclass();
        $result->data->columns = array($lang->describe, $lang->value);
        $result->data->data = array();

        $result->data->data[] = array($lang->customTotal, (string)$describe->customFlaw);
        $result->data->data[] = array($lang->customDiff, (string)$describe->customDiff);
        $result->data->data[] = array($lang->customTest, (string)$describe->customTest);

        return $result;
    }

    public static function buildCumulantChartResult($chart)
    {
        global $dasLang;
        global $config;
        $legend = $dasLang->gompertz->legend;
        $pointSize = $config->default->pointSize;

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

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

        $cumulant = new stdclass();
        $cumulant->key          = 'actual';
        $cumulant->x            = $chart->sort;
        $cumulant->y            = $chart->cumulant;
        $cumulant->name         = $legend->cumulant;
        $cumulant->line         = array('shape' => 'spline');
        $cumulant->mode         = 'lines+text';
        $cumulant->type         = 'scatter';
        $cumulant->textposition = 'top center';
        $cumulant->textfont     = array('color' => '#1f77b4');

        $cumulantPredicted = new stdclass();
        $cumulantPredicted->key          = 'predicted';
        $cumulantPredicted->x            = $chart->sort;
        $cumulantPredicted->y            = $chart->cumulantPrev;
        $cumulantPredicted->name         = $legend->cumulantPrev;
        $cumulantPredicted->type         = 'scatter';
        $cumulantPredicted->mode         = 'lines+text';
        $cumulantPredicted->marker       = array('size' => $pointSize);
        $cumulantPredicted->textposition = 'bottom center';
        $cumulantPredicted->textfont     = array('color' => '#ff7f0e');

        $data = array();
        $data[] = (array)$cumulant;
        $data[] = (array)$cumulantPredicted;
        $data[] = array('x' => $chart->sort, 'y' => $chart->limit, 'name' => $legend->limit, 'type' => 'scatter', 'marker' => array('size' => $pointSize));

        $result->data['data'] = json_encode($data);
        $result->data['layout'] = array('title' => array('text' => $dasLang->gompertz->cumulantTitle, 'yanchor' => 'top', 'y' => 0.86));
        $result->data['layout'] += array('xaxis' => array('title' => $dasLang->gompertz->serial), 'yaxis' => array('title' => $dasLang->gompertz->lost));

        $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, 'dataValue' => $dasLang->gompertz->chartLabel);

        return $result;
    }

    public static function buildSingleChartResult($chart, $chartText)
    {
        global $dasLang;
        global $config;
        $legend = $dasLang->gompertz->legend;
        $pointSize = $config->default->pointSize;

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

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

        $flaw = new stdclass();
        $flaw->key          = 'actual';
        $flaw->x            = $chart->sort;
        $flaw->y            = $chart->flaw;
        $flaw->name         = $legend->flaw;
        $flaw->type         = 'scatter';
        $flaw->mode         = 'lines+text';
        $flaw->marker       = array('size' => $pointSize);
        $flaw->textposition = 'top center';
        $flaw->textfont     = array('color' => '#1f77b4');

        $flawPredicted = new stdclass();
        $flawPredicted->key          = 'predicted';
        $flawPredicted->x            = $chart->sort;
        $flawPredicted->y            = $chart->singlePrev;
        $flawPredicted->name         = $legend->singlePrev;
        $flawPredicted->type         = 'scatter';
        $flawPredicted->mode         = 'lines+text';
        $flawPredicted->line         = array('shape' => 'spline');
        $flawPredicted->textposition = 'bottom center';
        $flawPredicted->textfont     = array('color' => '#ff7f0e');

        $datas = array();
        $data = (array)$flaw;
        $data = getHoverTemplateXY($data, array('xname' => $chartText['xname'], 'yname' => $chartText['yname'], 'custom' => getDisOrderCustom($chart->sort, $chartText['unsortX'])));
        $datas[] = $data;
        $datas[] = (array)$flawPredicted;

        $result->data['data'] = json_encode($datas);
        $result->data['layout'] = array('title' => array('text' => $dasLang->gompertz->singleTitle, 'yanchor' => 'top', 'y' => 0.86));
        $result->data['layout'] += array('xaxis' => array('title' => $dasLang->gompertz->serial), 'yaxis' => array('title' => $dasLang->gompertz->lost));

        $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, 'dataValue' => $dasLang->gompertz->chartLabel);

        return $result;
    }

    public static function error($message)
    {
        return array('result' => 'fail', 'message' => $message);
    }

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

        /* Basic settings */
        self::$settings['sort']        = array('name' => 'sort',        'label' => $lang->number,     'required' => true);
        self::$settings['sample']      = array('name' => 'sample',      'label' => $lang->sample,     'required' => true);
        self::$settings['exceptLost']  = array('name' => 'exceptLost',  'label' => $lang->exceptLost, 'required' => true);
        self::$settings['exceptTotal'] = array('name' => 'exceptTotal', 'label' => $lang->exceptTotal);

        /* Type settings */
        self::$settings['sample']      += array('type' => 'column', 'columnType' => 'number');
        self::$settings['sort']        += array('type' => 'column', 'columnType' => 'number');
        self::$settings['exceptLost']  += array('type' => 'number', 'defaultValue' => 5);
        self::$settings['exceptTotal'] += array('type' => 'number');

        /* Limit settings */
        self::$settings['sort']        += array('distinct' => 'sample');
        self::$settings['exceptLost']  += array('min' => 0.000001, 'max' => 99.999999);
        self::$settings['exceptTotal'] += array('min' => 1, 'max' => 99999999);

        /* Col grid settings */

        /* More settings */

        /* Data validate */
        self::$settings['sample'] += array('validate' => array('continuous', 'number', 'notCommon', 'int', 'nonnegative', 'rowEqual'));
        self::$settings['sort']   += array('validate' => array('continuous', 'number', '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;
    }
}
