<?php

namespace Ig\IgChart\Domain\Model;

use Ig\IgChart\Utility\ChartUtility;
use stdClass;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

class Chart extends AbstractEntity
{
    /**
     * type
     *
     * @var string
     */
    protected $id = '';

    /**
     * type
     *
     * @var string
     */
    protected $type = 'bar';

    /**
     * labels
     *
     * @var array
     */
    protected $labels = [];

    /**
     * data
     *
     * @var array
     */
    protected $data = [];
    /**
     * colors
     *
     * @var array
     */
    protected $colors = ['#e0440e', '#e6693e', '#ec8f6e', '#f3b49f', '#f6c7b6'];

    public function __construct()
    {
        $this->chartUtility = GeneralUtility::makeInstance(ChartUtility::class);
        $this->id = $this->chartUtility->getId();
    }

    /**
     * Returns the id
     *
     * @return string
     */
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Sets the id
     */
    public function setId(string $id): void
    {
        $this->id = $id;
    }

    /**
     * Returns the type
     *
     * @return string
     */
    public function getType(): string
    {
        return $this->type;
    }

    /**
     * Sets the type
     */
    public function setType(string $type): void
    {
        $this->type = $type;
    }

    /**
     * Returns the labels
     *
     * @return aray
     */
    public function getLabels(): array
    {
        return $this->labels;
    }

    /**
     * Sets the labels
     */
    public function setLabels(array $labels): void
    {
        $this->labels = $labels;
    }
    public function addLabel(string $label): void
    {
        $this->labels[] = $label;
    }

    /**
     * Returns the datasets
     *
     * @return aray
     */
    public function getDatasets(): array
    {
        return $this->datasets;
    }

    /**
     * Sets the datasets
     */
    public function setDatasets(array $datasets): void
    {
        $this->datasets = $datasets;
    }

    public function addDataset(array $dataset): void
    {
        $this->datasets[] = $dataset;
    }

    /**
     * Returns the colors
     *
     * @return aray
     */
    public function getColors(): array
    {
        return $this->colors;
    }
    public function getColor($nr)
    {
        return $this->colors[$nr];
    }
    public function getColorsRgba($alpha = 1)
    {
        $colors = [];
        foreach ($this->colors as $color) {
            $colors[] = $this->chartUtility->hex2rgba($color, $alpha);
        }
        return $colors;
    }
    public function getColorRgba($nr, $alpha): string
    {
        if (!isset($this->colors[$nr])) {
            return '';
        }
        return $this->chartUtility->hex2rgba($this->colors[$nr], $alpha);
    }
    public function getCycledColorsRgba(int $count, float $alpha = 1): array
    {
        $palette = array_values($this->colors);
        $n = count($palette);
        if ($n === 0) {
            return [];
        }
        $result = [];
        for ($i = 0; $i < $count; $i++) {
            $result[] = $this->chartUtility->hex2rgba($palette[$i % $n], $alpha);
        }
        return $result;
    }

    /**
     * Sets the colors
     */
    public function setColors(array $colors): void
    {
        $this->colors = $colors;
    }

    /**
     * Prepare data from [ x=> [y,y2], x=> [y,y2], x=> [y,y2]]
     *
     * @param array $dataRecords
     */
    public function setDatasetsAndLabels(array $dataRecords, int $dataLimit = 0): void
    {
        $datasets = [];
        $datasetData = [];
        $index = 0;
        foreach ($dataRecords as $label => $data) {
            $this->addLabel($label ?? '');
            foreach ($data as $key => $d) {
                if (!isset($dataset[$key])) {
                    $dataset[$key] = new stdClass();
                    $dataset[$key]->data = [];
                }
                $dataset[$key]->data[] = $d;
            }
            $index++;
            if ($dataLimit > 0 && $index >= $dataLimit) {
                break;
            }
        }
        $this->setDatasets($datasets);
    }

