<?php

/*
 * Copyright 2005 - 2025 Centreon (https://www.centreon.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For more information : contact@centreon.com
 *
 */

/**
 * Class
 *
 * @class CentreonTimeperiodRenderer
 * @description Used for rendering time periods in order to make them more human-readable
 */
class CentreonTimeperiodRenderer
{
    /** @var CentreonDB|null */
    protected $db;

    /** @var int */
    protected $tpid;

    /** @var mixed */
    protected $tpname;

    /** @var mixed */
    protected $tpalias;

    /** @var array[] */
    protected $timerange;

    /** @var array[] */
    protected $timeline;

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

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

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

    /**
     * CentreonTimeperiodRenderer constructor
     *
     * @param CentreonDB $db
     * @param int $tpid
     * @param string $inex
     *
     * @throws PDOException
     */
    public function __construct($db, $tpid, $inex)
    {
        $dayTab = ['tp_sunday' => [], 'tp_monday' => [], 'tp_tuesday' => [], 'tp_wednesday' => [], 'tp_thursday' => [], 'tp_friday' => [], 'tp_saturday' => []];
        $this->timerange = $dayTab;
        $this->timeline = $dayTab;
        $this->db = $db;
        $this->tpid = $tpid;
        $query = "SELECT tp_name, tp_alias, tp_monday, tp_tuesday, tp_wednesday, tp_thursday, tp_friday,
            tp_saturday, tp_sunday
            FROM timeperiod
            WHERE tp_id = '" . $tpid . "'";
        $res = $this->db->query($query);
        if (! $res->rowCount()) {
            throw new Exception('Timeperiod not found');
        }
        $row = $res->fetchRow();
        $this->tpname = $row['tp_name'];
        $this->tpalias = $row['tp_alias'];
        foreach ($this->timerange as $key => $val) {
            if ($row[$key]) {
                $tmparr = explode(',', $row[$key]);
                foreach ($tmparr as $tr) {
                    $tmptr = $this->getTimeRange($this->tpid, $this->tpname, $inex, $tr);
                    $this->timerange[$key][] = $tmptr;
                }
            }
        }
        $this->updateInclusions();
        $this->updateExclusions();
        $this->orderTimeRanges();
        $this->db = null;
    }

