/*
 * Copyright (C) 2003 by Fredrik Noring
 * Copyright (C) 2003-2020 the xine project
 *
 * This file is part of xine, a unix video player.
 *
 * xine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * xine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 * Initial version by Fredrik Noring, January 2003, based on the xitk
 * and dfb sources.
 */

#ifndef __sun
#define _XOPEN_SOURCE 500
#endif
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <strings.h>

#include "main.h"
#include "actions.h"
#include "osd.h"
#include "post.h"

#define GUI_NEXT     1
#define GUI_PREV     2
#define GUI_RESET    3

parameter_t gParameter =
{
	{ 0, 0    },
	{ 0, NULL }
};

static const struct
{
	const char *description;
	const char *name;
	int action;
} default_actions[] =
{
	{ "Select next sub picture (subtitle) channel.",
		  "SpuNext", ACTID_SPU_NEXT },
	{ "Select previous sub picture (subtitle) channel.",
		  "SpuPrior", ACTID_SPU_PRIOR },
	{ "Select default sub picture (subtitle) channel.",
		  "SpuDefault", ACTID_SPU_DEFAULT },
	{ "Select next audio channel.",
		  "AudioChannelNext", ACTID_AUDIOCHAN_NEXT },
	{ "Select previous audio channel.",
		  "AudioChannelPrior", ACTID_AUDIOCHAN_PRIOR },
	{ "Set default audio channel.",
		  "AudioChannelDefault", ACTID_AUDIOCHAN_DEFAULT },
	{ "Playback pause toggle.",
		  "Pause", ACTID_PAUSE },
	{ "Aspect ratio values toggle.",
		  "ToggleAspectRatio", ACTID_TOGGLE_ASPECT_RATIO },
	{ "Display stream information using OSD.",
		  "OSDStreamInfos", ACTID_OSD_SINFOS },
	{ "Display information using OSD.",
		  "OSDWriteText", ACTID_OSD_WTEXT },

	{ "Interlaced mode toggle.",
		  "ToggleInterleave", ACTID_TOGGLE_INTERLEAVE },
	{ "Quit the program.",
		  "Quit", ACTID_QUIT },
	{ "Start playback.",
		  "Play", ACTID_PLAY },

	{ "Visibility toggle of the setup window.",
		  "SetupShow", ACTID_SETUP },
	{ "Stop playback.",
		  "Stop", ACTID_STOP },
	{ "Select and play next mrl in the playlist.",
		  "NextMrl", ACTID_MRL_NEXT },
	{ "Select and play previous mrl in the playlist.",
		  "PriorMrl", ACTID_MRL_PRIOR },
	{ "Visibility toggle of the event sender window.",
		  "EventSenderShow", ACTID_EVENT_SENDER },
	{ "Edit selected mediamark.",
		  "MediamarkEditor", ACTID_MMKEDITOR },
	{ "Eject the current medium.",
		  "Eject", ACTID_EJECT },

	{ "Set position to numeric-argument%% of current stream.",
		  "SetPosition%", ACTID_SET_CURPOS },
	{ "Set position to beginning of current stream.",
		  "SetPosition0%", ACTID_SET_CURPOS_0 },
	{ "Set position to 10%% of current stream.",
		  "SetPosition10%", ACTID_SET_CURPOS_10 },
	{ "Set position to 20%% of current stream.",
		  "SetPosition20%", ACTID_SET_CURPOS_20 },
	{ "Set position to 30%% of current stream.",
		  "SetPosition30%", ACTID_SET_CURPOS_30 },
	{ "Set position to 40%% of current stream.",
		  "SetPosition40%", ACTID_SET_CURPOS_40 },
	{ "Set position to 50%% of current stream.",
		  "SetPosition50%", ACTID_SET_CURPOS_50 },
	{ "Set position to 60%% of current stream.",
		  "SetPosition60%", ACTID_SET_CURPOS_60 },
	{ "Set position to 70%% of current stream.",
		  "SetPosition70%", ACTID_SET_CURPOS_70 },
	{ "Set position to 80%% of current stream.",
		  "SetPosition80%", ACTID_SET_CURPOS_80 },
	{ "Set position to 90%% of current stream.",
		  "SetPosition90%", ACTID_SET_CURPOS_90 },
	{ "Enter the number 0.",
		  "Number0", ACTID_EVENT_NUMBER_0 },
	{ "Enter the number 1.",
		  "Number1", ACTID_EVENT_NUMBER_1 },
	{ "Enter the number 2.",
		  "Number2", ACTID_EVENT_NUMBER_2 },
	{ "Enter the number 3.",
		  "Number3", ACTID_EVENT_NUMBER_3 },
	{ "Enter the number 4.",
		  "Number4", ACTID_EVENT_NUMBER_4 },
	{ "Enter the number 5.",
		  "Number5", ACTID_EVENT_NUMBER_5 },
	{ "Enter the number 6.",
		  "Number6", ACTID_EVENT_NUMBER_6 },
	{ "Enter the number 7.",
		  "Number7", ACTID_EVENT_NUMBER_7 },
	{ "Enter the number 8.",
		  "Number8", ACTID_EVENT_NUMBER_8 },
	{ "Enter the number 9.",
		  "Number9", ACTID_EVENT_NUMBER_9 },
	{ "Add 10 to the next entered number.",
		  "Number10add", ACTID_EVENT_NUMBER_10_ADD },
	{ "Set position back by numeric argument in current stream.",
		  "SeekRelative-", ACTID_SEEK_REL_m },
	{ "Set position to -60 seconds in current stream.",
		  "SeekRelative-60", ACTID_SEEK_REL_m60 },
	{ "Set position forward by numeric argument in current stream.",
		  "SeekRelative+", ACTID_SEEK_REL_p },
	{ "Set position to +60 seconds in current stream.",
		  "SeekRelative+60", ACTID_SEEK_REL_p60 },
	{ "Set position to -30 seconds in current stream.",
		  "SeekRelative-30", ACTID_SEEK_REL_m30 },
	{ "Set position to +30 seconds in current stream.",
		  "SeekRelative+30", ACTID_SEEK_REL_p30 },
	{ "Set position to -15 seconds in current stream.",
		  "SeekRelative-15", ACTID_SEEK_REL_m15 },
	{ "Set position to +15 seconds in current stream.",
		  "SeekRelative+15", ACTID_SEEK_REL_p15 },
	{ "Set position to -7 seconds in current stream.",
		  "SeekRelative-7", ACTID_SEEK_REL_m7 },
	{ "Set position to +7 seconds in current stream.",
		  "SeekRelative+7", ACTID_SEEK_REL_p7 },

	{ "Audio muting toggle.",
		  "Mute", ACTID_MUTE },
	{ "Change audio syncing.",
		  "AudioVideoDecay+", ACTID_AV_SYNC_p3600 },
	{ "Change audio syncing.",
		  "AudioVideoDecay-", ACTID_AV_SYNC_m3600 },
	{ "Reset audio video syncing offset.",
		  "AudioVideoDecayReset", ACTID_AV_SYNC_RESET },
	{ "Increment playback speed.",
		  "SpeedFaster", ACTID_SPEED_FAST },
	{ "Decrement playback speed.",
		  "SpeedSlower", ACTID_SPEED_SLOW },
	{ "Reset playback speed.",
		  "SpeedReset", ACTID_SPEED_RESET },
	{ "Increment audio volume.",
		  "Volume+", ACTID_pVOLUME },
	{ "Decrement audio volume.",
		  "Volume-", ACTID_mVOLUME },
	{ "Take a snapshot (Internal image fetch and save).",
		  "Snapshot", ACTID_SNAPSHOT },

	{ "Jump to Menu.",
		  "Menu", ACTID_EVENT_MENU1 },
	{ "Jump to Title Menu.",
		  "TitleMenu", ACTID_EVENT_MENU2 },
	{ "Jump to Root Menu.",
		  "RootMenu", ACTID_EVENT_MENU3 },
	{ "Jump to Subpicture Menu.",
		  "SubpictureMenu", ACTID_EVENT_MENU4 },
	{ "Jump to Audio Menu.",
		  "AudioMenu", ACTID_EVENT_MENU5 },
	{ "Jump to Angle Menu.",
		  "AngleMenu", ACTID_EVENT_MENU6 },
	{ "Jump to Part Menu.",
		  "PartMenu", ACTID_EVENT_MENU7 },
	{ "Up event.",
		  "EventUp", ACTID_EVENT_UP },
	{ "Down event.",
		  "EventDown", ACTID_EVENT_DOWN },
	{ "Left event.",
		  "EventLeft", ACTID_EVENT_LEFT },
	{ "Right event.",
		  "EventRight", ACTID_EVENT_RIGHT },
	{ "Previous event.",
		  "EventPrior", ACTID_EVENT_PRIOR },
	{ "Next event.",
		  "EventNext", ACTID_EVENT_NEXT },
	{ "Previous angle event.",
		  "EventAnglePrior", ACTID_EVENT_ANGLE_PRIOR },
	{ "Next angle event.",
		  "EventAngleNext", ACTID_EVENT_ANGLE_NEXT },
	{ "Select event.",
		  "EventSelect", ACTID_EVENT_SELECT },

	{ "Zoom in.",
		  "ZoomIn", ACTID_ZOOM_IN },
	{ "Zoom out.",
		  "ZoomOut", ACTID_ZOOM_OUT },
	{ "Reset zooming.",
		  "ZoomReset", ACTID_ZOOM_RESET },

	{ "Loop mode toggle.",
		  "ToggleLoopMode", ACTID_LOOPMODE },

#ifdef ENABLE_VDR_KEYS
	{ "VDR Red button",
	  "VDRButtonRed",           ACTID_EVENT_VDR_RED },
	{ "VDR Green button",
	  "VDRButtonGreen",         ACTID_EVENT_VDR_GREEN },
	{ "VDR Yellow button",
	  "VDRButtonYellow",        ACTID_EVENT_VDR_YELLOW },
	{ "VDR Blue button",
	  "VDRButtonBlue",          ACTID_EVENT_VDR_BLUE },
	{ "VDR Play",
	  "VDRPlay",                ACTID_EVENT_VDR_PLAY },
	{ "VDR Pause",
	  "VDRPause",               ACTID_EVENT_VDR_PAUSE },
	{ "VDR Stop",
	  "VDRStop",                ACTID_EVENT_VDR_STOP },
	{ "VDR Record",
	  "VDRRecord",              ACTID_EVENT_VDR_RECORD },
	{ "VDR Fast forward",
	  "VDRFastFwd",             ACTID_EVENT_VDR_FASTFWD },
	{ "VDR Fast rewind",
	  "VDRFastRew",             ACTID_EVENT_VDR_FASTREW },
	{ "VDR Power",
	  "VDRPower",               ACTID_EVENT_VDR_POWER },
	{ "VDR Channel +",
	  "VDRChannelPlus",         ACTID_EVENT_VDR_CHANNELPLUS },
	{ "VDR Channel -",
	  "VDRChannelMinus",        ACTID_EVENT_VDR_CHANNELMINUS },
	{ "VDR Schedule menu",
	  "VDRSchedule",            ACTID_EVENT_VDR_SCHEDULE },
	{ "VDR Channel menu",
	  "VDRChannels",            ACTID_EVENT_VDR_CHANNELS },
	{ "VDR Timers menu",
	  "VDRTimers",              ACTID_EVENT_VDR_TIMERS },
	{ "VDR Recordings menu",
	  "VDRRecordings",          ACTID_EVENT_VDR_RECORDINGS },
	{ "VDR Setup menu",
	  "VDRSetup",               ACTID_EVENT_VDR_SETUP },
	{ "VDR Command menu",
	  "VDRCommands",            ACTID_EVENT_VDR_COMMANDS },
	{ "VDR Command back",
	  "VDRBack",                ACTID_EVENT_VDR_BACK },
#ifdef XINE_EVENT_VDR_USER0 /* #ifdef is precaution for backward compatibility at the moment */
	{ "VDR User command 0",
	  "VDRUser0",               ACTID_EVENT_VDR_USER0 },
#endif
	{ "VDR User command 1",
	  "VDRUser1",               ACTID_EVENT_VDR_USER1 },
	{ "VDR User command 2",
	  "VDRUser2",               ACTID_EVENT_VDR_USER2 },
	{ "VDR User command 3",
	  "VDRUser3",               ACTID_EVENT_VDR_USER3 },
	{ "VDR User command 4",
	  "VDRUser4",               ACTID_EVENT_VDR_USER4 },
	{ "VDR User command 5",
	  "VDRUser5",               ACTID_EVENT_VDR_USER5 },
	{ "VDR User command 6",
	  "VDRUser6",               ACTID_EVENT_VDR_USER6 },
	{ "VDR User command 7",
	  "VDRUser7",               ACTID_EVENT_VDR_USER7 },
	{ "VDR User command 8",
	  "VDRUser8",               ACTID_EVENT_VDR_USER8 },
	{ "VDR User command 9",
	  "VDRUser9",               ACTID_EVENT_VDR_USER9 },
	{ "VDR Volume +",
	  "VDRVolumePlus",          ACTID_EVENT_VDR_VOLPLUS },
	{ "VDR Volume -",
	  "VDRVolumeMinus",         ACTID_EVENT_VDR_VOLMINUS },
	{ "VDR Mute audio",
	  "VDRMute",                ACTID_EVENT_VDR_MUTE },
	{ "VDR Audio menu",
	  "VDRAudio",               ACTID_EVENT_VDR_AUDIO },
	{ "VDR Command info",
	  "VDRInfo",                ACTID_EVENT_VDR_INFO },
#ifdef XINE_EVENT_VDR_CHANNELPREVIOUS /* #ifdef is precaution for backward compatibility at the moment */
	{ "VDR Previous channel",
	  "VDRChannelPrevious",     ACTID_EVENT_VDR_CHANNELPREVIOUS },
#endif
#ifdef XINE_EVENT_VDR_SUBTITLES /* #ifdef is precaution for backward compatibility at the moment */
	{ "VDR Subtitles menu",
	  "VDRSubtitles",           ACTID_EVENT_VDR_SUBTITLES },
#endif
#endif
	{ 0,
	  0,                        0 }
};