    /**
     * Prepare data from [[x,y,y2],[x,y,y2],[x,y,y2]]
     *
     * @param string $x
     * @param mixed $y (array oder string)
     */
    public function setDatasetsAndLabelsFromRecords(array $dataRecords, $x, $y, $colorAttribute = null): void
    {
        $datasets = [];
        // y ist Array -> Mehrere Datenreihen
        if (is_array($y)) {
            $datasetData = [];
            foreach ($y as $label => $attribute) {
                $datasetData[$label] = [];
            }
            foreach ($dataRecords as $nr => $data) {
                $this->addLabel($data[$x] ?? '');
                foreach ($y as $label => $attribute) {
                    $datasetData[$label][] = (float) $data[$attribute];
                }
            }
            foreach ($datasetData as $label => $data) {
                $dataset = new stdClass();
                $dataset->label = $label ?? '';
                $dataset->data = $data;
                $datasets[] = $dataset;
            }
        } else {
            // y ist nur ein Attributename
            $colors = [];
            $datasetData = [];
            foreach ($dataRecords as $nr => $data) {
                $this->addLabel($data[$x] ?? '');
                $datasetData[] = $data[$y];
                if ($colorAttribute) {
                    $colors[] = $data[$colorAttribute];
                }
            }
            $dataset = new stdClass();
            $dataset->data = $datasetData;
            $datasets[] = $dataset;
            if (!empty($colors)) {
                $this->setColors($colors);
            }
        }
        $this->setDatasets($datasets);
    }

    public function datasetsAppend(): array
    {
        $alpha = $this->chartUtility->getAlphaArray();

        $datasets = [];
        $count = 0;
        foreach ($this->getDatasets() as $dataset) {
            switch ($this->type) {
                case 'pie':
                case 'doughnut':
                    $count = count($dataset->data);
                    $dataset->backgroundColor = $this->getCycledColorsRgba($count, $alpha['default']);
                    $dataset->hoverBackgroundColor = $this->getCycledColorsRgba($count, $alpha['hover']);
                    break;

                case 'polarArea':
                    break;

                case 'bar':
                case 'horizontalBar':
                    $dataset->backgroundColor = $this->getColorRgba($count, $alpha['default']);
                    $dataset->borderColor = $this->getColorRgba($count, $alpha['border']);
                    $dataset->hoverBackgroundColor = $this->getColorRgba($count, $alpha['special']);
                    $dataset->hoverBorderColor = $this->getColorRgba($count, $alpha['special']);
                    break;

                case 'line':
                case 'radar':
                    $dataset->fill = false;
                    $dataset->backgroundColor = $this->getColorRgba($count, $alpha['default']);
                    $dataset->borderColor = $this->getColorRgba($count, $alpha['default']);
                    $dataset->pointBackgroundColor = $this->getColorRgba($count, $alpha['special']);
                    $dataset->pointBorderColor = '#FFF';
                    $dataset->pointHoverBackgroundColor = '#FFF';
                    $dataset->pointHoverBorderColor = $this->getColorRgba($count, $alpha['special']);
                    break;
                default:
                    break;
            }
            $count++;
            $datasets[] = $dataset;
        }
        return $datasets;
    }

    public function getData(): \stdClass
    {
        $ret = new stdClass();

        $ret->datasets = $this->datasetsAppend();
        $ret->labels = $this->getLabels();
        return $ret;
    }

    public function getDataJson(): string
    {
        return json_encode($this->getData());
    }