    /**
     * Set time bars
     *
     * @return void
     */
    public function timeBars(): void
    {
        $coef = 4;
        foreach ($this->timerange as $day => $ranges) {
            $timeindex = 0;
            $timeline[0] = ['start'    => 0, 'style'    => 'unset', 'end'      => 0, 'size'     => 0, 'Text'     => 'init', 'From'     => '', 'Textual'  => ''];
            if (isset($ranges[0])) {
                $last['in'] = '';
                $last['nb'] = 0;
                for ($i = 0; $i <= 1440; $i++) {
                    $actual['in'] = '';
                    $actual['nb'] = 0;
                    foreach ($ranges as $k => $r) {
                        if ($r['tstart'] <= $i && $i <= $r['tend']) {
                            $actual['in'] .= $actual['in'] != '' ? ',' . $k : $k;
                            $actual['nb']++;
                        }
                    }
                    if ($actual['in'] != $last['in'] || $i == 1440) {
                        if ($i == 0) {
                            $last = $actual;
                        }
                        $timeline[$timeindex]['end'] = $i;
                        $timeline[$timeindex]['size'] = round(($i - $timeline[$timeindex]['start']) / $coef);
                        switch ($last['nb']) {
                            case 0:
                                $ts = $timeline[$timeindex]['start'];
                                $timeline[$timeindex]['style'] = 'unset';
                                $timeline[$timeindex]['Textual'] = sprintf('%02d', intval($ts / 60)) . ':'
                                    . sprintf('%02d', $ts % 60) . '-' . sprintf('%02d', intval($i / 60)) . ':'
                                    . sprintf('%02d', $i % 60);
                                $timeline[$timeindex]['From'] = 'No timeperiod covering '
                                    . $timeline[$timeindex]['Textual'];
                                break;
                            default:
                                $idlist = explode(',', $last['in']);
                                foreach ($idlist as $v) {
                                    if ($ranges[$v]['inex'] == 0) {
                                        $timeline[$timeindex]['style'] = 'excluded';
                                    }
                                    $txt = $ranges[$v]['textual'];
                                    $inex = $ranges[$v]['inex'] ? 'include' : 'exclude';
                                    $from = $ranges[$v]['fromTpName'] . ' ' . $inex . ' ' . $txt;
                                    $timeline[$timeindex]['From'] .= $timeline[$timeindex]['From'] != ''
                                        ? ',' . $from : $from;
                                    $timeline[$timeindex]['Textual'] .= $timeline[$timeindex]['Textual'] != ''
                                        ? ',' . $txt : $txt;
                                }
                        }
                        switch ($last['nb']) {
                            case 0:
                                break;
                            case 1:
                                $timeline[$timeindex]['style'] = ($ranges[$last['in']]['inex'] == 1)
                                    ? 'included' : 'excluded';
                                break;
                            default:
                                $timeline[$timeindex]['style'] = 'warning';
                                $timeline[$timeindex]['From'] = 'OVERLAPS: ' . $timeline[$timeindex]['From'];
                                break;
                        }
                        if ($i < 1440) {
                            $timeline[++$timeindex] = ['start'    => $i, 'style'    => 'unset', 'end'      => 0, 'size'     => 0, 'Text'     => 'New in loop', 'From'     => '', 'Textual'  => ''];
                        }
                    }
                    $last = $actual;
                }
            }
            $endtime = $timeline[$timeindex]['end'];
            if ($endtime < 1440) {
                $textual = sprintf('%02d', intval($endtime / 60)) . ':' . sprintf('%02d', $endtime % 60) . '-24:00';
                $timeline[$timeindex] = ['start'    => $endtime, 'style'    => 'unset', 'end'      => 1440, 'size'     => (1440 - $endtime) / $coef, 'Text'     => 'No Timeperiod covering ' . $textual, 'From'     => 'No Timeperiod covering ' . $textual, 'Textual'  => $textual];
            }
            $this->timeline[$day] = $timeline;
            unset($timeline);
        }
    }

    /**
     * Get Time Period Name
     *
     * @return string
     */
    public function getName()
    {
        return $this->tpname;
    }

    /**
     * Get Time Period Alias
     *
     * @return string
     */
    public function getAlias()
    {
        return $this->tpalias;
    }

    /**
     * Get Timeline
     *
     * @return array
     */
    public function getTimeline()
    {
        return $this->timeline;
    }

    /**
     * Get Exception List
     *
     * @return array
     */
    public function getExceptionList()
    {
        return $this->exceptionList;
    }

    /**
     * @param string $field
     *
     * @return array
     */
    public static function getDefaultValuesParameters($field)
    {
        $parameters = [];
        $parameters['currentObject']['table'] = 'timeperiod';
        $parameters['currentObject']['id'] = 'tp_id';
        $parameters['currentObject']['name'] = 'tp_name';
        $parameters['currentObject']['comparator'] = 'tp_id';

        switch ($field) {
            case 'tp_include':
                $parameters['type'] = 'relation';
                $parameters['externalObject']['table'] = 'timeperiod';
                $parameters['externalObject']['id'] = 'tp_id';
                $parameters['externalObject']['name'] = 'tp_name';
                $parameters['externalObject']['comparator'] = 'tp_id';
                $parameters['relationObject']['table'] = 'timeperiod_include_relations';
                $parameters['relationObject']['field'] = 'timeperiod_include_id';
                $parameters['relationObject']['comparator'] = 'timeperiod_id';
                break;
            case 'tp_exclude':
                $parameters['type'] = 'relation';
                $parameters['externalObject']['table'] = 'timeperiod';
                $parameters['externalObject']['id'] = 'tp_id';
                $parameters['externalObject']['name'] = 'tp_name';
                $parameters['externalObject']['comparator'] = 'tp_id';
                $parameters['relationObject']['table'] = 'timeperiod_exclude_relations';
                $parameters['relationObject']['field'] = 'timeperiod_exclude_id';
                $parameters['relationObject']['comparator'] = 'timeperiod_id';
                break;
        }

        return $parameters;
    }

