<?php
require_once dirname(__DIR__) . '/base/regression.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/basic/describe.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/normal.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/stdnormal.php';

class fourInOne extends regressionBase
{
    private $lang;

    private $fittedResult;

    private $result;

    private $residual;

    private $pointSize;

    public function __construct($fittedResult, $result, $lang)
    {
        global $config;
        $residual = array();
        foreach($fittedResult as $key => $value)
        {
            if($result[$key] and $fittedResult[$key])
            {
                $residual[] = $result[$key] - $fittedResult[$key];
            }
            else
            {
                $residual[] = NULL;
            }
        }

        $this->fittedResult = $fittedResult;
        $this->result       = $result;
        $this->residual     = $residual;
        $this->lang         = $lang;
        $this->pointSize    = $config->default->pointSize;
    }

    public function checkSD()
    {
        $sd = Describe::standard(Describe::notNull($this->residual));
        return $sd > 0 ? true : false;
    }

    public function normalData()
    {
        $notNullRes = Describe::notNull($this->residual);

        $mean      = Describe::mean($notNullRes);
        $sd        = Describe::standard($notNullRes);
        $n         = count($notNullRes);
        $normal    = new normal($mean, $sd);
        $cdfPoints = array();
        $scatters  = $notNullRes;

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

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

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

        $data   = array();
        $data[] = array('x' => $xpoints, 'y' => $ypoints, 'name' => $this->lang->fit, 'mode' => 'lines', 'type' => 'scatter');
        $data[] = array('x' => $scatters, 'y' => $cdfPoints, 'name' => $this->lang->data, 'mode' => 'markers', 'type' => 'scatter', 'marker' => array('size' => $this->pointSize));

        return $data;
    }

    public function scatterData()
    {
        return array(array('x' => $this->fittedResult, 'y' => $this->residual, 'xaxis' => 'x2', 'yaxis' => 'y2', 'name' => $this->lang->data, 'mode' => 'markers', 'type' => 'scatter', 'marker' => array('size' => $this->pointSize)));
    }

    public function histogramData()
    {
        $notNullRes = Describe::notNull($this->residual);
        /* Histogram */
        list($min, $max, $unit) = self::getRange($notNullRes);

        return array(array('name' => 'normal', 'xaxis' => 'x3', 'yaxis' => 'y3', 'x' => Describe::notNull($this->residual), 'autobinx' => false, 'xbins' => array('end' => $max, 'start' => $min, 'size' => $unit), 'name' => $this->lang->data, 'type' => 'histogram', 'marker' => array('color' => '#7ba8ed', 'line' => array('color' => '#e3e3e3', 'width' => 1))));
    }

    public function lineData()
    {
        $lineKeys  = array();
        $lineValue = $this->residual;
        foreach($lineValue as $key => $value)
        {
            if(!Describe::checkNotNull($value))
            {
                unset($lineValue[$key]);
            }
            else
            {
                $lineKeys[] = $key;
            }
        }
        $lineValue = array_values($lineValue);

        return array(array('x' => $lineKeys, 'y' => $lineValue, 'xaxis' => 'x4', 'yaxis' => 'y4', 'name' => $this->lang->data, 'type' => 'scatter', 'marker' => array('size' => $this->pointSize)));
    }