int default_command_action(char *action_name)
{
	int i;

	for(i = 0; default_actions[i].name; i++)
		if(!strcasecmp(default_actions[i].name, action_name))
			return default_actions[i].action;

	return 0;   /* Unknown action */
};

static void action_pause(void)
{
	xine_set_param(fbxine.stream, XINE_PARAM_SPEED,
		       xine_get_param(fbxine.stream, XINE_PARAM_SPEED) ==
		       XINE_SPEED_PAUSE ? XINE_SPEED_NORMAL:XINE_SPEED_PAUSE);
	osd_update_status();
}

int get_pos_length(xine_stream_t *stream, int *pos, int *time, int *length)
{
	int t = 0, ret = 0;

	if(stream && xine_get_status(stream) == XINE_STATUS_PLAY)
		for(;;)
		{
			ret = xine_get_pos_length(stream, pos, time, length);
			if(ret || 10 < ++t)
				break;
			xine_usec_sleep(100000); /* wait before trying again */
		}

	return ret;
}

static int play(xine_stream_t *stream,
                int start_pos, int start_time_in_secs)
{
	return xine_play(stream, start_pos, start_time_in_secs * 1000);
}

static void action_play(void)
{
	play(fbxine.stream, 0, 0);
}

static __attribute__((noreturn)) void *seek_relative_thread(void *data)
{
	int sec, off_sec = (int)(intptr_t)data;

	pthread_detach(pthread_self());

	if(get_pos_length(fbxine.stream, 0, &sec, 0))
	{
		sec /= 1000;

		if(sec + off_sec < 0)
			sec = 0;
		else
			sec += off_sec;

                play(fbxine.stream, 0, sec);
		osd_stream_position();
	}

	fbxine.ignore_next = 0;
	pthread_exit(0);
}