    /**
     * Compare
     *
     * @param array $a
     * @param array $b
     * @return int
     */
    protected function startCompare($a, $b)
    {
        if ($a['tstart'] == $b['tstart']) {
            return 0;
        }
        if ($a['tstart'] < $b['tstart']) {
            return -1;
        }

        return 1;
    }

    /**
     * Order Time Ranges
     *
     * @return void
     */
    protected function orderTimeRanges()
    {
        foreach ($this->timerange as $key => $val) {
            usort($val, ['CentreonTimeperiodRenderer', 'startCompare']);
            $this->timerange[$key] = $val;
        }
    }

    /**
     * Update Time Range
     *
     * @param array $inexTr
     * @return void
     */
    protected function updateTimeRange($inexTr)
    {
        foreach ($inexTr as $key => $val) {
            if (isset($val[0])) {
                foreach ($val as $tp) {
                    $this->timerange[$key][] = $tp;
                }
            }
        }
    }

    /**
     * Update Inclusions
     *
     * @throws PDOException
     * @return void
     */
    protected function updateInclusions()
    {
        $query = "SELECT timeperiod_include_id
        		  FROM timeperiod_include_relations
        		  WHERE timeperiod_id= '" . $this->tpid . "'";
        $res = $this->db->query($query);
        while ($row = $res->fetchRow()) {
            $inctp = new CentreonTimeperiodRenderer($this->db, $row['timeperiod_include_id'], 1);
            $this->updateTimeRange($inctp->timerange);
            foreach ($inctp->exceptionList as $key => $val) {
                $this->exceptionList[] = $val;
            }
        }
    }

    /**
     * Update Exclusions
     *
     * @throws PDOException
     * @return void
     */
    protected function updateExclusions()
    {
        $query = "SELECT * FROM timeperiod_exceptions WHERE timeperiod_id='" . $this->tpid . "'";
        $DBRESULT = $this->db->query($query);
        while ($row = $DBRESULT->fetchRow()) {
            $excep = $this->getException($row['timeperiod_id'], $this->tpname, $row['days'], $row['timerange']);
            $this->exceptionList[] = $excep;
        }
        $query = "SELECT timeperiod_exclude_id FROM timeperiod_exclude_relations WHERE timeperiod_id='" . $this->tpid . "'";
        $DBRESULT = $this->db->query($query);
        while ($row = $DBRESULT->fetchRow()) {
            $extp = new CentreonTimePeriodRenderer($this->db, $row['timeperiod_exclude_id'], 0);
            $this->updateTimeRange($extp->timerange);
        }
    }

    /**
     * Get time range array
     *
     * @param int $id
     * @param string $name
     * @param string $in
     * @param string $range
     * @return array
     */
    protected function getTimeRange($id, $name, $in, $range)
    {
        $timeRange = [];
        $timeRange['fromTpId'] = $id;
        $timeRange['fromTpName'] = $name;
        $timeRange['inex'] = $in;
        $timeRange['textual'] = $range;
        if (! preg_match('/([0-9]+):([0-9]+)-([0-9]+)+:([0-9]+)/', $range, $trange)) {
            return null;
        }
        if ($id < 1) {
            return null;
        }
        $timeRange['tstart'] = ($trange[1] * 60) + $trange[2];
        $timeRange['tend'] = ($trange[3] * 60) + $trange[4];

        return $timeRange;
    }

    /**
     * Get Exception
     *
     * @param int $id
     * @param string $name
     * @param string $day
     * @param string $range
     * @return array
     */
    protected function getException($id, $name, $day, $range)
    {
        return ['fromTpId' => $id, 'fromTpName' => $name, 'day' => $day, 'range' => $range];
    }
}