    public function getSubplot()
    {
        $data = array();
        $data = array_merge($data, $this->normalData());
        $data = array_merge($data, $this->scatterData());
        $data = array_merge($data, $this->histogramData());
        $data = array_merge($data, $this->lineData());

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

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

        $result->data['data'] = json_encode($data);
        $result->data['layout'] = array('grid' => array('rows' => 2, 'columns' => 2, 'pattern' => 'independent'));
        $result->data['layout']['xaxis'] = array('title' => $this->lang->normal);
        $result->data['layout']['yaxis'] = array('title' => $this->lang->percent, '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));
        $result->data['layout']['xaxis2'] = array('title' => array('text' => $this->lang->withFit, 'font' => array('size' => 12)));
        $result->data['layout']['yaxis2'] = array('title' => array('text' => $this->lang->residual, 'font' => array('size' => 12)));
        $result->data['layout']['xaxis3'] = array('title' => array('text' => $this->lang->histogram, 'font' => array('size' => 12)));
        $result->data['layout']['yaxis3'] = array('title' => array('text' => $this->lang->frequency, 'font' => array('size' => 12)));
        $result->data['layout']['xaxis4'] = array('title' => array('text' => $this->lang->withOrder, 'font' => array('size' => 12)));
        $result->data['layout']['yaxis4'] = array('title' => array('text' => $this->lang->residual, 'font' => array('size' => 12)));

        return $result;
    }

    public function getNormalplot()
    {
        $normalResult        = new stdclass();
        $normalResult->type  = 'chart';
        $normalResult->title = '';

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

        $data = $this->normalData();

        $normalResult->data['data'] = json_encode($data);
        $normalResult->data['layout'] = array('xaxis' => array('title' => $this->lang->residual), 'yaxis' => array('title' => $this->lang->percent));
        $normalResult->data['layout']['title'] = array('text' => $this->lang->normal, 'xref' => 'paper', 'yref' => 'paper', 'y' => 1);

        return self::config($normalResult);
    }

    public function getScatterplot()
    {
        $result = new stdclass();
        $result->type  = 'chart';
        $result->title = '';

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

        $data = $this->scatterData();

        $result->data['data']   = json_encode($data);
        $result->data['layout'] = array('xaxis' => array('title' => $this->lang->fitValue), 'yaxis' => array('title' => $this->lang->residual));
        $result->data['layout']['title'] = array('text' => $this->lang->withFit, 'xref' => 'paper', 'yref' => 'paper', 'y' => 1);
        //$result->data['layout']['annotations'] = array(array('showarrow' =>false, 'text' => $this->lang->withFit, 'xref' => 'paper', 'yref' => 'paper', 'y' => 0.9));

        return self::config($result);
    }

    public function getHistogramplot()
    {
        $histogramResult               = new stdclass();
        $histogramResult->type         = 'chart';
        $histogramResult->title        = '';
        $histogramResult->data         = array();
        $histogramResult->data['type'] = 'histogram';

        $histogramData = $this->histogramData();

        $histogramResult->data['data'] = json_encode($histogramData);
        $histogramResult->data['layout'] = array('xaxis' => array('title' => $this->lang->residual), 'yaxis' => array('title' => $this->lang->frequency));
        $histogramResult->data['layout']['title'] = array('text' => $this->lang->histogram, 'xref' => 'paper', 'yref' => 'paper', 'y' => 1);

        return self::config($histogramResult);
    }

    public function getLineplot()
    {
        $lineResult = new stdclass();
        $lineResult->type = 'chart';
        $lineResult->title = '';
        $lineResult->data = array();
        $lineResult->data['type'] = 'histogram';
        $lineData = $this->lineData();

        $lineResult->data['data'] = json_encode($lineData);
        $lineResult->data['layout'] = array('xaxis' => array('title' => $this->lang->observeOrder), 'yaxis' => array('title' => $this->lang->residual));
        $lineResult->data['layout']['title'] = array('text' => $this->lang->withOrder, 'xref' => 'paper', 'yref' => 'paper', 'y' => 1);

        return self::config($lineResult);
    }

    /**
     * Get range
     *
     * @param number $min
     * @param number $max
     * @access private
     * @return void
     */
    private static function getRange($arr)
    {
        $min = $arr[0];
        $max = $arr[0];
        $n = 0;
        foreach($arr as $value)
        {
            if(Describe::checkNotNull($value))
            {
                $n ++;
                $min = min($min, $value);
                $max = max($max, $value);
            }
        }

        $pad   = $max - $min;
        $sqrtN = ceil(sqrt($n));
        $sqrtN = $sqrtN < 5 ? 5 : $sqrtN;
        $unit  = $pad / $sqrtN;

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

    private static function config($obj)
    {
        global $dasLang;
        $grid   = $dasLang->config->gridConfig;
        $title  = $dasLang->config->titleConfig;
        $legend = $dasLang->config->legendConfig;
        $legend['defaultValue'] = 'true';
        $obj->data['config'] = array('grid' => $grid, 'title' => $title, 'legend' => $legend);

        return $obj;
    }

}