    public function getHtml($arguments): string
    {
        $html = '';
        // Render HTML and JS
        /*
        $html .= '<div class="ig-chart-container chart-' . $this->getType() . '">';
        $html .= '<div class="canvasContainer ' . $extraClasses . '">';
        $html .= '<canvas id="igChart'.$this->chartUtility->getId().'" width="100" height="100" />';
        $html .= '</div>';
        $html .= '</div>';
        $js="var igChart".$this->chartUtility->getId()." = new Chart('igChart".$this->chartUtility->getId()."',{
        type: '".$this->getType()."',
        data: ".$this->getDataJson()."
        });";
        $pageRenderer=GeneralUtility::makeInstance(\TYPO3\CMS\Core\Page\PageRenderer::class);
        $pageRenderer->addJsFooterInlineCode( 'ig-chart-' . $this->chartUtility->getId(), $js);
        return $html;
         */

        // Generate extra classes
        $extraClasses = '';
        if ($arguments['showLegend']) {
            $extraClasses .= 'hasLegend ';
        }
        $extraClasses = rtrim($extraClasses);

        // Generate extra data tags
        $extraData = 'data-yscalesuffix="' . $arguments['yScaleSuffix'] . '" ';
        $extraData .= 'data-xscalesuffix="' . $arguments['xScaleSuffix'] . '" ';
        $extraData = rtrim($extraData);

        // Get the ratio of the chart
        $ratio = explode('-', (string) $arguments['ratio']);

        $html .= '<div class="frame frame-default ig-chart-container chart-' . $this->getType() . '">';
        $html .= '<div class="canvasContainer ' . $extraClasses . '">';
        $html .= '<canvas
                        data-offset="-300"
                        width="' . $ratio[0] . '"
                        height="' . $ratio[1] . '"
                        style=""
                        class="ig-chart" ' . $extraData . '
                        data-showlegend="' . $arguments['showLegend'] . '"
                        data-disabletooltipdata="' . $arguments['disableTooltipData'] . '"
                        data-charttype="' . $this->getType() . '"
                        data-chart="' . htmlentities((string) $this->getDataJson(), ENT_QUOTES, 'UTF-8') . '"
                        data-cutoutpercentage="' . $arguments['cutoutPercentage'] . '"
                        data-showdoughnuttotal="' . $arguments['showDoughnutTotal'] . '"
                        data-doughnuttotalsuffix="' . $arguments['doughnutTotalSuffix'] . '"
                        data-showxscale="' . $arguments['showXScale'] . '"
                        data-showyscale="' . $arguments['showYScale'] . '"
                        data-ystepsize="' . $arguments['yStepSize'] . '"
                        data-yscalemax="' . $arguments['yScaleMax'] . '"
                        data-yscalemin="' . $arguments['yScaleMin'] . '"
                        data-xstepsize="' . $arguments['xStepSize'] . '"
                        data-xscalemax="' . $arguments['xScaleMax'] . '"
                        data-xscalemin="' . $arguments['xScaleMin'] . '"
                        data-xreverse="' . $arguments['xReverse'] . '"
                        data-yreverse="' . $arguments['yReverse'] . '"
                        data-xscaleposition="' . $arguments['xScalePosition'] . '"
                        data-yscaleposition="' . $arguments['yScalePosition'] . '"
                        data-scalefontfamily="' . $arguments['scaleFontFamily'] . '"
                        data-scalefontcolor="' . $arguments['scaleFontColor'] . '"
                        data-scalefontsize="' . $arguments['scaleFontSize'] . '"
                        data-scalefontstyle="' . $arguments['scaleFontStyle'] . '"
                        data-animationduration="' . $arguments['animationDuration'] . '"
                        data-animationeasing="' . $arguments['animationEasing'] . '"
                        data-animationdelay="' . $arguments['animationDelay'] . '"
                        data-additional-options="' . htmlspecialchars(
            json_encode($arguments['additionalOptions'])
        ) . '"
                        data-locale="' . $arguments['locale'] . '"
                        >';

        if (isset($arguments['fallbackImg']) && is_integer($arguments['fallbackImg'])) {
            $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
            $fallbackUrl = $fileRepository->findFileReferenceByUid(intval($arguments['fallbackImg']))->getPublicUrl();
            $html .= '<img src="/' . $fallbackUrl . '" />';
        }

        $html .= '</canvas>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    /**
     * Converts the a CSV string to an array.
     *
     * Example:
     * csv2Array("label1,label2,label3\nvalue1,value2,value3") ->
     * [["label1", "label2", "label3"],[value1, value2, value3]]
     *
     * @param string $chartData - The data as CSV string.
     * @return array
     */
    public function csv2Array(string $chartData): array
    {
        $data = [];

        // Split at new lines
        $records = preg_split('/$\R?^/m', $chartData);
        foreach ($records as $record) {
            $data[] = str_getcsv($record);
        }
        return $data;
    }

