<?php
/**
 * The MIT License (MIT)
 *
 * @Author: sharky72 (https://github.com/KocourKuba)
 * Original code from DUNE HD
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

require_once 'movie_series.php';
require_once 'movie_season.php';
require_once 'movie_variant.php';
require_once 'lib/default_dune_plugin.php';
require_once 'lib/user_input_handler_registry.php';
require_once 'lib/json_serializer.php';

class Movie extends Json_Serializer implements User_Input_Handler
{
    const ID = 'movie';

    /**
     * @var string
     */
    public $id;

    /**
     * @var array
     */
    public $movie_info;

    /**
     * @var Movie_Season[]
     */
    public $seasons_list = array();

    /**
     * @var Movie_Series[]
     */
    public $series_list = array();

    /**
     * @var Default_Dune_Plugin
     */
    private $plugin;

    /**
     * @param string $id
     * @param Default_Dune_Plugin $plugin
     */
    public function __construct($id, $plugin)
    {
        if (is_null($id)) {
            hd_debug_print("Movie::id is null, create dummy movie");
            $id = "-1";
        }

        $this->id = (string)$id;
        $this->plugin = $plugin;
    }

    public function __sleep()
    {
        $vars = get_object_vars($this);
        unset($vars['plugin']);
        return array_keys($vars);
    }

    protected function do_get_action_map()
    {
        hd_debug_print(null, true);

        User_Input_Handler_Registry::get_instance()->register_handler($this);

        $actions[GUI_EVENT_PLAYBACK_STOP] = User_Input_Handler_Registry::create_action($this, GUI_EVENT_PLAYBACK_STOP);
        $actions[GUI_EVENT_TIMER] = User_Input_Handler_Registry::create_action($this, GUI_EVENT_TIMER);
        if (!is_limited_apk()) {
            $actions[GUI_EVENT_KEY_A_RED] = User_Input_Handler_Registry::create_action($this, ACTION_SLEEP_TIMER_CLEAR);
            $actions[GUI_EVENT_KEY_B_GREEN] = User_Input_Handler_Registry::create_action($this, ACTION_SLEEP_TIMER_ADD);
            $actions[GUI_EVENT_KEY_C_YELLOW] = User_Input_Handler_Registry::create_action($this, ACTION_SLEEP_TIMER);
        }

        return $actions;
    }

    ///////////////////////////////////////////////////////////////////////
    // User_Input_Handler interface

    /**
     * @inheritDoc
     */
    public function get_handler_id()
    {
        return static::ID;
    }

    /**
     * @inheritDoc
     */
    public function handle_user_input(&$user_input, &$plugin_cookies)
    {
        // handler_id => movie
        // control_id => playback_stop
        // osd_active => 1
        // plugin_vod_id => 99842
        // plugin_vod_series_ndx => 0
        // plugin_vod_stop_position => 43
        // plugin_vod_duration => 3075
        // plugin_vod_start_tm => 1696530358
        // plugin_vod_stop_tm => 1696530376
        // plugin_vod_prebuffering_end_tm => 1696530360
        // playback_end_of_stream => 0
        // playback_mode => plugin_vod
        // playback_browser_activated => 0
        // playback_stop_pressed => 1

        if (!isset($user_input->control_id)) {
            hd_debug_print("user input control id not set", true);
            return null;
        }

        switch ($user_input->control_id) {
            case GUI_EVENT_PLAYBACK_STOP:
                if (isset($user_input->playback_stop_pressed)) {
                    Sleep_Timer::set_sleep_timer(0);
                }

                $series_list = array_values($this->series_list);
                $episode = $series_list[$user_input->plugin_vod_series_ndx];

                $watched = (isset($user_input->playback_end_of_stream) && (int)$user_input->playback_end_of_stream !== 0)
                    || ($user_input->plugin_vod_duration - $user_input->plugin_vod_stop_position) < 60;

                $series_idx = empty($episode->id) ? $user_input->plugin_vod_series_ndx : $episode->id;
                hd_debug_print("add movie to history: id: $user_input->plugin_vod_id, series: $series_idx", true);

                $invalidate[] = Default_Dune_Plugin::get_group_media_url_str(VOD_GROUP_ID);
                $invalidate[] = Default_Dune_Plugin::get_group_media_url_str(VOD_HISTORY_GROUP_ID);

                if ($user_input->plugin_vod_id === VOD_LIST_GROUP_ID) {
                    $movie_id = $episode->id;
                    $invalidate[] = Default_Dune_Plugin::get_group_media_url_str(VOD_LIST_GROUP_ID);
                } else {
                    $movie_id = $user_input->plugin_vod_id;
                    $invalidate[] = Starnet_Vod_Series_List_Screen::make_vod_media_url_str($user_input->plugin_vod_id, $episode->season_id);
                }

                $this->plugin->set_vod_history(
                    $movie_id,
                    $series_idx,
                    array(
                        COLUMN_WATCHED => (int)$watched,
                        COLUMN_POSITION => $user_input->plugin_vod_stop_position,
                        COLUMN_DURATION => $user_input->plugin_vod_duration,
                        COLUMN_TIMESTAMP => $user_input->plugin_vod_stop_tm
                    )
                );

                return Action_Factory::invalidate_folders($invalidate);

            case GUI_EVENT_TIMER:
                clearstatcache();
                $sleep_timer = Sleep_Timer::get_sleep_timer();
                $post_action = $sleep_timer ? Action_Factory::change_behaviour($this->do_get_action_map(), 1000) : null;
                $comps = array();
                Sleep_Timer::create_estimated_timer_box($sleep_timer, $comps, $user_input);
                return Action_Factory::update_osd($comps, $post_action);

            case ACTION_SLEEP_TIMER:
                return Sleep_Timer::show_sleep_timer_dialog($this);

            case Sleep_Timer::CONTROL_SLEEP_TIME_SET:
                $min = (int)$user_input->{Sleep_Timer::CONTROL_SLEEP_TIME_MIN};
                Sleep_Timer::set_timer_op($min);
                Sleep_Timer::set_sleep_timer($min * 60);
                return Action_Factory::change_behaviour($this->do_get_action_map(), 1000);

            case ACTION_SLEEP_TIMER_ADD:
                $step = $this->plugin->get_parameter(PARAM_SLEEP_TIMER_STEP, 60);
                $sleep_timer = Sleep_Timer::get_sleep_timer() + $step;
                Sleep_Timer::set_sleep_timer($sleep_timer);
                $comps = array();
                Sleep_Timer::create_estimated_timer_box($sleep_timer, $comps, $user_input, true);
                return Action_Factory::update_osd($comps, Action_Factory::change_behaviour($this->do_get_action_map(), 1000));

            case ACTION_SLEEP_TIMER_CLEAR:
                Sleep_Timer::set_sleep_timer(0);
                $comps = array();
                Sleep_Timer::create_estimated_timer_box(0, $comps, $user_input, true);
                return Action_Factory::update_osd($comps, Action_Factory::change_behaviour($this->do_get_action_map(), 1000));
        }

        return null;
    }

    ///////////////////////////////////////////////////////////////////////

    /**
     * @param string $name
     * @param string $name_original
     * @param string $description
     * @param string $poster_url
     * @param string $length_min
     * @param string $year
     * @param string $directors_str
     * @param string $scenarios_str
     * @param string $actors_str
     * @param string $genres_str
     * @param string $rate_imdb
     * @param string $rate_kinopoisk
     * @param string $rate_mpaa
     * @param string $country
     * @param string $budget
     * @param array $details
     * @param array $rate_details
     */
    public function set_data(
        $name,
        $name_original,
        $description,
        $poster_url,
        $length_min,
        $year,
        $directors_str,
        $scenarios_str,
        $actors_str,
        $genres_str,
        $rate_imdb,
        $rate_kinopoisk,
        $rate_mpaa,
        $country,
        $budget = null,
        $details = array(),
        $rate_details = array())
    {
        $this->movie_info = array(
            PluginMovie::name => $this->to_string($name),
            PluginMovie::name_original => $this->to_string($name_original),
            PluginMovie::description => $this->to_string($description),
            PluginMovie::poster_url => $this->to_string($poster_url),
            PluginMovie::length_min => $this->to_int($length_min, -1),
            PluginMovie::year => $this->to_int($year, -1),
            PluginMovie::directors_str => $this->to_string($directors_str),
            PluginMovie::scenarios_str => $this->to_string($scenarios_str),
            PluginMovie::actors_str => $this->to_string($actors_str),
            PluginMovie::genres_str => $this->to_string($genres_str),
            PluginMovie::rate_imdb => $this->to_string($rate_imdb),
            PluginMovie::rate_kinopoisk => $this->to_string($rate_kinopoisk),
            PluginMovie::rate_mpaa => $this->to_string($rate_mpaa),
            PluginMovie::country => $this->to_string($country),
            PluginMovie::budget => $this->to_string($budget),
            PluginMovie::details => $details,
            PluginMovie::rate_details => $rate_details,
        );
    }

    /**
     * @param string|null $v
     * @return string
     */
    public function to_string($v)
    {
        return $v === null ? '' : (string)$v;
    }

    /**
     * @param string|int $v
     * @param string|int $default_value
     * @return int
     */
    private function to_int($v, $default_value)
    {
        $v = (string)$v;
        if (!is_numeric($v)) {
            return $default_value;
        }
        $v = (int)$v;
        return $v <= 0 ? $default_value : $v;
    }

    /**
     * @return void
     */
    public function clear_season_data()
    {
        $this->seasons_list = array();
    }

    /**
     * @param Movie_Season $movie_season
     */
    public function add_season_data($movie_season)
    {
        $this->seasons_list[] = $movie_season;
    }

    /**
     * @return void
     */
    public function clear_series_data()
    {
        $this->series_list = array();
    }

    /**
     * @param Movie_Series $movie_series
     */
    public function add_series_data($movie_series)
    {
        $this->series_list[$movie_series->id] = $movie_series;
    }

    /**
     * @return string
     */
    public function get_id()
    {
        return $this->id;
    }

    /**
     * @return array
     */
    public function get_movie_info()
    {
        return $this->movie_info;
    }

    /**
     * @return bool
     */
    public function has_seasons()
    {
        return !empty($this->seasons_list);
    }

    /**
     * @return array
     */
    public function get_seasons_list()
    {
        return $this->seasons_list;
    }

    /**
     * @return Movie_Season
     */
    public function get_season($season_id)
    {
        return isset($this->seasons_list[$season_id]) ? $this->seasons_list[$season_id] : null;
    }

    /**
     * @return bool
     */
    public function has_series()
    {
        return !empty($this->series_list);
    }

    /**
     * @return Movie_Series[]
     */
    public function get_series_list()
    {
        return $this->series_list;
    }

    /**
     * @return Movie_Series
     */
    public function get_series($series_id)
    {
        return isset($this->series_list[$series_id]) ? $this->series_list[$series_id] : reset($this->series_list);
    }

    /**
     * @param string $series_id
     * @return bool
     */
    public function has_qualities($series_id)
    {
        $qualities = $this->get_qualities($series_id);
        return !empty($qualities);
    }

    /**
     * @param $series_id
     * @return array
     */
    public function get_qualities($series_id)
    {
        if (!$this->has_series()) {
            hd_debug_print("no series present", true);
            return array();
        }

        $series = $this->get_series($series_id);
        if (empty($series->variants)) {
            return array();
        }

        return array_map(function ($variant) {
            return $variant->name;
        }, $series->variants);
    }

    /**
     * @param string $series_id
     * @param string $id
     * @return Movie_Variant
     */
    public function get_quality($series_id, $id)
    {
        if (!$this->has_series()) {
            hd_debug_print("no series present", true);
            return null;
        }

        return $this->get_series($series_id)->get_variant($id);
    }

    /**
     * @param string $series_id
     * @param string $quality_id
     * @return bool
     */
    public function has_audios($series_id, $quality_id)
    {
        $a_variants = $this->get_audios($series_id, $quality_id);
        return !empty($a_variants);
    }

    /**
     * @param string $series_id
     * @param string $quality_id
     * @return string[]|null
     */
    public function get_audios($series_id, $quality_id)
    {
        if (!$this->has_series()) {
            return null;
        }

        $q_variant = $this->get_quality($series_id, $quality_id);
        if (empty($q_variant)) {
            return null;
        }

        $a_variants = $q_variant->get_variants();
        if (empty($a_variants)) {
            return null;
        }

        return array_map(function ($variant) {
            return $variant->name;
        }, $a_variants);
    }

    /**
     * @param string $series_id
     * @param string $quality_id
     * @param string $id
     * @return Movie_Playback_Url|null
     */
    public function get_audio($series_id, $quality_id, $id)
    {
        $q_variant = $this->get_quality($series_id, $quality_id);
        if (empty($q_variant)) {
            return null;
        }

        $a_variant = $q_variant->get_variant($id);
        if (is_null($a_variant)) {
            return null;
        }

        return $a_variant->get_default_playback_url();
    }

    /**
     * Collect quality information only from first series
     * Return array of key => variant_name
     *
     * @param string $season_id
     * @return array
     */
    public function collect_all_qualities($season_id)
    {
        hd_debug_print("season_id: $season_id", true);
        $series_quality = array();
        foreach ($this->series_list as $series_id => $episode) {
            if (!is_null($season_id) && $season_id !== $episode->season_id) continue;

            $series_quality = $this->get_qualities($series_id);
            if (!empty($series_quality)) {
                unset($series_quality['auto']);
                break;
            }
        }

        return $series_quality;
    }

    /**
     * Collect audio information only from first series and first quality
     * @param string $season_id
     * @return array
     */
    public function collect_all_audios($season_id)
    {
        $quality_audios = array();
        foreach ($this->series_list as $series_id => $episode) {
            if (!is_null($season_id) && $season_id !== $episode->season_id) continue;

            $series_quality = $this->get_qualities($series_id);
            if (empty($series_quality)) continue;

            foreach ($series_quality as $key => $quality) {
                $quality_audios = $this->get_audios($series_id, $key);
                if (!empty($quality_audios)) {
                    unset($quality_audios['auto']);
                    break;
                }
            }
        }

        return $quality_audios;
    }

    /**
     * @param MediaURL $media_url
     * @return array
     */
    public function get_movie_play_info(MediaURL $media_url)
    {
        hd_debug_print(null, true);
        hd_debug_print($media_url, true);

        if (!isset($media_url->screen_id)) {
            hd_debug_print("get_movie_play_info: List screen in media url not set: " . $media_url->get_raw_string());
            print_backtrace();
            return array();
        }

        switch ($media_url->screen_id) {
            case Starnet_Vod_Seasons_List_Screen::ID:
                if ($this->has_seasons()) break;

                hd_debug_print("get_movie_play_info: Invalid movie: season list is empty");
                print_backtrace();
                return array();

            case Starnet_Vod_Series_List_Screen::ID:
            case Starnet_Vod_Movie_Screen::ID:
                if ($this->has_series()) break;

                hd_debug_print("get_movie_play_info: Invalid movie: series list is empty");
                print_backtrace();
                return array();

            case Starnet_Vod_List_Screen::ID:
                if ($this->has_series()) break;

                hd_debug_print("get_movie_play_info: Invalid movie playlist: list is empty");
                print_backtrace();
                return array();

            default:
                hd_debug_print("get_movie_play_info: Unknown list screen: $media_url->screen_id");
                print_backtrace();
                return array();
        }

        $sel_id = safe_get_member($media_url, 'episode_id');
        $series_array = array();
        $initial_series_ndx = 0;
        $def_q_variant = $this->plugin->get_setting(PARAM_VOD_DEFAULT_QUALITY, 'auto');
        hd_debug_print("default quality: $def_q_variant", true);
        $def_a_variant = $this->plugin->get_setting(PARAM_VOD_DEFAULT_AUDIO, 'auto');
        hd_debug_print("default audio: $def_a_variant", true);
        $counter = 0; // series index. Not the same as the key of series list
        $initial_start_array = array();
        foreach ($this->series_list as $series) {
            $q_variant = $series->get_variant($def_q_variant);
            if (is_null($q_variant)) {
                hd_debug_print("Default Playback data for series '$series->id': {$series->default_playback_url->url}", true);
                $playback_info = $series->default_playback_url;
            } else {
                // check if quality variant has audio variant
                $a_variant = $q_variant->get_variant($def_a_variant);
                if (empty($a_variant)) {
                    $playback_info = $q_variant->default_playback_url;
                    hd_debug_print("Default Audio Playback data for quality '$def_q_variant' - $playback_info->url", true);
                } else {
                    $playback_info = $a_variant->default_playback_url;
                    hd_debug_print("Playback data for for quality '$def_q_variant' and audio '$def_a_variant' - $playback_info->url", true);
                }
            }

            if ($playback_info->is_stream_url) {
                $vod_url = $playback_info->url;
            } else {
                hd_debug_print("Request playback url for $series->id", true);
                $vod_url = $this->plugin->vod->get_vod_playback_url($series->id, $def_q_variant, $def_a_variant);
                if (empty($vod_url)) {
                    hd_debug_print("no valid playback url for '$series->id' skipping");
                    continue;
                }
            }

            if (!is_null($sel_id) && $series->id === $sel_id) {
                $initial_series_ndx = $counter;
            }

            $pos = 0;
            $name = $series->name;
            $ids = explode(':', $media_url->movie_id);
            $movie_id = $ids[0];
            if ($media_url->screen_id === Starnet_Vod_List_Screen::ID) {
                $viewed_params = $this->plugin->get_vod_history_params($series->id, $series->id);
            } else {
                $viewed_params = $this->plugin->get_vod_history_params($movie_id, $series->id);
            }
            if (!empty($viewed_params) && $viewed_params[COLUMN_WATCHED] == 0 && $viewed_params[COLUMN_DURATION] != -1) {
                $name .= " [" . format_duration($viewed_params[COLUMN_POSITION]) . "]";

                $pos = $viewed_params[COLUMN_POSITION];
                if ($pos < 0) {
                    $pos = 0;
                }

                if ($pos > $viewed_params[COLUMN_DURATION]) {
                    $pos = 0;
                }
            }

            $initial_start_array[$counter] = $pos * 1000;
            $vod_url = HD::make_ts($vod_url);

            $dune_params = $this->plugin->collect_dune_params();
            if (!empty($dune_params)) {
                $magic = str_replace('=', ':', http_build_query($dune_params, null, ','));
                hd_debug_print("dune_params: $magic");
                $vod_url .= HD::DUNE_PARAMS_MAGIC . $magic;
            }

            hd_debug_print("Url: $vod_url", true);
            hd_debug_print("Playback movie: $media_url->movie_id, episode: $series->id ($def_q_variant)", true);
            $series_array[] = array(
                PluginVodSeriesInfo::name => $name,
                PluginVodSeriesInfo::playback_url => $vod_url,
                PluginVodSeriesInfo::playback_url_is_stream_url => true,
            );

            $counter++;
        }

        $initial_start = 0;
        if (isset($initial_start_array[$initial_series_ndx])) {
            $initial_start = $initial_start_array[$initial_series_ndx];
        }
        hd_debug_print("starting vod index $initial_series_ndx at position $initial_start", true);

        return array(
            PluginVodInfo::id => $this->id,
            PluginVodInfo::name => $this->movie_info[PluginMovie::name],
            PluginVodInfo::description => $this->movie_info[PluginMovie::description],
            PluginVodInfo::poster_url => $this->movie_info[PluginMovie::poster_url],
            PluginVodInfo::series => $series_array,
            PluginVodInfo::initial_series_ndx => $initial_series_ndx,
            PluginVodInfo::buffering_ms => (int)$this->plugin->get_setting(PARAM_BUFFERING_TIME, 1000),
            PluginVodInfo::actions => $this->do_get_action_map(),
            PluginVodInfo::initial_position_ms => $initial_start,
        );
    }
}