static void action_seek_relative(int off_sec)
{
	static pthread_t seek_thread;
	int err;

	if(fbxine.ignore_next ||
	   !xine_get_stream_info(fbxine.stream, XINE_STREAM_INFO_SEEKABLE) ||
	   xine_get_status(fbxine.stream) != XINE_STATUS_PLAY)
		return;

	fbxine.ignore_next = 1;

	err = pthread_create(&seek_thread, 0, seek_relative_thread,
			     (void *)(intptr_t)off_sec);
	if(!err)
		return;

	fprintf(stderr, "Failed to create action_seek_relative thread.\n");
	abort();
}


static void change_audio_channel(void *data)
{
        int dir = (int)(intptr_t)data;
	int channel;

	channel = xine_get_param(fbxine.stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL);

	if(dir == GUI_NEXT)
	     channel++;
	else if(dir == GUI_PREV)
	     channel--;
	else if(dir == GUI_RESET)
	     channel = -1;

        xine_set_param(fbxine.stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, channel);
	osd_display_audio_lang();
}

static void change_spu(void *data)
{
        int dir = (int)(intptr_t)data;
	int channel;

	channel = xine_get_param(fbxine.stream, XINE_PARAM_SPU_CHANNEL);

	if(dir == GUI_NEXT)
	     channel++;
	else if(dir == GUI_PREV)
	     channel--;
	else if(dir == GUI_RESET)
	     channel = -1;

        xine_set_param(fbxine.stream, XINE_PARAM_SPU_CHANNEL, channel);
	osd_display_spu_lang();
}