    /**
     * Build the correct datasets for each chart type.
     * For more info visit: http://www.chartjs.org/docs/
     *
     * @param array $chartData
     */
    public function getDatasetsFromCsv(string $chartData): void
    {
        $dataRecords = $this->csv2Array($chartData);
        /**
         * Generate Labels
         */

        $this->data['labels'] = [];

        if ($this->type == 'pie' || $this->type == 'doughnut' || $this->type == 'polarArea') {
            $this->datasets = [new stdClass()];
            $this->datasets[0]->data = [];
            $this->datasets[0]->backgroundColor = [];


            if ($this->type != 'polarArea') {
                $this->datasets[0]->hoverBackgroundColor = [];
            } else {
                $this->datasets[0]->label = '';
            }

            for ($i = 0, $length = count($dataRecords); $i < $length; $i++) {
                $this->values[] = $dataRecords[$i][1];
                $this->addLabel($dataRecords[$i][0] ?? '');
            }
            $this->rowCount = $length;
        } else {
            for ($i = 0, $length = count($dataRecords); $i < $length - 1; $i++) {
                $this->values[] = array_slice($dataRecords[$i + 1], 1);
                $labels[] = trim((string) $dataRecords[$i + 1][0]);
            }

            $this->rowCount = $length - 1;

            $this->labels = array_map('trim', $dataRecords[0]);
        }

        //Get colors from settings
        $colors = $this->arguments['colors'];
        $datasets = [];
        for ($i = 0; $i < $this->rowCount; $i++) {
            $datasetData = [];
            //Get current color
            if ($this->arguments['maxColors']) {
                $color = $colors[$i % $this->arguments['maxColors']];
            } else {
                $color = $colors[$i];
            }
            $alpha = $this->chartUtility->getAlphaArray($color);
            switch ($this->type) {
                case 'pie':
                case 'doughnut':
                    $this->datasets[0]->data[] = (int) $this->values[$i];
                    $this->datasets[0]->backgroundColor[] = $this->chartUtility->hex2rgba($color, $alpha['default']);
                    $this->datasets[0]->hoverBackgroundColor[] = $this->chartUtility->hex2rgba($color, $alpha['hover']);
                    break;

                case 'polarArea':
                    $this->datasets[0]->data[] = (int) $this->values[$i];
                    $this->datasets[0]->backgroundColor[] = $this->chartUtility->hex2rgba($color, $alpha['default']);
                    break;

                case 'bar':
                case 'horizontalBar':
                    $dataset = new stdClass();
                    $dataset->label = $labels[$i];
                    $dataset->backgroundColor = $this->chartUtility->hex2rgba($color, $alpha['default']);
                    $dataset->borderColor = $this->chartUtility->hex2rgba($color, $alpha['border']);
                    $dataset->hoverBackgroundColor = $this->chartUtility->hex2rgba($color, $alpha['special']);
                    $dataset->hoverBorderColor = $this->chartUtility->hex2rgba($color, $alpha['special']);
                    $dataset->data = $this->values[$i];
                    $this->datasets[] = $dataset;
                    break;

                case 'line':
                case 'radar':
                    $dataset = new stdClass();
                    $dataset->label = $this->labels[$i];
                    $dataset->fill = false;
                    $dataset->backgroundColor = $this->chartUtility->hex2rgba($color, $alpha['default']);
                    $dataset->borderColor = $this->chartUtility->hex2rgba($color, $alpha['special']);
                    $dataset->pointBackgroundColor = $this->chartUtility->hex2rgba($color, $alpha['special']);
                    $dataset->pointBorderColor = '#FFF';
                    $dataset->pointHoverBackgroundColor = '#FFF';
                    $dataset->pointHoverBorderColor = $this->chartUtility->hex2rgba($color, $alpha['special']);
                    $dataset->data = $this->values[$i];
                    $this->datasets[] = $dataset;
                    break;

                default:
                    break;
            }
        }
    }
}
