<?php

###############################################################################
#
# Автор (Author): Brigadir (forum.mydune.ru)
# Дата (Date): 10-12-2017
# Последнее обновление (Latest update): 31-01-2021
#
###############################################################################

require_once 'lib/abstract_regular_screen.php';
require_once 'lib/user_input_handler.php';
require_once 'lib/folder_view_params.php';
require_once 'lib/dune_stb_api.php';

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

class CustomExtEpgProgram
{
	const	validated							= 'validated';
	const	day_start_ts						= 'day_ts';
	const	start_tm_sec						= 'start_ts';
	const	end_tm_sec							= 'end_ts';
	const	title								= 'title';
	const	desc								= 'desc';
	const	details								= 'details';
	const	icon_url							= 'icon_url';
}

class CustomEpgScreenDefs
{
	const	program_info						= 'program_info';
	const	programs_list						= 'programs_list';
	const	channels_list						= 'channels_list';
	const	groups_list							= 'groups_list';
}

class CustomEPG implements Screen, UserInputHandler
{
	///////////////////////////////////////////////////////////////////////////

	const	CUSTOM_EPG_DIALOG_WIDTH 			= 1630;
	const	MAX_NUM_ACTORS_IN_SHORT_DETAILS 	= 3;
	const	MAX_NUM_DIRECTORS_IN_SHORT_DETAILS 	= 3;
	const	MAX_NUM_PRESENTERS_IN_SHORT_DETAILS = 3;
	const	TRANSPARENT_DOT						= '0x00ffffff';
	const	IMDB_RATING_ICON 					= '/firmware_ext/plugins/shell_ext/icons/imdb.png';

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

	private	$epg;
	private	$screen_id;
	private	$stb_firmware;
	private	$screens_counter;
	private $kp_rating_icon;
	private $imdb_rating_icon;
	private	$group_item_idx;
	private	$channel_item_idx;
	private	$program_item_idx;
	private	$thumbnails_not_available;
	private	$show_custom_epg_key;
	private	$tv_playback_group_id;
	private	$tv_playback_channel_id;
	private	$tv_playback_live_start_ts;
	private	$tv_playback_archive_start_ts;
	private	$tv_playback_url;
	private	$tv_play_action;
	private	$scroll_page_event;
	private	$scroll_lines_position;
	private	$program_data;
	private	$playback_archive_ts;
	private	$playback_live_ts;
	private	$replay_available;
	private	$display_please_wait;
	private	$display_live_epg_item;
	private	$display_playable_stream_info;
	private $tv_fav_channels_font_color;
	private $tv_all_channels_font_color;

	private static $days_ru =
		array
		(
			'<text color=4>Вс</text>',
			'<text>Пн</text>',
			'<text>Вт</text>',
			'<text>Ср</text>',
			'<text>Чт</text>',
			'<text>Пт</text>',
			'<text color=4>Сб</text>',
		);

	private static $ret_prev_screen_id =
		array
		(
			CustomEpgScreenDefs::program_info		=> CustomEpgScreenDefs::programs_list,
			CustomEpgScreenDefs::programs_list		=> CustomEpgScreenDefs::channels_list,
			CustomEpgScreenDefs::channels_list		=> CustomEpgScreenDefs::groups_list,
			CustomEpgScreenDefs::groups_list		=> null,
		);

	private static $get_next_screen_id =
		array
		(
			CustomEpgScreenDefs::program_info		=> null,
			CustomEpgScreenDefs::programs_list		=> CustomEpgScreenDefs::program_info,
			CustomEpgScreenDefs::channels_list		=> CustomEpgScreenDefs::programs_list,
			CustomEpgScreenDefs::groups_list		=> CustomEpgScreenDefs::channels_list,
		);

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