static void change_zoom(int zoom_dx, int zoom_dy)
{
	xine_set_param(fbxine.stream, XINE_PARAM_VO_ZOOM_X,
		       xine_get_param(fbxine.stream, XINE_PARAM_VO_ZOOM_X) + zoom_dx);
	xine_set_param(fbxine.stream, XINE_PARAM_VO_ZOOM_Y,
		       xine_get_param(fbxine.stream, XINE_PARAM_VO_ZOOM_Y) + zoom_dy);

	osd_display_zoom();
}

static void reset_zoom(void)
{
	xine_set_param(fbxine.stream, XINE_PARAM_VO_ZOOM_X, 100);
	xine_set_param(fbxine.stream, XINE_PARAM_VO_ZOOM_Y, 100);

	osd_display_zoom();
}


void do_action(int action)
{
	if(action & ACTID_IS_INPUT_EVENT)
	{
		xine_event_t xine_event;

		xine_event.type = action & ~ACTID_IS_INPUT_EVENT;
		xine_event.data_length = 0;
		xine_event.data = 0;
		xine_event.stream = fbxine.stream;
		gettimeofday(&xine_event.tv, NULL);

		xine_event_send(fbxine.stream, &xine_event);
		return;
	}

	switch(action)
	{
		case ACTID_SEEK_REL_m600:
			action_seek_relative(-600);
			break;
		case ACTID_SEEK_REL_p600:
			action_seek_relative(600);
			break;

		case ACTID_SEEK_REL_m60:
			action_seek_relative(-60);
			break;
		case ACTID_SEEK_REL_p60:
			action_seek_relative(60);
			break;

		case ACTID_SEEK_REL_p30:
			action_seek_relative(30);
			break;
		case ACTID_SEEK_REL_m30:
			action_seek_relative(-30);
			break;

		case ACTID_SEEK_REL_p15:
			action_seek_relative(15);
			break;
		case ACTID_SEEK_REL_m15:
			action_seek_relative(-15);
			break;

		case ACTID_SEEK_REL_p10:
			action_seek_relative(10);
			break;
		case ACTID_SEEK_REL_m10:
			action_seek_relative(-10);
			break;

		case ACTID_SEEK_REL_m7:
			action_seek_relative(-7);
			break;
		case ACTID_SEEK_REL_p7:
			action_seek_relative(7);
			break;

	        case ACTID_AUDIOCHAN_NEXT:
		        change_audio_channel((void*)GUI_NEXT);
			break;

	        case ACTID_AUDIOCHAN_PRIOR:
		        change_audio_channel((void*)GUI_PREV);
			break;

	        case ACTID_AUDIOCHAN_DEFAULT:
		        change_audio_channel((void*)GUI_RESET);
			break;

	        case ACTID_SPU_NEXT:
		        change_spu((void*)GUI_NEXT);
			break;

	        case ACTID_SPU_PRIOR:
		        change_spu((void*)GUI_PREV);
			break;

	        case ACTID_SPU_DEFAULT:
		        change_spu((void*)GUI_RESET);
			break;

	        case ACTID_OSD_SINFOS:
		        osd_stream_infos();
		        break;

		case ACTID_OSD_WTEXT:
			if (gParameter.alphanum.set) {
				osd_display_info("%s", gParameter.alphanum.arg);
			} else {
				osd_display_info("No text to display!");
			}
			break;

		case ACTID_PAUSE:
			action_pause();
			break;

		case ACTID_PLAY:
			action_play();
			break;

		case ACTID_QUIT:
			fbxine_exit();
			break;

		case ACTID_ZOOM_IN:
			change_zoom(5, 5);
			break;
		case ACTID_ZOOM_OUT:
			change_zoom(-5, -5);
			break;
		case ACTID_ZOOM_RESET:
			reset_zoom();
			break;

	        case ACTID_TOGGLE_INTERLEAVE:
		        fbxine.deinterlace_enable = !fbxine.deinterlace_enable;
			osd_display_info("Deinterlace: %s", (fbxine.deinterlace_enable) ?
					 "enabled" : "disabled");
			post_deinterlace();
		        break;
	}
}