	private static function get_programs_list_epg_screen_view_params($day, $caption, $thumbnails_support)
	{
		$fw_details_support = defined('ViewParams::details_box_width');
		$transparent_dot_path = get_paved_path(DuneSystem::$properties['tmp_dir_path'] . '/ex_controls/dots/') . '/' . self::TRANSPARENT_DOT . '.aai';
		if (!file_exists($transparent_dot_path))
		{
			$argb = str_split(self::TRANSPARENT_DOT, 2);
			if (false === file_put_contents($transparent_dot_path, pack("V2C4", 1, 1, hexdec($argb[4]), hexdec($argb[3]), hexdec($argb[2]), hexdec($argb[1]))))
				throw new Exception(get_class(self::$instance).': Attempt to write to the system drive failed!');
		}

		return
			array
			(
				PluginRegularFolderView::async_icon_loading => true,
				PluginRegularFolderView::view_params =>
					array
					(
						FolderViewParams::num_cols => 1,
						FolderViewParams::num_rows => 14,
						FolderViewParams::animation_enabled => false,
						FolderViewParams::scroll_animation_enabled => false,
						FolderViewParams::paint_icon_selection_box=> true,
						FolderViewParams::paint_widget => true,
						FolderViewParams::paint_content_box_background => false,
						FolderViewParams::paint_details => $fw_details_support,
						FolderViewParams::paint_details_box_background => false,
						FolderViewParams::paint_item_info_in_details => $fw_details_support,
						FolderViewParams::details_box_width => 680,
						FolderViewParams::item_detailed_info_font_size => FONT_SIZE_NORMAL,
						FolderViewParams::item_detailed_info_text_color => DEF_LABEL_TEXT_COLOR_WHITE,
						FolderViewParams::item_detailed_info_auto_line_break => false,
						FolderViewParams::item_detailed_info_use_main_icon => false,
						FolderViewParams::item_detailed_info_rel_y => $thumbnails_support? 440 : 74,
						FolderViewParams::detailed_icon_valign => VALIGN_BOTTOM,
						FolderViewParams::content_box_x => 140,
						FolderViewParams::content_box_y => 208,
						FolderViewParams::content_box_width => $fw_details_support? 1066 : 1630,
						FolderViewParams::content_box_height => 772,
						FolderViewParams::paint_path_box => false,
						FolderViewParams::paint_scrollbar => true,
						FolderViewParams::paint_help_line => true,
						FolderViewParams::icon_selection_box_dx => $fw_details_support? -330 : -614,
						FolderViewParams::icon_selection_box_dy => -20,
						FolderViewParams::icon_selection_box_width => $fw_details_support? 816 : 1384,
						FolderViewParams::background_order => 1,
						FolderViewParams::background_path=> CUSTOM_EPG_SCREEN_BG_PATH . '/programs_list.aai',
						FolderViewParams::optimize_full_screen_background => true,
						FolderViewParams::extra_content_objects => 'label{x=10}{y=-98}{color=' . (($day == 'Сб') || ($day == 'Вс')? DEF_LABEL_TEXT_COLOR_CORAL : DEF_LABEL_TEXT_COLOR_GAINSBORO) . '}{text=' . $day . '}label{x=100}{y=-104}{color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '}{text=' . $caption . '}',
					),
				PluginRegularFolderView::base_view_item_params =>
					array
					(
						FolderViewItemParams::item_layout => HALIGN_LEFT,
						FolderViewItemParams::icon_dy => -5,
						FolderViewItemParams::icon_width => 152,
						FolderViewItemParams::icon_height => 46,
						FolderViewItemParams::item_caption_font_size => FONT_SIZE_NORMAL,
						FolderViewItemParams::item_caption_dx => 24,
						FolderViewItemParams::item_detailed_icon_width => $thumbnails_support? 400 : 1,
						FolderViewItemParams::item_detailed_icon_height => $thumbnails_support? 290 : 1,
					),
				PluginRegularFolderView::not_loaded_view_item_params =>
					array
					(
						FolderViewItemParams::icon_path => $transparent_dot_path,
						FolderViewItemParams::item_detailed_icon_path => $transparent_dot_path,
					),
				PluginRegularFolderView::load_failed_view_item_params =>
					array
					(
						FolderViewItemParams::icon_path => $transparent_dot_path,
						FolderViewItemParams::item_detailed_icon_path => $transparent_dot_path,
					),
			);
	}

	private static function get_channels_list_epg_screen_view_params($caption)
	{
		return
			array
			(
				PluginRegularFolderView::async_icon_loading => false,
				PluginRegularFolderView::view_params =>
					array
					(
						FolderViewParams::num_cols => 1,
						FolderViewParams::num_rows => 11,
						FolderViewParams::animation_enabled => false,
						FolderViewParams::scroll_animation_enabled => false,
						FolderViewParams::paint_icon_selection_box=> true,
						FolderViewParams::paint_widget => true,
						FolderViewParams::paint_path_box => false,
						FolderViewParams::paint_scrollbar => true,
						FolderViewParams::paint_help_line => true,
						FolderViewParams::paint_icon_badge_box => true,
						FolderViewParams::paint_content_box_background => false,
						FolderViewParams::content_box_x => 140,
						FolderViewParams::content_box_y => 208,
						FolderViewParams::content_box_width => 1630,
						FolderViewParams::content_box_height => 772,
						FolderViewParams::icon_badge_box_dx => -750,
						FolderViewParams::icon_badge_box_width => 204,
						FolderViewParams::icon_badge_box_height => 63,
						FolderViewParams::icon_selection_box_dx => -574,
						FolderViewParams::icon_selection_box_dy => -20,
						FolderViewParams::icon_selection_box_width => 1340,
						FolderViewParams::icon_selection_box_height => 46,
						FolderViewParams::background_order => 1,
						FolderViewParams::background_path => CUSTOM_EPG_SCREEN_BG_PATH . '/channels_list.aai',
						FolderViewParams::optimize_full_screen_background => true,
						FolderViewParams::extra_content_objects => 'label{x=100}{y=-104}{color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '}{text=' . $caption . '}',
					),
				PluginRegularFolderView::base_view_item_params =>
					array
					(
						FolderViewItemParams::item_paint_icon => true,
						FolderViewItemParams::item_layout => HALIGN_LEFT,
						FolderViewItemParams::icon_width => 75,
						FolderViewItemParams::icon_height => 55,
						FolderViewItemParams::icon_margin_left => 50,
						FolderViewItemParams::icon_keep_aspect_ratio => false,
						FolderViewItemParams::item_override_default_badge => true,
						FolderViewItemParams::item_badge_icon_path => CUSTOM_EPG_RES_PATH . '/channel_badge.png',
						FolderViewItemParams::item_caption_font_size => FONT_SIZE_NORMAL,
						FolderViewItemParams::item_caption_dx => -2,
					),
				PluginRegularFolderView::not_loaded_view_item_params => array(),
				PluginRegularFolderView::load_failed_view_item_params => array(),
			);
	}

	private static function get_groups_list_epg_screen_view_params($caption)
	{
		return
			array
			(
				PluginRegularFolderView::async_icon_loading => false,
				PluginRegularFolderView::view_params =>
					array
					(
						FolderViewParams::num_cols => 1,
						FolderViewParams::num_rows => 11,
						FolderViewParams::animation_enabled => false,
						FolderViewParams::scroll_animation_enabled => false,
						FolderViewParams::paint_icon_selection_box=> true,
						FolderViewParams::paint_widget => true,
						FolderViewParams::paint_content_box_background => false,
						FolderViewParams::content_box_x => 140,
						FolderViewParams::content_box_y => 208,
						FolderViewParams::content_box_width => 1630,
						FolderViewParams::content_box_height => 772,
						FolderViewParams::paint_path_box => false,
						FolderViewParams::paint_scrollbar => true,
						FolderViewParams::paint_help_line => true,
						FolderViewParams::icon_badge_box_dx => -775,
						FolderViewParams::icon_badge_box_height => 62,
						FolderViewParams::icon_badge_box_width => 90,
						FolderViewParams::icon_selection_box_dx => -694,
						FolderViewParams::icon_selection_box_dy => -20,
						FolderViewParams::icon_selection_box_width => 1460,
						FolderViewParams::icon_selection_box_height => 46,
						FolderViewParams::paint_icon_badge_box => true,
						FolderViewParams::background_order => 1,
						FolderViewParams::background_path=> CUSTOM_EPG_SCREEN_BG_PATH . '/groups_list.aai',
						FolderViewParams::optimize_full_screen_background => true,
						FolderViewParams::extra_content_objects => 'label{x=100}{y=-104}{color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '}{text=' . $caption . '}',
					),
				PluginRegularFolderView::base_view_item_params =>
					array
					(
						FolderViewItemParams::item_paint_icon => true,
						FolderViewItemParams::item_layout => HALIGN_LEFT,
						FolderViewItemParams::icon_width => 90,
						FolderViewItemParams::icon_height => 50,
						FolderViewItemParams::icon_margin_left => -24,
						FolderViewItemParams::item_override_default_badge => true,
						FolderViewItemParams::item_badge_icon_path => CUSTOM_EPG_RES_PATH . '/group_icon_badge.png',
						FolderViewItemParams::item_caption_font_size => FONT_SIZE_NORMAL,
						FolderViewItemParams::item_caption_dx => 88,
					),
				PluginRegularFolderView::not_loaded_view_item_params => array(),
				PluginRegularFolderView::load_failed_view_item_params => array(),
			);
	}

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

	private function display($action)
	{
		$counter = 0;
		$post_action = $action;

		while(is_array($post_action))
		{
			if (isset($post_action[GuiAction::handler_string_id]) && (($post_action[GuiAction::handler_string_id] == PLUGIN_OPEN_FOLDER_ACTION_ID) || ($post_action[GuiAction::handler_string_id] == SHOW_DIALOG_ACTION_ID)))
			{
				$counter++;
				$post_action = isset($post_action[GuiAction::data][CloseAndRunActionData::post_action])? $post_action[GuiAction::data][CloseAndRunActionData::post_action] : null;
				continue;
			}

			break;
		}

		while($this->screens_counter-- > 0)
			$action = ActionFactorySafe::close_and_run($action);

		$this->screens_counter = $counter;

		return $action;
	}

	private function hide($post_action = null)
	{
		$action = $post_action;

		while($this->screens_counter-- > 0)
			$action = ActionFactorySafe::close_and_run($action);

		$this->screens_counter = 0;

		return $action;
	}

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

	protected function get_update_epg_action($channel_id, $day_ts, &$plugin_cookies, $post_action = null)
	{
		if ((($this->stb_firmware->branch_num == 11) && ($this->stb_firmware->build_date >= 190919)) || ($this->stb_firmware->branch_num > 12))
		{
			//r11, r13
			$epg = $this->epg->get_day_epg($channel_id, $day_ts, $plugin_cookies);

			if (!empty($epg))
				return ActionFactorySafe::update_epg($channel_id, true, $day_ts, $epg, $post_action);
		}

		return $post_action;
	}

	protected function get_shown_program_data($channel_id, $find_result)
	{
		$data =
			array
			(
				CustomExtEpgProgram::validated => isset($find_result[PluginTvEpgProgram::start_tm_sec]),
				CustomExtEpgProgram::day_start_ts => $find_result['day_ts'],
				CustomExtEpgProgram::start_tm_sec => isset($find_result[PluginTvEpgProgram::start_tm_sec])? $find_result[PluginTvEpgProgram::start_tm_sec] : 0,
				CustomExtEpgProgram::end_tm_sec => $find_result[PluginTvEpgProgram::end_tm_sec],
				CustomExtEpgProgram::details => array(),
			);

		if ($data[CustomExtEpgProgram::validated] && !empty($data[CustomExtEpgProgram::start_tm_sec]))
		{
			if (is_null($epg = $this->epg->get_epg_data($channel_id, $data[CustomExtEpgProgram::day_start_ts], $data[CustomExtEpgProgram::start_tm_sec])))
			{
				$data[CustomExtEpgProgram::validated] = false;

				return $data;
			}

			$data[CustomExtEpgProgram::desc] = explode('|', iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', trim(str_replace("\n", '|', preg_replace('/(!|\?)\.+\s*$/Uu', '$1', $epg[ExtEpgProgram::desc])))), 73, '|', true)));
			$data[CustomExtEpgProgram::icon_url] = isset($epg[ExtEpgProgram::main_icon])? $epg[ExtEpgProgram::main_icon] : '';
			$title = explode('|', iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', empty($epg[ExtEpgProgram::sub_title])? $epg[ExtEpgProgram::title] : $epg[ExtEpgProgram::title] . (preg_match('/[,.:;!?]$/', trim($epg[ExtEpgProgram::title]))? ' ' : '. ') . $epg[ExtEpgProgram::sub_title]), 73, '|', true)));

			if (count($title) > 2)
				$title[1] = rtrim($title[1], ".,?!([{\x20") . '...';

			$data[CustomExtEpgProgram::title] = array_slice($title, 0, 2);
			$details = '';
			// Возрастное ограничение: [Х+]
			if (!empty($epg[ExtEpgProgram::main_category]))
				$details .= preg_match('/^(х\/ф|т\/с)\s+/iu', $epg[ExtEpgProgram::title])? preg_replace('/\](.*)$/uU', ']', $epg[ExtEpgProgram::main_category]) . ' ' : $epg[ExtEpgProgram::main_category] . ', ';
			// Рейтинг IMDB
			if (!empty($epg[ExtEpgProgram::imdb_rating]))
				$details .= ':IMDB: ' . $epg[ExtEpgProgram::imdb_rating] . ', ';
			// Рейтинг Кинопоиск
			if (!empty($epg[ExtEpgProgram::kp_rating]))
				$details .= ':KP: ' . $epg[ExtEpgProgram::kp_rating] . ', ';
			// Страна
			if (!empty($epg[ExtEpgProgram::country]))
				$details .= $epg[ExtEpgProgram::country] . ', ';
			// Год производства
			if (!empty($epg[ExtEpgProgram::year]))
				$details .= $epg[ExtEpgProgram::year] . ', ';
			// Если указан директор,
			if (!empty($epg[ExtEpgProgram::director]))
			{
				// и если указаны актеры,
				if (!empty($epg[ExtEpgProgram::actor]))
				{
					// то директор является режиссером, включаем его с актерами в описание.
					$details .= 'реж. ' . implode(', ', $epg[ExtEpgProgram::director]) . '; актеры: ' . implode(', ', $epg[ExtEpgProgram::actor]) . '; ' .
							(empty($epg[ExtEpgProgram::producer])? '' : 'прод. ' . implode(', ', $epg[ExtEpgProgram::producer]) . '; ') .
							(empty($epg[ExtEpgProgram::writer])? '' : 'сцен. ' . implode(', ', $epg[ExtEpgProgram::writer]) . '; ') .
							(empty($epg[ExtEpgProgram::operator])? '' : 'опер. ' . implode(', ', $epg[ExtEpgProgram::operator]) . '; ') .
							(empty($epg[ExtEpgProgram::composer])? '' : 'комп. ' . implode(', ', $epg[ExtEpgProgram::composer]) . '; ');
				}
				else // иначе (если актеры не указаны),
					// директор является автором
					$details .= 'авт. ' . implode(', ', $epg[ExtEpgProgram::director]);
			}
			else
			{ // Иначе (если директор не указан)
				// Если указаны актеры, то они являются ведущими
				$details .= (empty($epg[ExtEpgProgram::actor])? '' : 'актеры: ' . implode(', ', $epg[ExtEpgProgram::actor]) . '; ') .
							(empty($epg[ExtEpgProgram::producer])? '' : 'прод. ' . implode(', ', $epg[ExtEpgProgram::producer]) . '; ') .
							(empty($epg[ExtEpgProgram::writer])? '' : 'сцен. ' . implode(', ', $epg[ExtEpgProgram::writer]) . '; ') .
							(empty($epg[ExtEpgProgram::operator])? '' : 'опер. ' . implode(', ', $epg[ExtEpgProgram::operator]) . '; ') .
							(empty($epg[ExtEpgProgram::composer])? '' : 'комп. ' . implode(', ', $epg[ExtEpgProgram::composer]) . '; ');
				// Если указан ведущий
				if (!empty($epg[ExtEpgProgram::presenter]))
					$details .= 'вед. ' . implode(', ', $epg[ExtEpgProgram::presenter]);
			}

			$details = trim($details, "\t\n\r\0\x20;,.");
			$data[CustomExtEpgProgram::details] = empty($details)? array() : explode('|', iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', preg_replace('/(!|\?)\.+\s*$/Uu', '$1', $details . '.')), empty($data[CustomExtEpgProgram::icon_url])? 74 : 54, '|', true)));

			if (empty($data[CustomExtEpgProgram::icon_url]))
				$data[CustomExtEpgProgram::details] = array_merge($data[CustomExtEpgProgram::details], $data[CustomExtEpgProgram::desc]);

			foreach ($data[CustomExtEpgProgram::details] as &$line)
				$line = preg_replace(array('/(\sАвт\.|\sвед\.|\sРеж\.|актеры:|Актеры:|прод\.|сцен\.|опер\.|комп\.)/'), array("</text><text color=" . DEF_LABEL_TEXT_COLOR_LIGHTYELLOW . ">$1</text><text>"), preg_replace(array('/:IMDB:/', '/:KP:/'), array("</text><gap width=6/><icon dy=4>{$this->imdb_rating_icon}</icon><text>", "</text><gap width=6/><icon dy=4>{$this->kp_rating_icon}</icon><text>"), $line));
		}

		return $data;
	}

	protected function get_main_dialog_controls_defs(MediaURL $media_url, &$player_state, &$plugin_cookies)
	{
		static $channel_id, $start_tm_utc, $channel_data, $scrolling_pos;

		if (!isset($channel_id) || ($channel_id <> $media_url->channel_id) || (!empty($this->program_data[CustomExtEpgProgram::start_tm_sec]) && ($this->program_data[CustomExtEpgProgram::start_tm_sec] <> $start_tm_utc)))
		{
			$start_tm_utc = empty($this->program_data[CustomExtEpgProgram::start_tm_sec])? null : $this->program_data[CustomExtEpgProgram::start_tm_sec];
			$channel_id = $media_url->channel_id;
			$channel = $this->epg->get_channel($channel_id);
			$channel_data =
				array
				(
					ExtTvChannel::number => str_pad($channel[ExtTvChannel::number], 3, '0', STR_PAD_LEFT),
					ExtTvChannel::caption => $channel[ExtTvChannel::caption],
					ExtTvChannel::icon_url => $channel[ExtTvChannel::icon_url],
					ExtTvChannel::have_archive => $channel[ExtTvChannel::have_archive],
					ExtTvChannel::archive_delay_sec => $channel[ExtTvChannel::archive_delay_sec],
					ExtTvChannel::archive_past_sec => $channel[ExtTvChannel::archive_past_sec],
					'audio_tracks' => null,
				);
		}

		$local_tm_ts = time() - $this->epg->time_zone_offset;
		$num_length = strlen($channel_data[ExtTvChannel::number]);
		ControlFactory::add_vgap($defs, -6);
		ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/dialog_main_bar.png</icon>');
		ControlFactory::add_vgap($defs, -69);
		ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 390) . '/><icon>' . CUSTOM_EPG_RES_PATH . '/dialog_time_bar.png</icon>');
		ControlFactory::add_vgap($defs, -61);
		ControlFactory::add_smart_label($defs, null, '<gap width=' . ((($num_length > 2)? 0 : (($num_length > 1)? 23 : 46)) + 10) . '/><text color=' . DEF_LABEL_TEXT_COLOR_SKYBLUE . '>' . $channel_data[ExtTvChannel::number] . '</text>');
		ControlFactory::add_vgap($defs, -69);
		ControlFactory::add_smart_label($defs, null, '<gap width=90/><icon width=69 height=50 dy=1>' . $channel_data[ExtTvChannel::icon_url] . '</icon><gap width=30/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . $channel_data[ExtTvChannel::caption] . '</text>');
		ControlFactory::add_vgap($defs, -66);
		ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 367) . '/>' . self::$days_ru[strftime('%w', $local_tm_ts)] . '<gap width=30/><text>' . strftime('%d.%m.%y', $local_tm_ts) . '</text>');
		ControlFactory::add_vgap($defs, -69);
		ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 120) . '/><text>' . strftime('%H:%M', $local_tm_ts) . '</text>');
		ControlFactory::add_vgap($defs, -12);

		if ($this->display_playable_stream_info)
		{
			ControlFactory::add_vgap($defs, 30);
			$vgap = 0;
			$tv_playback_url = explode('|', iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', trim(str_replace("\n", '|', $this->tv_playback_url))), 52, '|', true)));

			foreach ($tv_playback_url as $idx => $line)
			{
				ControlFactory::add_label($defs, (empty($idx)? 'URL потока:' : ''), $line);
				ControlFactory::add_vgap($defs, -25);
				$vgap += 44;
			}

			ControlFactory::add_vgap($defs, 10);
			$vgap -= 10;
			ControlFactory::add_label($defs, 'Разрешение видео: ', (isset($player_state['playback_video_width']) && ($player_state['playback_video_width'] > 0) && ($player_state['playback_video_height'] > 0))? ($player_state['playback_video_width'] . 'x' . $player_state['playback_video_height']) : 'n/a');
			ControlFactory::add_vgap($defs, -15);
			ControlFactory::add_label($defs, 'Текущий битрейт:', (isset($player_state['playback_current_bitrate']) && ($player_state['playback_current_bitrate'] > 0))? round($player_state['playback_current_bitrate'] / 1000000, 2) . ' Мбит/с' : 'n/a');
			ControlFactory::add_vgap($defs, -15);

			if (empty($channel_data['audio_tracks']))
				$channel_data['audio_tracks'] = get_audio_tracks_description();

			if (empty($channel_data['audio_tracks']))
			{
				ControlFactory::add_label($defs, 'Звуковые дорожки:', 'n/a');
				ControlFactory::add_vgap($defs, -15);
				$vgap += 54;
			}
			else
			{
				foreach ($channel_data['audio_tracks'] as $idx => $value)
				{
					ControlFactory::add_label($defs, (empty($idx)? 'Звуковые дорожки:' : ''), strtoupper('    ' . $value['codec'] . ' ' . $value['lang'] . ' (PID: ' . $value['pid'] . ')'));

					if ($player_state['audio_track'] == $idx)
					{
						ControlFactory::add_vgap($defs, -69);
						ControlFactory::add_smart_label($defs, null, '<gap width=420/><icon>' . CUSTOM_EPG_RES_PATH . '/checkbox.png</icon>');
						ControlFactory::add_vgap($defs, -15);
					}
					else
						ControlFactory::add_vgap($defs, -15);

					$vgap += 54;
				}
			}

			ControlFactory::add_vgap($defs, 542 + 5 + 42 - $vgap);
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/bottom_bar.png</icon>');
			ControlFactory::add_vgap($defs, -58);
			ControlFactory::add_smart_label($defs, null, '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . (($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)? '/select_btn.png' : '/info_btn.png') . '</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Телегид</text>');
			ControlFactory::add_vgap($defs, -10);

			return $defs;

		}
		else
		{
			$ch_tech_info_width = 4;
			$ch_tech_info_bar = '';

			if (isset($player_state['playback_video_height']))
			{
				$rows = $player_state['playback_video_height'];
				$rows = ($rows > 240)? (($rows > 360)? (($rows > 480)? (($rows > 540)? (($rows > 576)? (($rows > 720)? (($rows > 1080)? 2160 : 1080) : 720) : 576) : 540) : 480) : 360) : 240;
				$ch_tech_info_bar = "<icon>" . CUSTOM_EPG_RES_PATH . "/labels/frame_def_$rows.png</icon>";
				$ch_tech_info_width += 55;
			}

			if ((strpos($this->tv_playback_url, 'dvb://') !== false))
			{
				$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/dvb.png</icon>';
				$ch_tech_info_width += 77;
			}

			if (isset($player_state['audio_track']) && isset($player_state['audio_track.' . $player_state['audio_track'] . '.codec']))
			{
				$audio_track_logo = str_replace(' ', '_', strtolower($player_state['audio_track.' . $player_state['audio_track'] . '.codec']));

				if (file_exists(CUSTOM_EPG_RES_PATH . '/labels/audio_codec_' . $audio_track_logo) . '.png')
				{
					$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/audio_codec_' . $audio_track_logo . '.png</icon>';
					$ch_tech_info_width += 98;
				}
			}

			if (isset($player_state['teletext_available']) && $player_state['teletext_available'])
			{
				$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/teletext.png</icon>';
				$ch_tech_info_width += 75;
			}

			if ($ch_tech_info_width > 4)
			{
				ControlFactory::add_vgap($defs, -61);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - $ch_tech_info_width - 5 - 390) . '/>' . $ch_tech_info_bar);
			}
		}

		ControlFactory::add_vgap($defs, -8);

		if ($this->display_please_wait)
		{
			ControlFactory::add_vgap($defs, 344);
			ControlFactory::add_smart_label($defs, null, '<gap width=700/><text>Загрузка...</text>');
			ControlFactory::add_vgap($defs, 404);
			return $defs;
		}

		$scroll_available = false;
		$reminder_available = false;
		$reminder_enabled = false;
		$live = empty($this->tv_playback_archive_start_ts);

		if (!$this->program_data[CustomExtEpgProgram::validated])
		{
			ControlFactory::add_vgap($defs, 330);
			ControlFactory::add_smart_label($defs, null, '<gap width=560/><text>Нет программы передач</text>');
			ControlFactory::add_vgap($defs, 335);
		}
		else
		{
			$tv_program_start_ts = $start_tm_utc - $this->epg->time_zone_offset;
			$tv_program_stop_ts = $this->program_data[CustomExtEpgProgram::end_tm_sec] - $this->epg->time_zone_offset;
			$tv_playback_ts = (empty($this->tv_playback_archive_start_ts) || $this->display_live_epg_item)? $this->playback_live_ts : $this->playback_archive_ts;
			$playback_is_complete = !(($tv_program_start_ts <= $tv_playback_ts) && ($tv_program_stop_ts >= $tv_playback_ts));

			if (($live && !$playback_is_complete) || $this->display_live_epg_item)
				$tv_program_date = 'LIVE ';
			else
			{
				$tv_program_date = strftime('%d.%m.%Y', $tv_program_start_ts);

				if ($tv_program_date == strftime('%d.%m.%Y', $local_tm_ts))
					$tv_program_date = 'Сегодня';
			}
			$playback_remain_tm_sec = $local_tm_ts - $tv_program_start_ts;
			$this->replay_available = $channel_data[ExtTvChannel::have_archive] && ($playback_remain_tm_sec > $channel_data[ExtTvChannel::archive_delay_sec]) && ($playback_remain_tm_sec <= $channel_data[ExtTvChannel::archive_past_sec]);
			ControlFactory::add_vgap($defs, 10);
			ControlFactory::add_smart_label($defs, null, (($this->replay_available && !($live && !$playback_is_complete))? '<text color=' . DEF_LABEL_TEXT_COLOR_RED . '>R </text>' : '') . '<text color=' . DEF_LABEL_TEXT_COLOR_LIGHTGREEN . '>' . $tv_program_date . strftime('  %H:%M', $tv_program_start_ts) . '-' . strftime('%H:%M', $tv_program_stop_ts) . '</text>');

			if (!$playback_is_complete)
			{
				$tv_playback_ts -= $tv_program_start_ts;
				$pos_percent = round(($tv_playback_ts * 100) / ($tv_program_stop_ts - $tv_program_start_ts));
				ControlFactory::add_vgap($defs, -64);
				ControlFactoryExt::add_progressbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH - 1090, 780, $pos_percent);
				ControlFactory::add_vgap($defs, -74);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 270) . '/><text>' . $pos_percent . '%</text>');
				ControlFactory::add_vgap($defs, -69);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 148) . '/><text>(' . strftime('%H:%M', $tv_playback_ts).')</text>');
			}
			else
			{
				$reminder_enabled = ($start_tm_utc - time() >= 60);

				if ($reminder_enabled)
				{
					$reminders = $this->epg->get_reminders_list($plugin_cookies);
					$reminder_available = isset($reminders[$channel_id][$start_tm_utc]);

					if ($reminder_available)
					{
						ControlFactory::add_vgap($defs, -69);
						ControlFactory::add_smart_label($defs, null, '<gap width=1570/><icon width=40 height=40>' . CUSTOM_EPG_RES_PATH . '/bell.png</icon>');
					}
				}
			}

			ControlFactory::add_vgap($defs, -12);
			ControlFactory::add_label($defs, null, empty($this->program_data[CustomExtEpgProgram::title][0])? '' : $this->program_data[CustomExtEpgProgram::title][0]);
			ControlFactory::add_vgap($defs, -28);
			ControlFactory::add_label($defs, null, empty($this->program_data[CustomExtEpgProgram::title][1])? '' : $this->program_data[CustomExtEpgProgram::title][1]);
			ControlFactory::add_vgap($defs, -16);
			ControlFactory::add_smart_label($defs, null, '<icon width=400 height=300>' . $this->program_data[CustomExtEpgProgram::icon_url] . '</icon>');
			ControlFactory::add_vgap($defs, -78);

			$max_visible_lines = empty($this->program_data[CustomExtEpgProgram::icon_url])? 12 : 7;
			$count_lines = count($this->program_data[CustomExtEpgProgram::details]);
			$invisible_lines = max($count_lines - $max_visible_lines, 0);

			if ($this->scroll_lines_position < 0)
				$this->scroll_lines_position = 0;

			if (empty($this->scroll_lines_position))
				$scrolling_pos = 0;

			if ($this->scroll_page_event <> 0)
			{
				if ($this->scroll_page_event > 0)
				{
					if (($this->scroll_lines_position + $max_visible_lines) < $count_lines)
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;

						if (($this->scroll_lines_position + $max_visible_lines) > $count_lines)
							$this->scroll_lines_position = $invisible_lines;

						$this->scroll_page_event = 0;
					}
				}
				else
				{
					if (empty($scrolling_pos))
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;

						if ($this->scroll_lines_position < 0)
							$this->scroll_lines_position = 0;

						$this->scroll_page_event = 0;
					}
				}
			}

			$line_idx = ($count_lines > $max_visible_lines)? ((($this->scroll_lines_position + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $this->scroll_lines_position) : 0;

			if ($count_lines > $max_visible_lines)
			{
				$scroll_available = true;
				$line_idx = (($this->scroll_lines_position + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $this->scroll_lines_position;
			}
			else
				$line_idx = 0;

			$line = $line_idx;
			$lines = $max_visible_lines;

			while ($lines > 0)
			{
				$string = (isset($this->program_data[CustomExtEpgProgram::details][$line]))? $this->program_data[CustomExtEpgProgram::details][$line++] : '';
				ControlFactory::add_smart_label($defs, null, (empty($this->program_data[CustomExtEpgProgram::icon_url])? '' : '<gap width=430/>') . '<text>' . $string . '</text>');
				ControlFactory::add_vgap($defs, -25);
				$lines--;
			}

			ControlFactoryExt::add_scrollbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH, 44, $max_visible_lines, $count_lines, $line_idx);
			ControlFactory::add_vgap($defs, 10);

			if (!empty($this->program_data[CustomExtEpgProgram::icon_url]))
			{
				$notes_max_visible_lines = $max_visible_lines;
				$notes_count_lines = $count_lines;
				$notes_invisible_lines = $invisible_lines;
				$max_visible_lines = 6;
				$count_lines = count($this->program_data[CustomExtEpgProgram::desc]);
				$invisible_lines = max($count_lines - $max_visible_lines, 0);
				$invisible_lines += $notes_invisible_lines;

				if ($this->scroll_page_event <> 0)
				{
					if ($this->scroll_page_event > 0)
					{
						if (($this->scroll_lines_position + $max_visible_lines) < $count_lines + $notes_invisible_lines)
							$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;
					}
					else
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;
						if ($this->scroll_lines_position < $notes_invisible_lines)
							$this->scroll_lines_position = $notes_invisible_lines;
					}
					$this->scroll_page_event = 0;
				}

				if ($this->scroll_lines_position > $invisible_lines)
					$this->scroll_lines_position = $invisible_lines;

				$scrolling_pos = ($notes_count_lines > $notes_max_visible_lines)? ((($this->scroll_lines_position + $notes_max_visible_lines) > $notes_count_lines)?  $notes_max_visible_lines + $this->scroll_lines_position - $notes_count_lines : 0) : $this->scroll_lines_position;

				if ($this->scroll_lines_position > $scrolling_pos + $notes_invisible_lines)
					$this->scroll_lines_position = $scrolling_pos + $notes_invisible_lines;

				if ($count_lines > $max_visible_lines)
				{
					$scroll_available = true;
					$line_idx = (($scrolling_pos + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $scrolling_pos;
				}
				else
					$line_idx = 0;

				$line = $line_idx;
				$lines = $max_visible_lines;

				while ($lines > 0)
				{
					$string = (isset($this->program_data[CustomExtEpgProgram::desc][$line]))? $this->program_data[CustomExtEpgProgram::desc][$line++] : '';
					ControlFactory::add_smart_label($defs, null, '<text>' . $string . '</text>');
					ControlFactory::add_vgap($defs, -25);
					$lines--;
				}
				ControlFactoryExt::add_scrollbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH, 44, $max_visible_lines, $count_lines, $line_idx);
			}
			else ControlFactory::add_vgap($defs, 44);
		}

		ControlFactory::add_vgap($defs, 13);
		ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/bottom_bar.png</icon>');
		ControlFactory::add_vgap($defs, -58);
		ControlFactory::add_smart_label($defs, null,
			((!$this->program_data[CustomExtEpgProgram::validated])? '' : '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_left_btn.png</icon><gap width=6/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_right_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Навигация</text>') .
			'<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . (($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)? '/select_btn.png' : '/info_btn.png') . '</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Тех. Инфо</text>'.
			((!$this->program_data[CustomExtEpgProgram::validated])? '' : ($this->replay_available? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/enter_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_CORAL . ' size=small>Повтор</text>' : ($reminder_enabled? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/enter_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . ' size=small>' . ($reminder_available? 'Не напоминать' : 'Напомнить') . '</text>' : ''))) .
			((!$channel_data[ExtTvChannel::have_archive] || !$this->program_data[CustomExtEpgProgram::validated])? '' : ($live? '' : '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/play_btn.png</icon><gap width=12/><text color=' . ($this->display_live_epg_item? DEF_LABEL_TEXT_COLOR_LIGHTGREEN : DEF_LABEL_TEXT_COLOR_SILVER) . ' size=small>Эфир</text>')) .
			(($scroll_available)? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_up_btn.png</icon><gap width=6/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_down_btn.png</icon><gap width=12/><icon>' . CUSTOM_EPG_RES_PATH . '/page_minus_btn.png</icon><gap width=6/><icon>' . CUSTOM_EPG_RES_PATH . '/page_plus_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Прокрутка</text>' : ''));
		ControlFactory::add_vgap($defs, -10);

		return $defs;
	}

	protected function get_main_screen_controls_defs(MediaURL $media_url, &$player_state, &$plugin_cookies)
	{
		static $channel_id, $start_tm_utc, $channel_data, $scrolling_pos;

		if (!isset($channel_id) || ($channel_id <> $media_url->channel_id) || (!empty($this->program_data[CustomExtEpgProgram::start_tm_sec]) && ($this->program_data[CustomExtEpgProgram::start_tm_sec] <> $start_tm_utc)))
		{
			$start_tm_utc = empty($this->program_data[CustomExtEpgProgram::start_tm_sec])? null : $this->program_data[CustomExtEpgProgram::start_tm_sec];
			$channel_id = $media_url->channel_id;
			$channel = $this->epg->get_channel($channel_id);
			$channel_data =
				array
				(
					ExtTvChannel::number => $channel[ExtTvChannel::number],
					ExtTvChannel::caption => $channel[ExtTvChannel::caption],
					ExtTvChannel::icon_url => $channel[ExtTvChannel::icon_url],
					ExtTvChannel::have_archive => $channel[ExtTvChannel::have_archive],
					ExtTvChannel::archive_delay_sec => $channel[ExtTvChannel::archive_delay_sec],
					ExtTvChannel::archive_past_sec => $channel[ExtTvChannel::archive_past_sec],
					'audio_tracks' => null,
				);
		}

		$local_tm_ts = time() - $this->epg->time_zone_offset;
		$num_length = strlen($channel_data[ExtTvChannel::number]);
		ControlFactory::add_vgap($defs, -163);
		ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_SCREEN_BG_PATH . '/program_info.aai</icon>');

		if ($media_url->screen_id == CustomEpgScreenDefs::programs_list)
		{
			$day = preg_replace('/\<.*\>/U', '', self::$days_ru[strftime('%w', $media_url->day_start_tm_utc)]);
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/calendar.aai</icon>');
			ControlFactory::add_vgap($defs, -56);
			ControlFactory::add_smart_label($defs, null, '<gap width=23/><text color=' . (($day == 'Сб') || ($day == 'Вс')? DEF_LABEL_TEXT_COLOR_CORAL : DEF_LABEL_TEXT_COLOR_GAINSBORO) . '>' . $day . '</text>');
			ControlFactory::add_vgap($defs, -75);
			ControlFactory::add_smart_label($defs, null, '<gap width=113/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . strftime('Программа передач на %d.%m.%Y', $media_url->day_start_tm_utc) . '</text>');
			ControlFactory::add_vgap($defs, 332);

			if ($this->display_please_wait)
				ControlFactory::add_smart_label($defs, null, '<gap width=700/><text>Загрузка...</text>');
			else
			{
				ControlFactory::add_smart_label($defs, null, '<gap width=560/><text>Нет программы передач</text>');
				ControlFactory::add_vgap($defs, 410);
				ControlFactory::add_smart_label($defs, null,
					'<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/return_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Каналы</text>' .
					'<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_left_btn.png</icon><gap width=6/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_right_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Дни недели</text>');
			}

			return $defs;
		}

		ControlFactory::add_vgap($defs, -63);
		ControlFactory::add_smart_label($defs, null, '<gap width=' . ((($num_length > 2)? 0 : (($num_length > 1)? 23 : 46)) + 10) . '/><text color=' . DEF_LABEL_TEXT_COLOR_SKYBLUE . '>' . $channel_data[ExtTvChannel::number] . '</text>');
		ControlFactory::add_vgap($defs, -69);
		ControlFactory::add_smart_label($defs, null, '<gap width=90/><icon width=69 height=50 dy=1>' . $channel_data[ExtTvChannel::icon_url] . '</icon><gap width=30/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . $channel_data[ExtTvChannel::caption] . '</text>');

		if ($this->display_playable_stream_info)
		{
			ControlFactory::add_vgap($defs, 50);
			$vgap = 0;
			$tv_playback_url = explode('|', iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', trim(str_replace("\n", '|', $this->tv_playback_url))), 52, '|', true)));

			foreach ($tv_playback_url as $idx => $line)
			{
				if (empty($idx))
				{
					ControlFactory::add_smart_label($defs, null, '<gap width=40/><text color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>URL потока:</text>');
					ControlFactory::add_vgap($defs, -69);
					ControlFactory::add_smart_label($defs, null, '<gap width=450/><text>' . $line . '</text>');
				}
				else
					ControlFactory::add_smart_label($defs, null, '<gap width=450/><text>' . $line . '</text>');

				ControlFactory::add_vgap($defs, -25);
				$vgap += 44;
			}

			$vgap -= 10;
			$android_bw = '';
			ControlFactory::add_vgap($defs, 10);
			ControlFactory::add_smart_label($defs, null, '<gap width=40/><text color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>Разрешение видео:</text>');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '<gap width=450/><text>' . ((($player_state['playback_video_width'] > 0) && ($player_state['playback_video_height'] > 0))? ($player_state['playback_video_width'] . 'x' . $player_state['playback_video_height']) : 'n/a') . '</text>');
			ControlFactory::add_vgap($defs, -15);
			ControlFactory::add_smart_label($defs, null, '<gap width=40/><text color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>Текущий битрейт:</text>');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '<gap width=450/><text>' . (empty($android_bw)? (($player_state['playback_current_bitrate'] > 0)? round($player_state['playback_current_bitrate'] / 1000000, 2) . ' Мбит/с' : 'n/a') : round($android_bw / 100000, 2) . ' Мбит/с') . '</text>');
			ControlFactory::add_vgap($defs, -15);

			if (empty($channel_data['audio_tracks']))
				$channel_data['audio_tracks'] = get_audio_tracks_description();

			foreach ($channel_data['audio_tracks'] as $idx => $value)
			{
				if (empty($idx))
				{
					ControlFactory::add_smart_label($defs, null, '<gap width=40/><text color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>Звуковые дорожки:</text>');
					ControlFactory::add_vgap($defs, -69);
					ControlFactory::add_smart_label($defs, null, '<gap width=500/><text>' . strtoupper($value['codec'] . ' ' . $value['lang'] . ' (PID: ' . $value['pid'] . ')') . '</text>');
				}
				else
					ControlFactory::add_smart_label($defs, null, '<gap width=500/><text>' . strtoupper($value['codec'] . ' ' . $value['lang'] . ' (PID: ' . $value['pid'] . ')') . '</text>');

				if ($player_state['audio_track'] == $idx)
				{
					ControlFactory::add_vgap($defs, -69);
					ControlFactory::add_smart_label($defs, null, '<gap width=450/><icon>' . CUSTOM_EPG_RES_PATH . '/checkbox.png</icon>');
				}

				ControlFactory::add_vgap($defs, -15);
				$vgap += 54;
			}

			ControlFactory::add_vgap($defs, 692 - $vgap);
			ControlFactory::add_vgap($defs, -58);
			ControlFactory::add_smart_label($defs, null, '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . (($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)? '/select_btn.png' : '/info_btn.png') . '</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Телегид</text>');
			ControlFactory::add_vgap($defs, -10);

			return $defs;
		}

		$reminder_available = false;
		$reminder_enabled = false;

		if (($this->tv_playback_channel_id == $channel_id))
		{
			$ch_tech_info_width = 4;
			$ch_tech_info_bar = '';

			if (isset($player_state['playback_video_height']))
			{
				$rows = $player_state['playback_video_height'];
				$rows = ($rows > 240)? (($rows > 360)? (($rows > 480)? (($rows > 540)? (($rows > 576)? (($rows > 720)? (($rows > 1080)? 2160 : 1080) : 720) : 576) : 540) : 480) : 360) : 240;
				$ch_tech_info_bar = "<icon>" . CUSTOM_EPG_RES_PATH . "/labels/frame_def_$rows.png</icon>";
				$ch_tech_info_width += 55;
			}

			if ((strpos($this->tv_playback_url, 'dvb://') !== false))
			{
				$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/dvb.png</icon>';
				$ch_tech_info_width += 77;
			}

			if (isset($player_state['audio_track']) && isset($player_state['audio_track.' . $player_state['audio_track'] . '.codec']))
			{
				$audio_track_logo = str_replace(' ', '_', strtolower($player_state['audio_track.' . $player_state['audio_track'] . '.codec']));
				if (file_exists(CUSTOM_EPG_RES_PATH . '/labels/audio_codec_' . $audio_track_logo) . '.png')
				{
					$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/audio_codec_' . $audio_track_logo . '.png</icon>';
					$ch_tech_info_width += 98;
				}
			}

			if (isset($player_state['teletext_available']) && $player_state['teletext_available'])
			{
				$ch_tech_info_bar .= '<gap width=10/><icon>' . CUSTOM_EPG_RES_PATH . '/labels/teletext.png</icon>';
				$ch_tech_info_width += 75;
			}

			if ($ch_tech_info_width > 4)
			{
				ControlFactory::add_vgap($defs, -68);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - $ch_tech_info_width - 390) . '/>' . $ch_tech_info_bar);
			}
		}

		ControlFactory::add_vgap($defs, -8);

		if ($this->display_please_wait)
		{
			ControlFactory::add_vgap($defs, 340);
			ControlFactory::add_smart_label($defs, null, '<gap width=700/><text>Загрузка...</text>');
			ControlFactory::add_vgap($defs, 344);
			return $defs;
		}

		$scroll_available = false;
		$live = empty($this->tv_playback_archive_start_ts);
		$not_validated = !$this->program_data[CustomExtEpgProgram::validated] || empty($this->program_data[CustomExtEpgProgram::day_start_ts]);

		if ($not_validated)
		{
			$this->display_live_epg_item = ($this->tv_playback_channel_id <> $channel_id);
			ControlFactory::add_vgap($defs, 340);
			ControlFactory::add_smart_label($defs, null, '<gap width=560/><text>Нет программы передач</text>');
			ControlFactory::add_vgap($defs, 403);
		}
		else
		{
			$tv_program_start_ts = $start_tm_utc - $this->epg->time_zone_offset;
			$tv_program_stop_ts = $this->program_data[CustomExtEpgProgram::end_tm_sec] - $this->epg->time_zone_offset;
			$tv_playback_ts = (empty($this->tv_playback_archive_start_ts) || $this->display_live_epg_item || ($this->tv_playback_channel_id <> $channel_id))? $this->playback_live_ts : $this->playback_archive_ts;
			$playback_is_complete = !(($tv_program_start_ts <= $tv_playback_ts) && ($tv_program_stop_ts >= $tv_playback_ts));
			$live = (empty($this->tv_playback_archive_start_ts));

			if ((($live || ($this->tv_playback_channel_id <> $channel_id)) && !$playback_is_complete) || $this->display_live_epg_item)
			{
				$tv_program_date = 'LIVE ';
				$this->display_live_epg_item = true;
			}
			else
			{
				$tv_program_date = strftime('%d.%m.%Y', $tv_program_start_ts);

				if ($tv_program_date == strftime('%d.%m.%Y', $local_tm_ts))
					$tv_program_date = 'Сегодня ';
			}

			$playback_remain_tm_sec = $local_tm_ts - $tv_program_start_ts;
			$this->replay_available = $channel_data[ExtTvChannel::have_archive] && ($playback_remain_tm_sec > $channel_data[ExtTvChannel::archive_delay_sec]) && ($playback_remain_tm_sec <= $channel_data[ExtTvChannel::archive_past_sec]);
			ControlFactory::add_vgap($defs, 48);
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/title_bar.png</icon>');
			ControlFactory::add_vgap($defs, -63);
			ControlFactory::add_smart_label($defs, null, (($this->replay_available && !($live && !$playback_is_complete))? '<gap width=30/><text color=' . DEF_LABEL_TEXT_COLOR_RED . '>R </text>' : '<gap width=30/>') . '<text color=' . DEF_LABEL_TEXT_COLOR_LIGHTGREEN . '>' . $tv_program_date . strftime(' %H:%M', $tv_program_start_ts) . (($tv_program_stop_ts > 0)? '-' . strftime('%H:%M', $tv_program_stop_ts) : '') . '</text>');

			if (!$playback_is_complete)
			{
				$tv_playback_ts -= $tv_program_start_ts;
				$pos_percent = round(($tv_playback_ts * 100) / ($tv_program_stop_ts - $tv_program_start_ts));
				ControlFactory::add_vgap($defs, -64);
				ControlFactoryExt::add_progressbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH - 1070, 780, $pos_percent);
				ControlFactory::add_vgap($defs, -74);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 250) . '/><text>' . $pos_percent . '%</text>');
				ControlFactory::add_vgap($defs, -69);
				ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 128) . '/><text>('.strftime('%H:%M', $tv_playback_ts).')</text>');
			}
			else
			{
				$reminder_enabled = ($start_tm_utc - time() >= 60);

				if ($reminder_enabled)
				{
					$reminders = $this->epg->get_reminders_list($plugin_cookies);
					$reminder_available = isset($reminders[$channel_id][$start_tm_utc]);

					if ($reminder_available)
					{
						ControlFactory::add_vgap($defs, -69);
						ControlFactory::add_smart_label($defs, null, '<gap width=1590/><icon>' . CUSTOM_EPG_RES_PATH . '/bell.png</icon>');
					}
				}
			}

			ControlFactory::add_vgap($defs, -16);
			ControlFactory::add_smart_label($defs, null, empty($this->program_data[CustomExtEpgProgram::title][0])? '' : '<gap width=30/><text>' . $this->program_data[CustomExtEpgProgram::title][0] . '</text>');
			ControlFactory::add_vgap($defs, -28);
			ControlFactory::add_smart_label($defs, null, empty($this->program_data[CustomExtEpgProgram::title][1])? '' : '<gap width=30/><text>' . $this->program_data[CustomExtEpgProgram::title][1] . '</text>');
			ControlFactory::add_smart_label($defs, null, '<gap width=30/><icon width=400 height=300>' . $this->program_data[CustomExtEpgProgram::icon_url] . '</icon>');
			ControlFactory::add_vgap($defs, -78);

			$max_visible_lines = empty($this->program_data[CustomExtEpgProgram::icon_url])? 13 : 7;
			$count_lines = count($this->program_data[CustomExtEpgProgram::details]);
			$invisible_lines = max($count_lines - $max_visible_lines, 0);

			if ($this->scroll_lines_position < 0)
				$this->scroll_lines_position = 0;

			if (empty($this->scroll_lines_position))
				$scrolling_pos = 0;

			if ($this->scroll_page_event <> 0)
			{
				if ($this->scroll_page_event > 0)
				{
					if (($this->scroll_lines_position + $max_visible_lines) < $count_lines)
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;

						if (($this->scroll_lines_position + $max_visible_lines) > $count_lines)
							$this->scroll_lines_position = $invisible_lines;

						$this->scroll_page_event = 0;
					}
				}
				else
				{
					if (empty($scrolling_pos))
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;

						if ($this->scroll_lines_position < 0)
							$this->scroll_lines_position = 0;

						$this->scroll_page_event = 0;
					}
				}
			}

			$line_idx = ($count_lines > $max_visible_lines)? ((($this->scroll_lines_position + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $this->scroll_lines_position) : 0;

			if ($count_lines > $max_visible_lines)
			{
				$scroll_available = true;
				$line_idx = (($this->scroll_lines_position + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $this->scroll_lines_position;
			}
			else
				$line_idx = 0;

			$line = $line_idx;
			$lines = $max_visible_lines;

			while ($lines > 0)
			{
				$string = (isset($this->program_data[CustomExtEpgProgram::details][$line]))? $this->program_data[CustomExtEpgProgram::details][$line++] : '';
				ControlFactory::add_smart_label($defs, null, (empty($this->program_data[CustomExtEpgProgram::icon_url])? '<gap width=30/>' : '<gap width=460/>') . '<text>' . $string . '</text>');
				ControlFactory::add_vgap($defs, -25);
				$lines--;
			}

			ControlFactoryExt::add_scrollbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH + 14, 44, $max_visible_lines, $count_lines, $line_idx);
			ControlFactory::add_vgap($defs, 10);

			if (!empty($this->program_data[CustomExtEpgProgram::icon_url]))
			{
				$notes_max_visible_lines = $max_visible_lines;
				$notes_count_lines = $count_lines;
				$notes_invisible_lines = $invisible_lines;
				$max_visible_lines = 6;
				$count_lines = count($this->program_data[CustomExtEpgProgram::desc]);
				$invisible_lines = max($count_lines - $max_visible_lines, 0);
				$invisible_lines += $notes_invisible_lines;

				if ($this->scroll_page_event <> 0)
				{
					if ($this->scroll_page_event > 0)
					{
						if (($this->scroll_lines_position + $max_visible_lines) < $count_lines + $notes_invisible_lines)
							$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;
					}
					else
					{
						$this->scroll_lines_position += $this->scroll_page_event*$max_visible_lines;

						if ($this->scroll_lines_position < $notes_invisible_lines)
							$this->scroll_lines_position = $notes_invisible_lines;
					}

					$this->scroll_page_event = 0;
				}

				if ($this->scroll_lines_position > $invisible_lines)
					$this->scroll_lines_position = $invisible_lines;

				$scrolling_pos = ($notes_count_lines > $notes_max_visible_lines)? ((($this->scroll_lines_position + $notes_max_visible_lines) > $notes_count_lines)?  $notes_max_visible_lines + $this->scroll_lines_position - $notes_count_lines : 0) : $this->scroll_lines_position;

				if ($this->scroll_lines_position > $scrolling_pos + $notes_invisible_lines)
					$this->scroll_lines_position = $scrolling_pos + $notes_invisible_lines;

				if ($count_lines > $max_visible_lines)
				{
					$scroll_available = true;
					$line_idx = (($scrolling_pos + $max_visible_lines) > $count_lines)? $count_lines - $max_visible_lines : $scrolling_pos;
				}
				else
					$line_idx = 0;

				$line = $line_idx;
				$lines = $max_visible_lines;

				while ($lines > 0)
				{
					$string = (isset($this->program_data[CustomExtEpgProgram::desc][$line]))? $this->program_data[CustomExtEpgProgram::desc][$line++] : '';
					ControlFactory::add_smart_label($defs, null, '<gap width=30/><text>' . $string . '</text>');
					ControlFactory::add_vgap($defs, -25);
					$lines--;
				}

				ControlFactoryExt::add_scrollbar($defs, self::CUSTOM_EPG_DIALOG_WIDTH + 14, 44, $max_visible_lines, $count_lines, $line_idx);
			}

			ControlFactory::add_vgap($defs, 22);
		}

		ControlFactory::add_vgap($defs, 7);
		ControlFactory::add_smart_label($defs, null,
			($this->display_please_wait? '' : '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/return_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>К списку</text>') .
			($not_validated? '' : '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_left_btn.png</icon><gap width=6/><icon>' . CUSTOM_EPG_RES_PATH . '/arrow_right_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Навигация</text>') .
			(($this->tv_playback_channel_id == $channel_id)? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . (($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)? '/select_btn.png' : '/info_btn.png') . '</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Тех. Инфо</text>' : '').
			(($not_validated)? '' : ($this->replay_available? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/enter_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_CORAL . ' size=small>Повтор</text>' : ($reminder_enabled? '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/enter_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . ' size=small>' . ($reminder_available? 'Не напоминать' : 'Напомнить') . '</text>' : ''))) .
			(((!$channel_data[ExtTvChannel::have_archive] || $not_validated) && ($this->tv_playback_channel_id == $channel_id))? '' : (($live && ($this->tv_playback_channel_id == $channel_id))? '' : '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/play_btn.png</icon><gap width=12/><text color=' . ($this->display_live_epg_item? DEF_LABEL_TEXT_COLOR_LIGHTGREEN : DEF_LABEL_TEXT_COLOR_SILVER) . ' size=small>Эфир</text>')));
		ControlFactory::add_vgap($defs, -10);

		return $defs;
	}

	protected function get_reminder_dialog_controls_defs(MediaURL $media_url)
	{
		static	$defs, $time_label_idx, $timer_label_idx, $group_id, $channel_id, $start_tm_utc;

		if (!isset($channel_id) || ($channel_id <> $media_url->channel_id) || (!empty($this->program_data[CustomExtEpgProgram::start_tm_sec]) && ($this->program_data[CustomExtEpgProgram::start_tm_sec] <> $start_tm_utc)))
		{
			$group_id = $media_url->group_id;
			$channel_id = $media_url->channel_id;
			$start_tm_utc = $this->program_data[CustomExtEpgProgram::start_tm_sec];
			$channel = $this->epg->get_channel($channel_id);
			$num_length = strlen($channel[ExtTvChannel::number]);

			$defs = array();
			ControlFactory::add_vgap($defs, -6);
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/dialog_main_bar.png</icon>');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 390) . '/><icon>' . CUSTOM_EPG_RES_PATH . '/dialog_time_bar.png</icon>');
			ControlFactory::add_vgap($defs, -61);
			ControlFactory::add_smart_label($defs, null, '<gap width=' . ((($num_length > 2)? 0 : (($num_length > 1)? 23 : 46)) + 10) . '/><text color=' . DEF_LABEL_TEXT_COLOR_SKYBLUE . '>' . $channel[ExtTvChannel::number] . '</text>');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '<gap width=102/><icon width=50 height=50>' . $channel[ExtTvChannel::icon_url] . '</icon><gap width=30/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . $channel[ExtTvChannel::caption] . '</text>');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '');
			ControlFactory::add_vgap($defs, -69);
			ControlFactory::add_smart_label($defs, null, '');

			$time_label_idx = count($defs) - 2;
			ControlFactory::add_vgap($defs, 10);

			$title = isset($this->program_data[CustomExtEpgProgram::title][0])? trim($this->program_data[CustomExtEpgProgram::title][0]) . (isset($this->program_data[CustomExtEpgProgram::title][1])? trim($this->program_data[CustomExtEpgProgram::title][1]) : '') : '';
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/bell_ringing.png</icon><gap width=16/><text color=' . DEF_LABEL_TEXT_COLOR_LIGHTGREEN . '>Сегодня' . strftime(' %H:%M', $start_tm_utc - $this->epg->time_zone_offset) . '-' . strftime('%H:%M', $this->program_data[CustomExtEpgProgram::end_tm_sec] - $this->epg->time_zone_offset) . '</text>');
			ControlFactory::add_vgap($defs, -10);
			ControlFactory::add_multiline_label($defs, null, $title, 5);
			ControlFactory::add_vgap($defs, 10);

			$desc = '';

			foreach($this->program_data[CustomExtEpgProgram::desc] as $line => $str)
			{
				if ($line > 5)
					break;

				$desc .= $str . "\n";
			}

			ControlFactory::add_multiline_label($defs, null, $desc, 6);
			ControlFactory::add_vgap($defs, 30);
			ControlFactory::add_smart_label($defs, null, '<icon width=' . self::CUSTOM_EPG_DIALOG_WIDTH . ' height=190>' . CUSTOM_EPG_RES_PATH . '/title_bar.png</icon>');
			ControlFactory::add_vgap($defs, -50);
			ControlFactory::add_smart_label($defs, null, '');

			$timer_label_idx = count($defs) - 1;
			ControlFactory::add_vgap($defs, -50);
			ControlFactory::add_custom_close_dialog_and_apply_button(
				$defs,
				ControlFactoryExt::get_left_margin_str(70),
				'Переключить на эфир',
				630,
				UserInputHandlerRegistry::create_action(
					$this,
					'change_media_url',
					array
					(
						'initial_media_url' =>
							MediaURL::encode(
								array
								(
									'channel_id' => $channel_id,
									'group_id' => $group_id,
									'archive_tm' => -1,
									'is_favorite' => ($group_id == $this->epg->tv_fav_channels_group_id
								)
							)
						),
					)
				)
			);

			ControlFactory::add_custom_close_dialog_and_apply_button(
				$defs,
				'',
				'Подробно о телепередаче',
				630,
				UserInputHandlerRegistry::create_action(
					$this,
					'show',
					array
					(
						'selected_media_url' =>
							MediaURL::encode(
								array
								(
									'screen_id' => CustomEpgScreenDefs::program_info,
									'group_id' => $group_id,
									'channel_id' => $channel_id,
									'start_tm_utc' => $start_tm_utc
								)
							),
					)
				)
			);

			ControlFactory::add_vgap($defs, -84);
			ControlFactory::add_smart_label($defs, null, '<gap width=24/><text size=small color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>По истечении указанного времени трансляция</text>');
			ControlFactory::add_vgap($defs, -34);
			ControlFactory::add_smart_label($defs, null, '<gap width=24/><text size=small color=' . DEF_LABEL_TEXT_COLOR_DIMGRAY . '>переключится автоматически.</text>');
			ControlFactory::add_vgap($defs, 10);
		}

		$ts = $start_tm_utc - time();
		$local_tm_ts = time() - $this->epg->time_zone_offset;

		if ($ts > 0)
			$label_str = 'Трансляция начнется через ' . $ts . ' сек.';
		else if (($ts == 0) || (($ts < 0) && ($ts > -5)))
			$label_str = 'Начало трансляции...';
		else if ($ts > -60)
			$label_str = 'Трансляция началась ' . abs($ts) . ' сек. назад';
		else
			$label_str = 'Трансляция началась ' . abs(round($ts / 60)) . ' мин. назад';

		$defs[$timer_label_idx][GuiControlDef::specific_def][GuiLabelDef::caption] = '<gap width=24/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . $label_str . '</text>';
		$defs[$time_label_idx - 1][GuiControlDef::specific_def][GuiLabelDef::caption] = '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 367) . '/>' . str_replace('<text', '<text dy=3', self::$days_ru[strftime('%w', $local_tm_ts)]) . '<gap width=30/><text dy=3>' . strftime('%d.%m.%y', $local_tm_ts) . '</text>';
		$defs[$time_label_idx + 1][GuiControlDef::specific_def][GuiLabelDef::caption] = '<gap width=' . (self::CUSTOM_EPG_DIALOG_WIDTH - 120) . '/><text dy=3>' . strftime('%H:%M', $local_tm_ts) . '</text>';

		return $defs;
	}

	protected function get_main_dialog_action_map(MediaURL $media_url)
	{
		$add_params = array('selected_media_url' => $media_url->get_raw_string());
		$close_dialog_action = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_STOP);
		$actions =
			array
			(
				GUI_EVENT_KEY_STOP => $close_dialog_action,
				GUI_EVENT_TIMER => UserInputHandlerRegistry::create_action($this, GUI_EVENT_TIMER, $add_params),
			);

		if ($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)
			$actions[GUI_EVENT_KEY_SELECT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_INFO, $add_params);
		else
			$actions[GUI_EVENT_KEY_INFO] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_INFO, $add_params);

		if (!$this->display_playable_stream_info)
		{
			$actions[GUI_EVENT_KEY_LEFT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_LEFT, $add_params);
			$actions[GUI_EVENT_KEY_RIGHT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_RIGHT, $add_params);
			$actions[GUI_EVENT_KEY_UP] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_UP, $add_params);
			$actions[GUI_EVENT_KEY_DOWN] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_DOWN, $add_params);
			$actions[GUI_EVENT_KEY_PLAY] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_PLAY, $add_params);
			$actions[GUI_EVENT_KEY_ENTER] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_ENTER, $add_params);
			$actions[GUI_EVENT_KEY_P_PLUS] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_P_PLUS, $add_params);
			$actions[GUI_EVENT_KEY_P_MINUS] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_P_MINUS, $add_params);
		}

		if (!empty($this->show_custom_epg_key))
			$actions[$this->show_custom_epg_key] = $close_dialog_action;

		if ($this->stb_firmware->branch_num >= 10)
			$actions[GUI_EVENT_KEY_RETURN] = $close_dialog_action;

		return $actions;
	}

	protected function get_main_screen_action_map(MediaURL $media_url)
	{
		$add_params = array('selected_media_url' => $media_url->get_raw_string());
		$close_action = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_STOP);
		$actions =
			array
			(
				GUI_EVENT_KEY_STOP => $close_action,
				GUI_EVENT_KEY_TOP_MENU => $close_action,
				GUI_EVENT_KEY_RETURN => $close_action,
				GUI_EVENT_TIMER => UserInputHandlerRegistry::create_action($this, GUI_EVENT_TIMER, $add_params),
			);

		if ($media_url->screen_id == CustomEpgScreenDefs::program_info)
		{
			if (!$this->display_please_wait)
			{
				$actions[GUI_EVENT_KEY_RETURN] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_RETURN);

				if ($this->tv_playback_channel_id == $media_url->channel_id)
				{
					if ($this->show_custom_epg_key <> GUI_EVENT_KEY_SELECT)
						$actions[GUI_EVENT_KEY_SELECT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_INFO);
					else
						$actions[GUI_EVENT_KEY_INFO] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_INFO);
				}
			}

			if (!$this->display_playable_stream_info)
			{
				$actions[GUI_EVENT_KEY_PLAY] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_PLAY);
				$actions[GUI_EVENT_KEY_LEFT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_LEFT);
				$actions[GUI_EVENT_KEY_RIGHT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_RIGHT);
				$actions[GUI_EVENT_KEY_UP] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_UP);
				$actions[GUI_EVENT_KEY_DOWN] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_DOWN);
				$actions[GUI_EVENT_KEY_P_PLUS] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_P_PLUS);
				$actions[GUI_EVENT_KEY_P_MINUS] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_P_MINUS);
				$actions[GUI_EVENT_KEY_ENTER] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_ENTER);
			}
		}
		else
		{
			$dummy = UserInputHandlerRegistry::create_action($this, 'dummy', $add_params);

			if ($this->display_please_wait)
			{
				$actions[GUI_EVENT_KEY_LEFT] = $dummy;
				$actions[GUI_EVENT_KEY_RIGHT] = $dummy;
				$actions[GUI_EVENT_KEY_ENTER] = $dummy;
				$actions[GUI_EVENT_KEY_RETURN] = $dummy;
				$actions[GUI_EVENT_KEY_UP] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_UP, array('screen_key_up' => 1));
				$actions[GUI_EVENT_KEY_DOWN] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_DOWN, array('screen_key_down' => 1));
			}
			else
			{
				$actions[GUI_EVENT_KEY_ENTER] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_ENTER, array('choose_folder_item' => 1));
				$actions[GUI_EVENT_KEY_RETURN] = ($media_url->screen_id == CustomEpgScreenDefs::groups_list)? $close_action : UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_RETURN);

				if ($media_url->screen_id == CustomEpgScreenDefs::programs_list)
				{
					$actions[GUI_EVENT_KEY_LEFT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_LEFT, array('display_prev_day' => 1));
					$actions[GUI_EVENT_KEY_RIGHT] = UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_RIGHT, array('display_next_day' => 1));
				}
				else
				{
					$actions[GUI_EVENT_TIMER] = $dummy;
					$actions[GUI_EVENT_KEY_LEFT] = $dummy;
					$actions[GUI_EVENT_KEY_RIGHT] = $dummy;
				}
			}
		}

		if (!empty($this->show_custom_epg_key))
			$actions[$this->show_custom_epg_key] = $close_action;

		return $actions;
	}

	protected function get_reminder_dialog_action_map(MediaURL $media_url)
	{
		$before_tm_sec = $media_url->start_tm_utc - time();

		if (($before_tm_sec < 0) && ($before_tm_sec >= -30))
		{
			return
				array
				(
					GUI_EVENT_TIMER =>
						ActionFactorySafe::close_dialog_and_run(
							UserInputHandlerRegistry::create_action(
								$this,
								'change_media_url',
								array
								(
									'initial_media_url' =>
										MediaURL::encode(
											array
											(
												'channel_id' => $media_url->channel_id,
												'group_id' => $media_url->group_id,
												'archive_tm' => -1,
												'is_favorite' => ($media_url->group_id == $this->epg->tv_fav_channels_group_id)
											)),
								))),
					GUI_EVENT_KEY_RETURN =>
						UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_STOP),
				);
		}

		return
			array
			(
				GUI_EVENT_TIMER =>
					UserInputHandlerRegistry::create_action(
						$this,
						GUI_EVENT_TIMER,
						array
						(
							'selected_media_url' => $media_url->get_raw_string(),
							'update_reminder_dialog' => 1,
						)),
				GUI_EVENT_KEY_RETURN =>
					UserInputHandlerRegistry::create_action($this, GUI_EVENT_KEY_STOP),
			);
	}

	public function find_program_by_time(&$result_arr, $channel_id, $find_ts, $previous = false)
	{
		$epg = array();
		$day_ts = $this->epg->get_day_start_ts($find_ts);

		while(1)
		{
			if (!$epg[$day_ts] = $this->epg->get_day_slice($channel_id, $day_ts))
			{
				$result_arr = array('day_ts' => $day_ts);
				return false;
			}

			$first_item_ts = null;
			$begin_item = null;
			$end_item = null;
			$found = false;

			if (ksort($epg))
				foreach($epg as $day_ts => $day_epg)
					foreach($day_epg as $item)
					{
						if (empty($first_item_ts))
							$first_item_ts = $item[PluginTvEpgProgram::start_tm_sec];

						if ($find_ts >= $item[PluginTvEpgProgram::start_tm_sec])
							$begin_item = $item;

						if (empty($end_item) && ($find_ts < $item[PluginTvEpgProgram::end_tm_sec]))
							$end_item = $item;

						if (($find_ts >= $item[PluginTvEpgProgram::start_tm_sec]) && ($find_ts < $item[PluginTvEpgProgram::end_tm_sec]))
						{
							$found = true;
							break 2;
						}
					}

			if (!$found)
			{
				if (empty($first_item_ts))
				{
					if ($find_ts < $item[PluginTvEpgProgram::start_tm_sec])
						$day_ts -= 86400;
					else
						if ($find_ts >= $item[PluginTvEpgProgram::end_tm_sec])
							$day_ts += 86400;

					continue;
				}
				else
				{
					if (!empty($begin_item) && !empty($end_item))
					{
						$result_arr = $previous? $begin_item : $end_item;
						return true;
					}

					if (empty($begin_item))
					{
						$day_ts -= 86400;
						continue;
					}

					if (empty($end_item))
					{
						$day_ts += 86400;
						continue;
					}
				}

				$result_arr = array('day_ts' => null);
				return false;
			}

			$result_arr = $item;
			return true;
		}
	}

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

	public function __construct(DefaultDunePlugin &$plugin, EpgEngine &$epg)
	{
		$this->epg = $epg;

		$this->screen_id = CustomEpgScreenDefs::program_info;
		DefaultDunePlugin::create_screen($this);

		$this->screen_id = CustomEpgScreenDefs::programs_list;
		DefaultDunePlugin::create_screen($this);

		$this->screen_id = CustomEpgScreenDefs::channels_list;
		DefaultDunePlugin::create_screen($this);

		$this->screen_id = CustomEpgScreenDefs::groups_list;
		DefaultDunePlugin::create_screen($this);

		$this->stb_firmware = json_decode(json_encode(get_parsed_firmware_ver()));
		define('CUSTOM_EPG_RES_PATH',			dirname(__FILE__) . '/res');
		define('CUSTOM_EPG_SCREEN_BG_PATH', 	get_paved_path(DuneSystem::$properties['data_dir_path'] . '/customs/epg'));
		define('CUSTOM_EPG_SCREEN_AVAILABLE',	file_exists(CUSTOM_EPG_SCREEN_BG_PATH . '/channels_list.aai') &&
												file_exists(CUSTOM_EPG_SCREEN_BG_PATH . '/groups_list.aai') &&
												file_exists(CUSTOM_EPG_SCREEN_BG_PATH . '/program_info.aai') &&
												file_exists(CUSTOM_EPG_SCREEN_BG_PATH . '/programs_list.aai') &&
												($this->stb_firmware->branch_num > 10));

		$this->kp_rating_icon = CUSTOM_EPG_RES_PATH . '/kp.png';
		$this->imdb_rating_icon = file_exists(self::IMDB_RATING_ICON)? self::IMDB_RATING_ICON : CUSTOM_EPG_RES_PATH . '/imdb.png';
		UserInputHandlerRegistry::get_instance()->register_handler($this);
	}

	public function init($show_event_key, $tv_play_action = null, $tv_fav_channels_font_color = DEF_LABEL_TEXT_COLOR_SKYBLUE, $tv_all_channels_font_color = DEF_LABEL_TEXT_COLOR_YELLOW)
	{
		$this->screens_counter = 0;
		$this->show_custom_epg_key = $show_event_key;
		$this->tv_play_action = $tv_play_action;
		$this->tv_playback_group_id = null;
		$this->tv_fav_channels_font_color = $tv_fav_channels_font_color;
		$this->tv_all_channels_font_color = $tv_all_channels_font_color;
		hd_print(get_class($this) . ' initialized successfully!');
	}

	public function show($channel_id, $group_id)
	{
		if (is_null($this->tv_playback_group_id))
			$this->tv_playback_group_id = $group_id;

		$media_url = MediaURL::decode();
		$media_url->screen_id = CustomEpgScreenDefs::program_info;
		$media_url->group_id = $group_id;
		$media_url->channel_id = $channel_id;
		$this->screens_counter = 0;

		return
			UserInputHandlerRegistry::create_action($this, 'show', array('selected_media_url' => $media_url->get_raw_string()));
	}

	public function get_id()
	{
		return $this->screen_id;
	}

	public function get_handler_id()
	{
		return 'custom_epg_handler';
	}

	public function retrive_tv_playback_url($channel_id, $archive_ts, $media_url)
	{
		$this->tv_playback_group_id = null;
		$this->tv_playback_channel_id = $channel_id;
		$this->tv_playback_live_start_ts = time();
		$this->tv_playback_archive_start_ts = ($archive_ts > 0)? $archive_ts : 0;

		if (preg_match_all('/^(drm:\/\/.*\/.*\/|)(.*127\.0\.0\.1\/.*\?|.*localhost\/.*\?|)(.*)(\|\|\|.*|)$/iU', $media_url, $matches) && isset($matches[3][0]))
			$tv_playback_url = $matches[3][0];

		$this->tv_playback_url = isset($tv_playback_url)? $tv_playback_url : $media_url;

		return $this->tv_playback_url;
	}

	public function get_reminder_dialog_action($playback_channel_id, $playback_group_id, &$plugin_cookies)
	{
		static	$reminders;
		static	$alert_queue = array();

		$reminders_has_changed = false;
		$playback_state = get_playback_state();

		if (($playback_state == PLAYBACK_PLAYING) || ($playback_state == PLAYBACK_PAUSED))
		{
			$ts = time();
			$reminders = $this->epg->get_reminders_list($plugin_cookies);

			foreach ($reminders as $channel_id => $broadcasts)
				foreach ($broadcasts as $start_tm_utc => $day_ts)
					if ($start_tm_utc - $ts <= 60)
					{
						if ($start_tm_utc - $ts >= -300)
						{
							array_push(
								$alert_queue,
								array
								(
									'group_id' => $playback_group_id,
									'channel_id' => $channel_id,
									'start_tm_utc' => $start_tm_utc,
								)
							);
						}

						unset($reminders[$channel_id][$start_tm_utc]);

						if (empty($reminders[$channel_id]))
							unset($reminders[$channel_id]);

						$reminders_has_changed = true;
						break 2;
					}

			if ($reminders_has_changed)
				$this->epg->set_reminders_list($reminders, $plugin_cookies);
		}
		else
			return null;

		if (!empty($alert_queue))
		{
			$alert = array_shift($alert_queue);

			if (($alert['channel_id'] <> $playback_channel_id) || !empty($this->tv_playback_archive_start_ts))
			{
				$media_url = MediaURL::decode($alert);
				$channel = $this->epg->get_channel($media_url->channel_id);

				if (!$this->program_data[CustomExtEpgProgram::validated] = $this->find_program_by_time($find_result, $media_url->channel_id, $media_url->start_tm_utc))
				{
					while(!empty($find_result['day_ts']))
					{
						$day_ts = null;

						if ($this->program_data[CustomExtEpgProgram::validated] = $this->find_program_by_time($find_result, $media_url->channel_id, $media_url->start_tm_utc))
						{
							$this->program_data = $this->get_shown_program_data($media_url->channel_id, $find_result);
							break;
						}

						$this->epg->get_day_epg($media_url->channel_id, $day_ts, $plugin_cookies);
					}
				}
				else
					$this->program_data = $this->get_shown_program_data($media_url->channel_id, $find_result);

				$media_url->stop_tm_utc = isset($this->program_data[CustomExtEpgProgram::end_tm_sec])? $this->program_data[CustomExtEpgProgram::end_tm_sec] : 0;

				if (array_search($media_url->group_id, $channel[ExtTvChannel::group_ids]) === false)
				{
					$group_idx = array_search($media_url->group_id, $this->epg->get_tv_favorites());
					$media_url->group_id = (($group_idx === false) || empty($this->epg->tv_fav_channels_group_id))? $this->epg->tv_all_channels_group_id : $this->epg->tv_fav_channels_group_id;
				}

				$dialog_action = ActionFactorySafe::show_dialog(
					null,
					$this->get_reminder_dialog_controls_defs($media_url),
					true,
					self::CUSTOM_EPG_DIALOG_WIDTH,
					array
					(
						'dialog_params' =>
							array
							(
								'frame_style' => DIALOG_FRAME_STYLE_GLASS,
							),
						ShowDialogActionData::timer => ActionFactorySafe::timer(1000),
						ShowDialogActionData::actions => $this->get_reminder_dialog_action_map($media_url),
					)
				);
			}
		}

		return
			$reminders_has_changed? $this->get_update_epg_action($channel_id, $day_ts, $plugin_cookies, isset($dialog_action)? $dialog_action : null) : null;
	}

	public function get_folder_range(MediaURL $media_url, $from_ndx, &$plugin_cookies, $from_cache = false)
	{
		return
			AbstractRegularScreen::create_regular_folder_range($this->get_all_folder_items($media_url, $plugin_cookies, $from_cache));
	}

	public function get_folder_view(MediaURL $media_url, &$plugin_cookies)
	{
		if ($media_url->screen_id == CustomEpgScreenDefs::program_info)
		{
			$player_state = get_player_state_assoc();
			$folder_view =
				array
				(
					PluginControlsFolderView::defs => $this->get_main_screen_controls_defs($media_url, $player_state, $plugin_cookies),
					PluginControlsFolderView::actions => $this->get_main_screen_action_map($media_url),
					PluginControlsFolderView::timer => ActionFactorySafe::timer(1000),
					PluginControlsFolderView::params =>
						array
						(
							PluginFolderViewParams::paint_path_box => true,
							PluginFolderViewParams::paint_path_box_background => false,
							PluginFolderViewParams::paint_content_box_background => false,
						),
				);

			return
				array
				(
					PluginFolderView::multiple_views_supported  => false,
					PluginFolderView::archive                   => null,
					PluginFolderView::view_kind                 => PLUGIN_FOLDER_VIEW_CONTROLS,
					PluginFolderView::data                      => $folder_view,
				);
		}
		else
		{
			if ($media_url->screen_id == CustomEpgScreenDefs::programs_list)
			{
				$thumbnails_support = !is_null($epg_info = $this->epg->get_parser_info($media_url->channel_id))? $epg_info->thumbnails_support : false;
				$day = preg_replace('/\<.*\>/U', '', self::$days_ru[strftime('%w', $media_url->day_start_tm_utc)]);
				$folder_view = $this->get_programs_list_epg_screen_view_params($day, strftime('Программа передач на %d.%m.%Y', $media_url->day_start_tm_utc), $thumbnails_support && !$this->thumbnails_not_available);
			}
			else if ($media_url->screen_id == CustomEpgScreenDefs::channels_list)
			{
				$group_id = isset($this->tv_playback_group_id)? $this->tv_playback_group_id : $media_url->group_id;

				if ($group_id == $this->epg->tv_fav_channels_group_id)
					$group_caption = $this->epg->tv_fav_channels_group_name;
				else
				{
					$group = $this->epg->get_group($group_id);
					$group_caption = $group[PluginTvGroup::caption];
				}

				$folder_view = $this->get_channels_list_epg_screen_view_params($group_caption);
				$folder_view[PluginRegularFolderView::initial_range] = $this->get_folder_range($media_url, 0, $plugin_cookies, true);
			}
			else if ($media_url->screen_id == CustomEpgScreenDefs::groups_list)
			{
				$folder_view = $this->get_groups_list_epg_screen_view_params('Группы каналов');
				$folder_view[PluginRegularFolderView::initial_range] = $this->get_folder_range($media_url, 0, $plugin_cookies, true);
			}
		}

		$folder_view[PluginRegularFolderView::actions] = $this->get_main_screen_action_map($media_url);

		if (empty($folder_view[PluginRegularFolderView::initial_range][PluginRegularFolderRange::items]))
		{
			ControlFactory::add_vgap($defs, -163);
			ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_SCREEN_BG_PATH . '/program_info.aai</icon>');
			ControlFactory::add_vgap($defs, -69);

			if ($media_url->screen_id == CustomEpgScreenDefs::programs_list)
			{
				if (!$this->display_please_wait)
				{
					$folder_view[PluginRegularFolderView::initial_range] = $this->get_folder_range($media_url, 0, $plugin_cookies, true);

					if (!empty($folder_view[PluginRegularFolderView::initial_range][PluginRegularFolderRange::items]))
						return
							array
							(
								PluginFolderView::multiple_views_supported  => false,
								PluginFolderView::archive                   => null,
								PluginFolderView::view_kind                 => PLUGIN_FOLDER_VIEW_REGULAR,
								PluginFolderView::data                      => $folder_view,
							);
				}

				$defs = $this->get_main_screen_controls_defs($media_url, $player_state, $plugin_cookies);
				$day_ts = $media_url->day_start_tm_utc;

				if (!$this->epg->get_day_slice($media_url->channel_id, $day_ts))
				{
					$dummy = UserInputHandlerRegistry::create_action($this, 'dummy', array('selected_media_url' => $media_url->get_raw_string()));
					$folder_view[PluginRegularFolderView::actions][GUI_EVENT_KEY_ENTER] = $dummy;
				}
			}
			else
			{
				ControlFactory::add_smart_label($defs, null, '<icon>' . CUSTOM_EPG_RES_PATH . '/folder.aai</icon>');
				ControlFactory::add_vgap($defs, -62);
				ControlFactory::add_smart_label($defs, null, '<gap width=113/><text color=' . DEF_LABEL_TEXT_COLOR_YELLOW . '>' . $group_caption . '</text>');
				ControlFactory::add_vgap($defs, 340);
				ControlFactory::add_smart_label($defs, null, '<gap width=600/><text>В этой группе нет каналов</text>');
				ControlFactory::add_vgap($defs, 402);
				ControlFactory::add_smart_label($defs, null, '<gap width=30/><icon>' . CUSTOM_EPG_RES_PATH . '/return_btn.png</icon><gap width=12/><text color=' . DEF_LABEL_TEXT_COLOR_SILVER . ' size=small>Группы</text>');
			}

			$folder_view[PluginControlsFolderView::defs] = $defs;
			$folder_view[PluginControlsFolderView::params] =
				array
				(
					PluginFolderViewParams::paint_path_box => true,
					PluginFolderViewParams::paint_path_box_background => false,
					PluginFolderViewParams::paint_content_box_background => false,
				);

			if ($this->display_please_wait && !empty($day_ts))
				$folder_view[PluginControlsFolderView::timer] = ActionFactorySafe::timer(100);

			return
				array
				(
					PluginFolderView::multiple_views_supported  => false,
					PluginFolderView::archive                   => null,
					PluginFolderView::view_kind                 => PLUGIN_FOLDER_VIEW_CONTROLS,
					PluginFolderView::data                      => $folder_view,
				);
		}

		$folder_view[PluginRegularFolderView::initial_range] = $this->get_folder_range($media_url, 0, $plugin_cookies, true);

		return
			array
			(
				PluginFolderView::multiple_views_supported  => false,
				PluginFolderView::archive                   => null,
				PluginFolderView::view_kind                 => PLUGIN_FOLDER_VIEW_REGULAR,
				PluginFolderView::data                      => $folder_view,
			);
	}

	public function get_all_folder_items(MediaURL $media_url, &$plugin_cookies, $from_cache)
	{
		static $cache;

		$initial_screen_id = $media_url->screen_id;
		$initial_group_id = $media_url->group_id;
		$initial_channel_id = $media_url->channel_id;
		$initial_day_start_tm = isset($media_url->day_start_tm_utc)? $media_url->day_start_tm_utc : null;
		$initial_start_tm = isset($media_url->start_tm_utc)? $media_url->start_tm_utc : null;

		$id = md5("$initial_screen_id$initial_channel_id$initial_day_start_tm$initial_start_tm");

		if ($from_cache && isset($cache[$id]))
			return $cache[$id];

		$items = array();
		$this->program_item_idx = -1;
		$this->channel_item_idx = -1;
		$this->group_item_idx = -1;

		if (isset($this->tv_playback_group_id, $initial_channel_id) && !is_null($initial_channel = $this->epg->get_channel($initial_channel_id)) && in_array($this->tv_playback_group_id, $initial_channel[ExtTvChannel::group_ids]))
			$initial_group_id = $this->tv_playback_group_id;
		else
			$this->tv_playback_group_id = $initial_group_id;

		if ($initial_screen_id == CustomEpgScreenDefs::programs_list)
		{
			$this->thumbnails_not_available = true;
			$channel = $this->epg->get_channel($initial_channel_id);
			$tv_playback_ts = (empty($this->tv_playback_archive_start_ts) || $this->display_live_epg_item)? $this->playback_live_ts : $this->playback_archive_ts;
			$program_start_ts = empty($initial_start_tm)? (isset($this->program_data[CustomExtEpgProgram::start_tm_sec])? $this->program_data[CustomExtEpgProgram::start_tm_sec] : -1) : $initial_start_tm;
			$epg_thumbnails_support = !is_null($epg_info = $this->epg->get_parser_info($initial_channel_id))? $epg_info->thumbnails_support : false;

			if ($slice = $this->epg->get_day_slice($initial_channel_id, $initial_day_start_tm))
			{
				$left_margin = ControlFactoryExt::get_left_margin_str(5);
				$reminders = $this->epg->get_reminders_list($plugin_cookies);

				foreach($slice as $epg_item)
				{
					$tv_program_ts = $epg_item[PluginTvEpgProgram::start_tm_sec] - $this->epg->time_zone_offset;
					$playback_remain_tm_sec = time() - $this->epg->time_zone_offset - $tv_program_ts;
					$tv_program_start_ts = $epg_item[PluginTvEpgProgram::start_tm_sec] - $this->epg->time_zone_offset;
					$tv_program_stop_ts = $epg_item[PluginTvEpgProgram::end_tm_sec] - $this->epg->time_zone_offset;
					$playback_is_not_complete = ($initial_channel_id == $this->tv_playback_channel_id) && ($tv_program_start_ts <= $tv_playback_ts) && ($tv_program_stop_ts >= $tv_playback_ts);
					$replay_enabled = $channel[ExtTvChannel::have_archive] && ($playback_remain_tm_sec > $channel[ExtTvChannel::archive_delay_sec]) && ($playback_remain_tm_sec <= $channel[ExtTvChannel::archive_past_sec]);

					if (!is_null($epg = $this->epg->get_epg_data($initial_channel_id, $epg_item['day_ts'], $epg_item[PluginTvEpgProgram::start_tm_sec])))
					{
						$icon = $playback_is_not_complete? ($replay_enabled? '/played_and_replay_mark.png' : '/played_mark.png') : ($replay_enabled? '/replay_mark.png' : (($epg_item[PluginTvEpgProgram::start_tm_sec] - time() >= 60) && isset($reminders[$initial_channel_id][$epg_item[PluginTvEpgProgram::start_tm_sec]])? '/bell_mark.png' : ''));
						$title = $epg[ExtEpgProgram::title];
					}
					else
						continue;

					$details_icon_url = null;
					$details_desc = '';

					if (defined('ViewParams::details_box_width'))
					{
						$title = $epg[ExtEpgProgram::title];
						$details_icon_url = $epg_thumbnails_support? $epg[ExtEpgProgram::main_icon] : null;
						$this->thumbnails_not_available &= empty($details_icon_url);

						if (!empty($epg[ExtEpgProgram::sub_title]))
							$details_desc .= $epg[ExtEpgProgram::sub_title] . '|';

						$details = trim((!empty($epg[ExtEpgProgram::main_category])? (preg_match('/^(х\/ф|т\/с)\s+/iu', $title)? preg_replace('/\](.*)$/uU', ']', $epg[ExtEpgProgram::main_category]) . ' ' : $epg[ExtEpgProgram::main_category] . ', ') : '') .
										(!empty($epg[ExtEpgProgram::country])? $epg[ExtEpgProgram::country] . ', ' : '') .
										(!empty($epg[ExtEpgProgram::year])? $epg[ExtEpgProgram::year] . ', ' : '') .
										(is_null($t = empty($epg[ExtEpgProgram::director])? null : array_slice($epg[ExtEpgProgram::director], 0, self::MAX_NUM_DIRECTORS_IN_SHORT_DETAILS))? '' : 'реж. ' . implode(', ', $t) . ((count($epg[ExtEpgProgram::director]) > count($t))? ' и другие; ' : '; ')) .
										(is_null($t = empty($epg[ExtEpgProgram::actor])? null : array_slice($epg[ExtEpgProgram::actor], 0, self::MAX_NUM_ACTORS_IN_SHORT_DETAILS))? '' : 'актеры: ' . implode(', ', $t) . ((count($epg[ExtEpgProgram::actor]) > count($t))? ' и другие; ' : '; ')) .
										(is_null($t = empty($epg[ExtEpgProgram::presenter])? null : array_slice($epg[ExtEpgProgram::presenter], 0, self::MAX_NUM_PRESENTERS_IN_SHORT_DETAILS))? '' : 'вед. ' . implode(', ', $t) . ((count($epg[ExtEpgProgram::presenter]) <> count($t))? ' и другие; ' : '; ')), "\t\n\r\0\x20;,.");

						if (!empty($details))
							$details_desc .= "$details.|";

						if (!empty($epg[ExtEpgProgram::desc]))
							$details_desc .= preg_replace(array('/(\t|\n|\r|\0)+/', '/(!|\?)\.+\s*$/Uu'), array('|', '$1'), $epg[ExtEpgProgram::desc]);

						$details_desc = iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', $details_desc), 33, "|", true));
					}

					$media_url_str =
						MediaURL::encode(
							array
							(
								'screen_id' => $initial_screen_id,
								'group_id' => $initial_group_id,
								'channel_id' => $initial_channel_id,
								'day_start_tm_utc' => $initial_day_start_tm,
								'start_tm_utc' => $epg_item[PluginTvEpgProgram::start_tm_sec],
							));

					$items[] =
						array
						(
							PluginRegularFolderItem::media_url => $media_url_str,
							PluginRegularFolderItem::caption => strftime('%H:%M', $tv_program_ts) . $left_margin . $title,
							PluginRegularFolderItem::view_item_params =>
								array
								(
									FolderViewItemParams::item_paint_icon => !empty($icon),
									FolderViewItemParams::icon_path => empty($icon)? null : CUSTOM_EPG_RES_PATH . $icon,
									FolderViewItemParams::item_detailed_icon_path => $details_icon_url,
									FolderViewItemParams::item_detailed_icon_width => empty($details_icon_url)? 1 : 400,
									FolderViewItemParams::item_detailed_icon_height => empty($details_icon_url)? 1 : 290,
									FolderViewItemParams::item_detailed_info => (!empty($details_icon_url) && $epg_thumbnails_support)? "|$details_desc" : "|\n|" . iconv('Windows-1251', 'UTF-8', wordwrap(iconv('UTF-8', 'Windows-1251', $title), 33, "|", true)) . "| |$details_desc",
								),
						);

					if ($epg_item[PluginTvEpgProgram::start_tm_sec] <= $program_start_ts)
						$this->program_item_idx = defined('PluginOpenFolderActionData::sel_id')? $media_url_str : count($items) - 1;
				}
			}
		}
		else if ($initial_screen_id == CustomEpgScreenDefs::channels_list)
		{
			$left_margin = ControlFactoryExt::get_left_margin_str(9) . '  ';

			if ($initial_group_id == $this->epg->tv_fav_channels_group_id)
			{
				foreach ($this->epg->get_tv_favorites() as $channel_id)
				{
					$channel = $this->epg->get_channel($channel_id);
					$num_length = strlen($channel[ExtTvChannel::number]);
					$media_url_str =
						MediaURL::encode(
							array
							(
								'screen_id' => $initial_screen_id,
								'group_id' => $initial_group_id,
								'channel_id' => $channel[ExtTvChannel::id],
							));

					$items[] =
						array
						(
							PluginRegularFolderItem::media_url => $media_url_str,
							PluginRegularFolderItem::caption => $channel[ExtTvChannel::number] . $left_margin . $channel[ExtTvChannel::caption],
							PluginRegularFolderItem::view_item_params =>
								array
								(
									FolderViewItemParams::item_caption_dx => (($num_length > 2)? 0 : (($num_length > 1)? 23 : 46)) - 2,
									FolderViewItemParams::icon_path => $channel[ExtTvChannel::icon_url],
								),
						);

					if ($channel_id == $initial_channel_id)
						$this->channel_item_idx = defined('PluginOpenFolderActionData::sel_id')? $media_url_str : count($items) - 1;

					if ($channel_id == $this->tv_playback_channel_id)
						$items[count($items)-1][PluginRegularFolderItem::view_item_params][FolderViewItemParams::item_badge_icon_path] = CUSTOM_EPG_RES_PATH . '/channel_played_badge.png';
				}
			}
			else
			{
				foreach ($this->epg->get_channels() as $channel)
				{
					if (array_search($initial_group_id, $channel[ExtTvChannel::group_ids]) === false)
						continue;

					$channel_id = $channel[ExtTvChannel::id];
					$num_length = strlen($channel[ExtTvChannel::number]);
					$media_url_str =
						MediaURL::encode(
							array
							(
								'screen_id' => $initial_screen_id,
								'group_id' => isset($this->tv_playback_group_id)? $this->tv_playback_group_id : $initial_group_id,
								'channel_id' => $channel_id
							));

					$items[] =
						array
						(
							PluginRegularFolderItem::media_url => $media_url_str,
							PluginRegularFolderItem::caption => $channel[ExtTvChannel::number] . $left_margin . $channel[ExtTvChannel::caption],
							PluginRegularFolderItem::view_item_params =>
								array
								(
									FolderViewItemParams::item_caption_dx => (($num_length > 2)? 0 : (($num_length > 1)? 23 : 46)) - 2,
									FolderViewItemParams::icon_path => $channel[ExtTvChannel::icon_url],
								),
						);

					if ($channel_id == $initial_channel_id)
						$this->channel_item_idx = defined('PluginOpenFolderActionData::sel_id')? $media_url_str : count($items) - 1;

					if ($channel_id == $this->tv_playback_channel_id)
						$items[count($items)-1][PluginRegularFolderItem::view_item_params][FolderViewItemParams::item_badge_icon_path] = CUSTOM_EPG_RES_PATH . '/channel_played_badge.png';
				}
			}
		}
		else if ($initial_screen_id == CustomEpgScreenDefs::groups_list)
		{
			foreach ($this->epg->get_groups() as $group)
			{
				$group_id = $group[PluginTvGroup::id];
				$item_idx = count($items);
				$media_url_str =
					MediaURL::encode(
						array
						(
							'screen_id' => $initial_screen_id,
							'group_id' => $group_id,
						));

				$items[] =
					array
					(
						PluginRegularFolderItem::media_url => $media_url_str,
						PluginRegularFolderItem::caption => $group[PluginTvGroup::caption],
						PluginRegularFolderItem::view_item_params =>
							array
							(
								FolderViewItemParams::icon_path => $group[PluginTvGroup::icon_url],
							),
					);



				if ($group_id == $initial_group_id)
					$this->group_item_idx = defined('PluginOpenFolderActionData::sel_id')? $media_url_str : $item_idx;

				if ($group_id == $this->epg->tv_fav_channels_group_id)
					$items[$item_idx][PluginRegularFolderItem::view_item_params][FolderViewItemParams::item_caption_color] = $this->tv_fav_channels_font_color;

				if ($group_id == $this->epg->tv_all_channels_group_id)
					$items[$item_idx][PluginRegularFolderItem::view_item_params][FolderViewItemParams::item_caption_color] = $this->tv_all_channels_font_color;
			}
		}

		$cache = array($id => $items);

		return $items;
	}

	public function handle_user_input(&$user_input, &$plugin_cookies)
	{
		static	$media_url;
		static	$list_empty = false;
		static	$timer_actions = array();

		if (isset($user_input->control_id))
		{
			$control_id = $user_input->control_id;

			if ($control_id == 'dummy')
				return null;

			if (isset($user_input->selected_media_url))
				$media_url = MediaURL::decode($user_input->selected_media_url);
			else
				if (($control_id <> 'change_media_url') && ($control_id <> GUI_EVENT_KEY_STOP) && ($control_id <> GUI_EVENT_KEY_RETURN) && empty($media_url))
					throw new DuneException('Var $media_url is empty!');

			while(1)
			{
				$player_state = get_player_state_assoc();
				$local_tm_ts = time() - $this->epg->time_zone_offset;

				if (isset($player_state['playback_position']))
				{
					$this->playback_archive_ts = (empty($player_state['playback_position']) || ($this->stb_firmware->branch_num <= 9))? (($player_state['playback_state'] == PLAYBACK_PLAYING)? ($local_tm_ts + $this->tv_playback_archive_start_ts - $this->tv_playback_live_start_ts) : $this->playback_archive_ts) : $this->tv_playback_archive_start_ts + $player_state['playback_position'] - $this->epg->time_zone_offset;
					$this->playback_live_ts = (($player_state['playback_state'] == PLAYBACK_PLAYING)? $local_tm_ts : $this->playback_live_ts);
				}
				else
				{
					$this->playback_archive_ts = 0;
					$this->playback_live_ts = 0;
				}

				switch ($control_id)
				{
					case 'change_media_url':
						if (isset($user_input->initial_media_url))
						{
							$this->epg->tv_info_set_faulty(0);

							if (class_exists('PlaybackPoints'))
								PlaybackPoints::update();

							if (is_array($this->tv_play_action) && isset($this->tv_play_action[GuiAction::handler_string_id]))
							{
								$this->tv_play_action[GuiAction::params]['initial_media_url'] = $user_input->initial_media_url;

								return
									$this->hide($this->tv_play_action);//ActionFactorySafe::stop_playback(false, $this->tv_play_action));
							}

							return
								$this->hide(ActionFactorySafe::tv_play(MediaURL::decode($user_input->initial_media_url)));// ActionFactorySafe::stop_playback(false, ActionFactorySafe::tv_play(MediaURL::decode($user_input->initial_media_url))));
						}
						break;

					case GUI_EVENT_KEY_STOP:
						if ($this->stb_firmware->branch_num <= 9)
						{
							send_ir_code(GUI_EVENT_KEY_RETURN);

							return
								$this->hide();
						}

						if ($this->stb_firmware->branch_num == 10)
							return $this->hide(ActionFactorySafe::update_osd(null));

						return
							$this->hide(ActionFactorySafe::close_dialog());

					case GUI_EVENT_KEY_RETURN:
						if ($this->display_playable_stream_info)
							return $this->hide();

						$this->display_live_epg_item = false;
						$parent_media_url = MediaURL::decode($user_input->parent_media_url);
						$prev_screen_id = self::$ret_prev_screen_id[$parent_media_url->screen_id];

						if (empty($prev_screen_id))
							return null;

						$media_url->screen_id = $prev_screen_id;

						if ($prev_screen_id == CustomEpgScreenDefs::programs_list)
						{
							unset($media_url->start_tm_utc);

							if (!isset($this->program_data[CustomExtEpgProgram::start_tm_sec]))
							{
								if (!isset($media_url->day_start_tm_utc))
									$media_url->day_start_tm_utc = $this->epg->get_day_start_ts();
							}
							else
								$media_url->day_start_tm_utc = $this->epg->get_day_start_ts($this->program_data[CustomExtEpgProgram::validated]? $this->program_data[CustomExtEpgProgram::start_tm_sec] : null);
						}
						else if (($prev_screen_id == CustomEpgScreenDefs::channels_list) || ($prev_screen_id == CustomEpgScreenDefs::groups_list))
							unset($media_url->day_start_tm_utc, $media_url->start_tm_utc);

						$range = $this->get_folder_range($media_url, 0, $plugin_cookies);
						$sel_item = ($media_url->screen_id == CustomEpgScreenDefs::programs_list)? $this->program_item_idx : (($media_url->screen_id == CustomEpgScreenDefs::channels_list)? $this->channel_item_idx : $this->group_item_idx);
						$update_action = (!defined('PluginOpenFolderActionData::id') && ($sel_item > 0))? ActionFactorySafe::update_regular_folder($range, true, $sel_item) : null;

						return
							$this->display(ActionFactorySafe::open_folder($media_url->get_raw_string(), '', null, $sel_item, $update_action));

					case 'show':
						$this->program_data =
							array
							(
								CustomExtEpgProgram::validated => false,
								CustomExtEpgProgram::start_tm_sec => 0,
							);

						$timer_actions = array();
						$this->replay_available = false;
						$this->display_please_wait = true;
						$this->display_live_epg_item = false;
						$this->display_playable_stream_info = false;

						if (!isset($media_url->day_start_tm_utc))
							$media_url->day_start_tm_utc = $this->epg->get_day_start_ts();

					case 'search_program_position':
						$list_empty = false;
						$this->scroll_page_event = 0;
						$this->scroll_lines_position = 0;
						$this->playback_archive_ts = empty($this->playback_archive_ts)? 0 : ((empty($player_state['playback_position']) || ($this->stb_firmware->branch_num <= 9))? $local_tm_ts + $this->tv_playback_archive_start_ts - $this->tv_playback_live_start_ts : $this->tv_playback_archive_start_ts + $player_state['playback_position'] - $this->epg->time_zone_offset);
						$tv_playback_ts = (empty($this->playback_live_ts) && empty($this->playback_archive_ts))? (empty($this->tv_playback_archive_start_ts)? time() : $this->tv_playback_archive_start_ts) : (($this->tv_playback_channel_id == $media_url->channel_id)? ((empty($this->tv_playback_archive_start_ts)? $this->playback_live_ts : $this->playback_archive_ts) + $this->epg->time_zone_offset) : time());

						if ($media_url->screen_id == CustomEpgScreenDefs::program_info)
						{
							// Поиск текущей программы и позиционирование массива
							$this->display_please_wait = true;
							$find_program_ts = isset($media_url->start_tm_utc)? $media_url->start_tm_utc : $tv_playback_ts;

							if (empty($find_program_ts))
							{
								$find_program_ts = time();
								$this->display_live_epg_item = true;
							}

							if (!$this->program_data[CustomExtEpgProgram::validated] = $this->find_program_by_time($find_result, $media_url->channel_id, $find_program_ts))
							{
								if ($this->display_please_wait)
								{
									if (empty($find_program_ts))
									{
										$this->display_please_wait = false;
										$this->display_live_epg_item = false;
									}
									else
									{
										$media_url->day_start_tm_utc = $find_result['day_ts'];
										$add_params = array('selected_media_url' => $media_url->get_raw_string());
										array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'search_program_position', $add_params), UserInputHandlerRegistry::create_action($this, 'load_day_epg', $add_params));
									}
								}
							}
							else
							{
								$this->program_data = $this->get_shown_program_data($media_url->channel_id, $find_result);
								$media_url->start_tm_utc = $this->program_data[CustomExtEpgProgram::start_tm_sec];
								$this->display_please_wait = false;
							}
						}

						if (($this->stb_firmware->branch_num <= 10) || !CUSTOM_EPG_SCREEN_AVAILABLE)
							return
								ActionFactorySafe::show_dialog(
									null,
									$this->get_main_dialog_controls_defs($media_url, $player_state, $plugin_cookies),
									($this->stb_firmware->branch_num <= 9),
									self::CUSTOM_EPG_DIALOG_WIDTH,
									array
									(
										'dialog_params' =>
											array
											(
												'frame_style' => DIALOG_FRAME_STYLE_GLASS,
											),
										ShowDialogActionData::actions => $this->get_main_dialog_action_map($media_url),
										ShowDialogActionData::timer => ActionFactorySafe::timer(100),
									));

						return
							$this->display(ActionFactorySafe::open_folder($media_url->get_raw_string()));

					case GUI_EVENT_KEY_INFO:
						$this->display_playable_stream_info = !$this->display_playable_stream_info;

						if ($this->stb_firmware->branch_num <= 9)
							array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'do_crutch'));

						break;

					case GUI_EVENT_KEY_LEFT:
						if (isset($user_input->display_prev_day))
						{
							$channel = $this->epg->get_channel($media_url->channel_id);
							$day_ts = $media_url->day_start_tm_utc - 86400;
							$live_program_ts = $this->find_program_by_time($find_result, $media_url->channel_id, time())? $find_result[PluginTvEpgProgram::start_tm_sec] : time();
							$today = $this->epg->get_day_start_ts($live_program_ts);

							if ($today > $day_ts + 86400 * $channel[ExtTvChannel::past_epg_days])
								return null;
						}
						else
							$find_ts = $this->program_data[CustomExtEpgProgram::start_tm_sec] - 60;

					case GUI_EVENT_KEY_RIGHT:
						if (isset($user_input->display_prev_day) || isset($user_input->display_next_day))
						{
							if (!isset($day_ts))
							{
								$channel = $this->epg->get_channel($media_url->channel_id);
								$day_ts = $media_url->day_start_tm_utc + 86400;
								$live_program_ts = $this->find_program_by_time($find_result, $media_url->channel_id, time())? $find_result[PluginTvEpgProgram::start_tm_sec] : time();
								$today = $this->epg->get_day_start_ts($live_program_ts);

								if ($day_ts > $today + 86400 * $channel[ExtTvChannel::future_epg_days])
									return null;
							}

							$list_empty = false;
							$day = $day_ts;

							if (!$epg = $this->epg->get_day_slice($media_url->channel_id, $day))
							{
								$list_empty = true;

								if (!empty($day))
								{
									$this->display_please_wait = true;
									$selected_media_url = array('selected_media_url' => $media_url->get_raw_string(), (isset($user_input->display_prev_day)? 'display_prev_day' : 'display_next_day') => 1);
									$media_url->day_start_tm_utc = $day_ts;
									array_push($timer_actions, UserInputHandlerRegistry::create_action($this, $control_id, $selected_media_url), UserInputHandlerRegistry::create_action($this, 'load_day_epg', array('selected_media_url' => $media_url->get_raw_string())));

									return
										$this->display(ActionFactorySafe::open_folder($media_url->get_raw_string()));
								}
							}

							$this->display_please_wait = false;
							$media_url->day_start_tm_utc = $day_ts;
							$range = $this->get_folder_range($media_url, 0, $plugin_cookies);
							$sel_item = ($media_url->screen_id == CustomEpgScreenDefs::programs_list)? $this->program_item_idx : (($media_url->screen_id == CustomEpgScreenDefs::channels_list)? $this->channel_item_idx : $this->group_item_idx);
							$update_action = (!defined('PluginOpenFolderActionData::id') && ($sel_item > 0))? ActionFactorySafe::update_regular_folder($range, true, $sel_item) : null;

							return
								$this->display(ActionFactorySafe::open_folder($media_url->get_raw_string(), '', null, $sel_item, $update_action));
						}

						if (!empty($timer_actions))
							break;

						if (!$this->find_program_by_time($find_result, $media_url->channel_id, isset($find_ts)? $find_ts : $this->program_data[CustomExtEpgProgram::end_tm_sec]))
						{
							if (empty($find_result['day_ts']))
							{
								$this->display_please_wait = false;
								break;
							}

							$this->display_please_wait = true;
							$media_url->day_start_tm_utc = $find_result['day_ts'];
							$add_params = array('selected_media_url' => $media_url->get_raw_string());
							array_push($timer_actions, UserInputHandlerRegistry::create_action($this, $control_id, $add_params), UserInputHandlerRegistry::create_action($this, 'load_day_epg', $add_params));

							return
								CUSTOM_EPG_SCREEN_AVAILABLE?
									ActionFactorySafe::change_behaviour($this->get_main_screen_action_map($media_url), ActionFactorySafe::timer(100), ActionFactorySafe::reset_controls($this->get_main_screen_controls_defs($media_url, $player_state, $plugin_cookies))) :
									ActionFactorySafe::change_behaviour($this->get_main_dialog_action_map($media_url), ActionFactorySafe::timer(100), ActionFactorySafe::reset_controls($this->get_main_dialog_controls_defs($media_url, $player_state, $plugin_cookies)));
						}
						else
						{
							$this->program_data = $this->get_shown_program_data($media_url->channel_id, $find_result);
							$media_url->day_start_tm_utc = $find_result['day_ts'];
						}

						$this->display_live_epg_item = false;
						$this->display_please_wait = false;
						$this->scroll_lines_position = 0;
						if ($this->stb_firmware->branch_num <= 9)
							array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'do_crutch'));

						break;

					case 'load_day_epg':
						$update_epg_action = $this->get_update_epg_action($media_url->channel_id, $media_url->day_start_tm_utc, $plugin_cookies);

						if (!empty($update_epg_action))
							return ActionFactorySafe::change_behaviour($this->get_main_screen_action_map($media_url), ActionFactorySafe::timer(empty($timer_actions)? 1000 : 100), $update_epg_action);

						$list_empty = true;
						$this->display_please_wait = false;
						$timer_actions = array();

						break;

					case GUI_EVENT_KEY_DOWN:
						if (isset($user_input->screen_key_down))
							$update_action = ActionFactorySafe::update_regular_folder($this->get_folder_range(MediaURL::decode($user_input->parent_media_url), 0, $plugin_cookies), true, 1);

					case GUI_EVENT_KEY_UP:
						if (isset($user_input->screen_key_down) || isset($user_input->screen_key_up))
						{
							$this->display_please_wait = false;
							$parent_media_url = MediaURL::decode($user_input->parent_media_url);

							return
								ActionFactorySafe::change_behaviour($this->get_main_screen_action_map($parent_media_url), null, empty($update_action)? null : $update_action);
						}

						if ($this->display_please_wait)
							break;

						if ($control_id == GUI_EVENT_KEY_UP)
							--$this->scroll_lines_position;
						else if ($control_id == GUI_EVENT_KEY_DOWN)
							++$this->scroll_lines_position;
						else break;

						if ($this->stb_firmware->branch_num <= 9)
							array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'do_crutch'));

						break;

					case GUI_EVENT_KEY_P_PLUS:
						if (!$this->display_please_wait)
							$this->scroll_page_event = -1;

						break;

					case GUI_EVENT_KEY_P_MINUS:
						if (!$this->display_please_wait)
							$this->scroll_page_event = 1;

						break;

					case 'do_crutch': // Only for b9
						send_ir_code(GUI_EVENT_KEY_CLEAR);

						return
							ActionFactorySafe::change_behaviour($this->get_main_dialog_action_map($media_url), ActionFactorySafe::timer(1000));

					case GUI_EVENT_KEY_ENTER:
						if (isset($user_input->choose_folder_item))
						{
							if (($media_url->screen_id == CustomEpgScreenDefs::programs_list) && $list_empty)
								return null;

							if ($media_url->screen_id == CustomEpgScreenDefs::groups_list)
								$this->tv_playback_group_id = $media_url->group_id;

							$media_url->screen_id = self::$get_next_screen_id[$media_url->screen_id];

							if ($media_url->screen_id == CustomEpgScreenDefs::program_info)
							{
								if (isset($media_url->channel_id))
								{
									$control_id = 'show';
									continue 2;
								}

								return null;
							}

							if ($media_url->screen_id == CustomEpgScreenDefs::programs_list)
							{
								if (!isset($media_url->channel_id))
									return null;

								$media_url->start_tm_utc = $this->find_program_by_time($find_result, $media_url->channel_id, time())? $find_result[PluginTvEpgProgram::start_tm_sec] : time();
								$media_url->day_start_tm_utc = $this->epg->get_day_start_ts($media_url->start_tm_utc);
								$media_url->day_start_tm_utc -= 86400;
								$control_id = GUI_EVENT_KEY_RIGHT;
								$user_input->display_next_day = 1;
								continue 2;
							}

							$range = $this->get_folder_range($media_url, 0, $plugin_cookies);
							$sel_item = ($media_url->screen_id == CustomEpgScreenDefs::programs_list)? $this->program_item_idx : (($media_url->screen_id == CustomEpgScreenDefs::channels_list)? $this->channel_item_idx : $this->group_item_idx);
							$update_action = (!defined('PluginOpenFolderActionData::id') && ($sel_item > 0))? ActionFactorySafe::update_regular_folder($range, true, $sel_item) : null;

							return
								$this->display(ActionFactorySafe::open_folder($media_url->get_raw_string(), '', null, $sel_item, $update_action));
						}

						if ($this->display_please_wait)
							break;

						if ($this->replay_available)
						{
							$action =
								UserInputHandlerRegistry::create_action(
									$this,
									'change_media_url',
									array
									(
										'initial_media_url' =>
											MediaURL::encode(
												array
												(
													'group_id' => $media_url->group_id,
													'channel_id' => $media_url->channel_id,
													'archive_tm' => $this->program_data[CustomExtEpgProgram::start_tm_sec],
													'is_favorite' => ($media_url->group_id == $this->epg->tv_fav_channels_group_id),
												)
											),
									)
								);

							if ($this->stb_firmware->branch_num <= 9)
							{
								send_ir_code(GUI_EVENT_KEY_RETURN);
								return $action;
							}

							return $this->hide($action);
						}

						if ($this->program_data[CustomExtEpgProgram::start_tm_sec] - 60 > time())
						{
							$reminders = $this->epg->get_reminders_list($plugin_cookies);
							if (isset($reminders[$media_url->channel_id][$this->program_data[CustomExtEpgProgram::start_tm_sec]]))
							{
								unset($reminders[$media_url->channel_id][$this->program_data[CustomExtEpgProgram::start_tm_sec]]);
								if (empty($reminders[$media_url->channel_id]))
									unset($reminders[$media_url->channel_id]);
							}
							else
								$reminders[$media_url->channel_id][$this->program_data[CustomExtEpgProgram::start_tm_sec]] = $this->program_data[CustomExtEpgProgram::day_start_ts];

							$this->epg->set_reminders_list($reminders, $plugin_cookies);
							array_push($timer_actions, $this->get_update_epg_action($media_url->channel_id, $this->program_data[CustomExtEpgProgram::day_start_ts], $plugin_cookies));
						}

						break;

					case GUI_EVENT_KEY_PLAY:
						if ($this->display_please_wait)
							break;

						if (($this->tv_playback_channel_id == $media_url->channel_id) && (empty($this->tv_playback_archive_start_ts)))
							break;

						if (!$this->display_live_epg_item)
						{
							$media_url->start_tm_utc = 0;
							$this->program_data[CustomExtEpgProgram::start_tm_sec] = 0;
							array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'search_program_position', array('selected_media_url' => $media_url->get_raw_string())));

							if ($this->stb_firmware->branch_num < 10)
								array_push($timer_actions, UserInputHandlerRegistry::create_action($this, 'do_crutch'));

							break;
						}

						$action =
							UserInputHandlerRegistry::create_action(
								$this,
								'change_media_url',
								array
								(
									'initial_media_url' =>
										MediaURL::encode
										(
											array
											(
												'group_id' => $media_url->group_id,
												'channel_id' => $media_url->channel_id,
												'archive_tm' => -1,
												'is_favorite' => ($media_url->group_id == $this->epg->tv_fav_channels_group_id),
											)
										),
								)
							);

						if ($this->stb_firmware->branch_num <= 9)
						{
							send_ir_code(GUI_EVENT_KEY_RETURN);
							return $action;
						}

						return $this->hide($action);

					case GUI_EVENT_TIMER:
						if (isset($user_input->update_reminder_dialog))
							return
								ActionFactorySafe::change_behaviour(
									$this->get_reminder_dialog_action_map($media_url),
									ActionFactorySafe::timer(1000),
									ActionFactorySafe::reset_controls($this->get_reminder_dialog_controls_defs($media_url)));

						if (isset($player_state['video_on_top']) && $player_state['video_on_top'] && (STB_FIRMWARE_BL == 'r'))
							disable_video_on_top();

						if (!is_null($reminder_dialog_action = $this->get_reminder_dialog_action($media_url->channel_id, $media_url->group_id, $plugin_cookies)))
							array_push($timer_actions, $this->hide($reminder_dialog_action));
				}

				return
					CUSTOM_EPG_SCREEN_AVAILABLE?
						ActionFactorySafe::change_behaviour($this->get_main_screen_action_map($media_url), ActionFactorySafe::timer(empty($timer_actions)? 1000 : 100), ActionFactorySafe::reset_controls($this->get_main_screen_controls_defs($media_url, $player_state, $plugin_cookies), array_pop($timer_actions))) :
						ActionFactorySafe::change_behaviour($this->get_main_dialog_action_map($media_url), ActionFactorySafe::timer(empty($timer_actions)? 1000 : 100), ActionFactorySafe::reset_controls($this->get_main_dialog_controls_defs($media_url, $player_state, $plugin_cookies), array_pop($timer_actions)));
			}
		}
		return null;
	}
}

///////////////////////////////////////////////////////////////////////////////
?>
