/*
 * GQmpeg
 * (C)1998, 1999 John Ellis
 *
 * Author: John Ellis
 *
 * This software is released under the GNU General Public License.
 * Please read the included file COPYING for more information.
 * This software comes with no warranty of any kind, use at you own risk!
 */

#include "config.h"
#include "gqmpeg.h"

static gint update_timeout_id = -1;

/*
 *-----------------------------------------------------------------------------
 * path manipulation routines (public)
 *-----------------------------------------------------------------------------
 */ 

gchar *filename_from_path(char *t)
{
	char *p;

	p = t + strlen(t);
	while(p > t && p[0] != '/') p--;
	if (p[0] == '/') p++;
	return p;
}

gchar *remove_level_from_path(gchar *path)
{
	gchar *new_path;
	gchar *ptr = path;
	gint p;

	if (!path) return NULL;

	p = strlen(path) - 1;
	if (p < 0) return NULL;
	while(ptr[p] != '/' && p > 0) p--;
	if (p == 0 && ptr[p] == '/') p++;
	new_path = g_strndup(path, (guint)p);
	return new_path;
}

void parse_out_relatives(gchar *path)
{
	gint s, t;

	if (!path) return;

	s = t = 0;

	while (path[s] != '\0')
		{
		if (path[s] == '/' && path[s+1] == '.' && (path[s+2] == '/' || path[s+2] == '\0') )
			{
			s += 2;
			}
		else if (path[s] == '/' && path[s+1] == '.' && path[s+2] == '.' && (path[s+3] == '/' || path[s+3] == '\0') )
			{
			s += 3;
			if (t > 0) t--;
			while (path[t] != '/' && t > 0) t--;
			}
		else
			{
			if (s != t) path[t] = path[s];
			t++;
			s++;
			}
		}
	if (t == 0 && path[t] == '/') t++;
	if (t > 1 && path[t-1] == '/') t--;
	path[t] = '\0';
}

gint file_in_path(gchar *file)
{
	gchar *path = g_strdup(getenv("PATH"));
	gint p, l;

	if (!path) return FALSE;

	p = 0;
	l = strlen(path);
	while (p < l)
		{
		gchar *f;
		gint e = p;
		while (path[e] != ':' && path[e] != '\0') e++;
		path[e] = '\0';
		e++;
		f = g_strconcat(path + p, "/", file, NULL);
		if (isfile(f))
			{
			g_free(f);
			g_free(path);
			return TRUE;
			}
		g_free(f);
		p = e;
		}
	return FALSE;
}

/*
 *-----------------------------------------------------------------------------
 * current song routines (public)
 *-----------------------------------------------------------------------------
 */

static SongData *current_songdata = NULL;
static gint current_song_in_playlist = FALSE;

static void clear_current_song()
{
	if (!current_songdata) return;

	if (status != STATUS_STOP && status != STATUS_NEXT)
		{
		playback_stop(current_songdata);
		}

	if (!current_song_in_playlist)
		{
		playlist_free_data(current_songdata);
		}

	current_songdata = NULL;
	current_song_in_playlist = FALSE;
}

SongData *current_song_get_data()
{
	return current_songdata;
}

gint current_song_get_number()
{
	if (current_song_in_playlist && current_songdata)
		{
		return playlist_get_index_by_data(current_songdata);
		}
	else
		{
		return -1;
		}
}

void current_song_set(gint n, gchar *path)
{
	gint old_status = status;

	if (path)
		{
		clear_current_song();
		current_songdata = playlist_new_data(path);
		current_song_in_playlist = FALSE;
		}
	else if (n >= 0)
		{
		SongData *sd = playlist_get_data(n);
		if (sd)
			{
			if (current_song_in_playlist)
				{
				if (current_songdata == sd && sd->live) return;
				}
			clear_current_song();
			current_songdata = sd;
			current_song_in_playlist = TRUE;
			}
		else
			{
			clear_current_song();
			current_song_in_playlist = FALSE;
			}
		}
	else
		{
		clear_current_song();
		}

	display_draw_song_info(TRUE);

	if ((old_status == STATUS_PLAY || old_status == STATUS_NEXT) && current_songdata)
		{
		playback_start(current_songdata , 0);
		}
}

gint current_song_is_in_playlist()
{
	return current_song_in_playlist;
}

void current_song_set_to_next()
{
	gint n = current_song_get_number();
	if (n == -1)
		{
		gint p = playlist_get_first();
		if (p >= 0)
			{
			current_song_set(p, NULL);
			}
		else
			{
			if (status == STATUS_NEXT)
				{
				if (repeat_mode && current_songdata)
					{
					playback_start(current_songdata , 0);
					}
				else
					{
					status = STATUS_STOP;
					}
				}
			}
		}
	else
		{
		gint p = playlist_get_next(n);
		if (p >= 0)
			{
			current_song_set(p, NULL);
			}
		else if (repeat_mode)
			{
			if (shuffle_mode)
				{
				shuffle_list_create(FALSE);
				}
			current_song_set(playlist_get_first(), NULL);
			}
		else
			{
			if (status == STATUS_NEXT) status = STATUS_STOP;
			}
		}
}

void current_song_set_to_prev()
{
	gint p = playlist_get_prev(current_song_get_number());
	
	if (p >= 0) current_song_set(p, NULL);
}

void current_song_set_and_play(gint n, gchar *path)
{
	current_song_set(n, path);
	if (status == STATUS_STOP)
		{
		playback_start(current_songdata , 0);
		}
}

gchar *current_song_get_path()
{
	if (!current_songdata) return NULL;
	return current_songdata->path;
}

gchar *current_song_get_title()
{
	if (!current_songdata) return NULL;

	if (current_songdata->title)
		{
		return current_songdata->title;
		}

	return filename_from_path(current_songdata->path);
}

gchar *current_song_get_artist()
{
	if (!current_songdata) return NULL;
	return current_songdata->artist;
}

gchar *current_song_get_album()
{
	if (!current_songdata) return NULL;
	return current_songdata->album;
}

gchar *current_song_get_genre()
{
	if (!current_songdata) return NULL;
	return current_songdata->genre;
}

/*
 *-----------------------------------------------------------------------------
 * the main loop! called 8 times per second (125 ms)
 *-----------------------------------------------------------------------------
 */

static gint update_condition()
{
	static gint status_chk = -1;
	static gint update_counter = 0;
	static gint mixer_counter = 0;

	/* update mixer (volume/balance controls) every 4 seconds */
	mixer_counter++;
	if (mixer_counter > 32)
		{
		mixer_counter = 0;
		display_draw_volume(FALSE);
		display_draw_balance(TRUE);
		}

	if (main_window_widgets_update()) return TRUE;

	/* this is true once the song's header info is set by the player */
	if (new_song && (frames != 0 || seconds != 0))
		{
		display_draw_io_information(FALSE, FALSE);
		display_draw_song_text_info(-1, TRUE, FALSE);
		display_draw_title(current_song_get_title(), TRUE);
		new_song = FALSE;
		}

	update_counter++;

	if (status == STATUS_PLAY)
		{
		static int old_seconds;

		if (debug_mode) putchar('u');

		/* display */
		if (update_counter > 15)
			{
			update_counter = 0;
			display_draw_cpu(check_child_cpu_usage(pid), FALSE);
			}
		if (seconds != old_seconds && !pos_slider_in_drag)
			{
			display_draw_time(seconds, seconds_remaining, FALSE);
			display_draw_frame_count(frames, FALSE);
			display_draw_position(TRUE);
			}
		old_seconds = seconds;
		}
	if (status == status_chk)
		{
		if (debug_mode) putchar('c');
		return TRUE;
		}

	if (status == STATUS_NEXT)
		{
		current_song_set_to_next();
		}
	if (status == STATUS_PLAY)
		{
		display_draw_status(TRUE);
		}
	if (status == STATUS_STOP)
		{
		display_draw_cpu(0, FALSE);
		display_draw_time(0, 0, FALSE);
		display_draw_frame_count(0, FALSE);
		display_draw_io_information(TRUE, FALSE);
		display_draw_position(FALSE);
		display_draw_status(TRUE);
		}
	if (status == STATUS_PAUSE)
		{
		display_draw_cpu(0, FALSE);
		display_draw_status(TRUE);
		}
	status_chk = status;
	return 1;
}

/*
 *-----------------------------------------------------------------------------
 * ipc command line parsing
 *-----------------------------------------------------------------------------
 */

static gchar *prepend_path_if_match(gchar *text)
{
	gchar *buf;

	if (isfile(text))
		{
		return g_strdup(text);
		}
	buf = g_strconcat(current_path, "/", text, NULL);
	if (isfile(buf))
		{
		return buf;
		}
	g_free(buf);
	return g_strdup(text);
}

static gint send_if_ipc_match(gchar *short_str, gchar *long_str, gchar *command,
			      gint *i, int argc, char *argv[], gint extra, gint multiple)
{
	gchar *text = argv[*i];

	if (!strcmp(text, short_str) || (long_str && !strcmp(text, long_str)))
		{
		if (!ipc_another_process_active())
			{
			static tried_once = FALSE;
			gint c = 0;
			gint found = FALSE;
			gchar *exec_cmd;

			if (tried_once) return TRUE;
			tried_once = TRUE;
			printf("Running GQmpeg process not found, spawning one...\n");
			if (debug_mode)
				{
				exec_cmd = "gqmpeg -debug -blank &";
				}
			else
				{
				exec_cmd = "gqmpeg -blank &";
				}
			if (system(exec_cmd) == -1)
				{
				printf("execing gqmpeg failed\n");
				return TRUE;
				}
			while (c < 10 && !found)
				{
				sleep(1);
				found = ipc_another_process_active();
				c++;
				}
			if (!found)
				{
				printf("Timeout waiting for ipc of spawned gqmpeg process\n");
				return TRUE;
				}
			}
		if (extra)
			{
			if (*i < argc - 1)
				{
				gchar *path;
				gchar *buf;
				*i = *i + 1;
				path = prepend_path_if_match(argv[*i]);
				if (*i < argc - 1 && strncmp(argv[*i], "-", 1) != 0 && strcmp(command, "play_file") == 0)
					{
					/* add to playlist for multiple files with -f */
					buf = g_strconcat("pladd_play \"", path, "\"",NULL);
					}
				else
					{
					buf = g_strconcat(command, " \"", path, "\"",NULL);
					}
				g_free(path);
				ipc_send(buf);
				g_free(buf);
				if (multiple)
					{
					/* special case, only play first one for -f */
					if (strcmp(command, "play_file") == 0)
						{
						command = "pladd";
						}
					while (*i < argc - 1 && strncmp(argv[*i], "-", 1) != 0)
						{
						*i = *i + 1;
						path = prepend_path_if_match(argv[*i]);
						buf = g_strconcat(command, " \"", path, "\"",NULL);
						g_free(path);
						ipc_send(buf);
						g_free(buf);
						}
					}
				}
			else
				{
				printf("ipc %s command requires data\n", text);
				}
			}
		else
			{
			ipc_send(command);
			}
		return TRUE;
		}

	return FALSE;
}

static gint check_for_ipc_command(gint *i, int argc, char *argv[])
{
	if (send_if_ipc_match("-p", "--play", "play", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-s", "--stop", "stop", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-n", "--next", "next", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-b", "--back", "prev", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-ps", "--pause", "pause", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-pladd", "--playlist_add", "pladd", i, argc, argv, TRUE, TRUE)) return TRUE;
	if (send_if_ipc_match("-plrm", "--playlist_remove", "plrm", i, argc, argv, TRUE, TRUE)) return TRUE;
	if (send_if_ipc_match("-plclr", "--playlist_clear", "plclr", i, argc, argv, FALSE, FALSE)) return TRUE;
	if (send_if_ipc_match("-pladdplay", "--playlist_add_play", "pladd_play", i, argc, argv, TRUE, FALSE)) return TRUE;
	if (send_if_ipc_match("-pl", "--playlist", "plload", i, argc, argv, TRUE, FALSE)) return TRUE;
	if (send_if_ipc_match("-plappend", "--playlist_append", "plappend", i, argc, argv, TRUE, TRUE)) return TRUE;
	if (send_if_ipc_match("-f", "--file", "play_file", i, argc, argv, TRUE, TRUE)) return TRUE;

	return FALSE;
}

/*
 *-----------------------------------------------------------------------------
 * misc start-up utils
 *-----------------------------------------------------------------------------
 */

static void check_for_home_path(gchar *path)
{
	gchar *buf;
	buf = g_strconcat(homedir(), "/", path, NULL);
	if (!isdir(buf))
		{
		printf("Creating GQmpeg dir:%s\n", buf);
		if (mkdir (buf, 0755) < 0)
			{
			printf("Could not create dir:%s\n", buf);
			}
		}
	g_free(buf);
}

static gchar *determine_skin_path(gchar *file)
{
	gchar *path = NULL;
	gchar *buf;

	if (isdir(file))
		{
		path = g_strdup(file);
		}
	else
		{
		buf = g_strconcat(current_path, "/", file, NULL);
		if (isdir(buf))
			{
			path = buf;
			}
		else
			{
			g_free(buf);
			buf = g_strconcat(homedir(), "/", 
					GQMPEG_RC_DIR_SKIN, "/", file, NULL);
			if (isdir(buf))
				{
				path = buf;
				}
			else
				{
				g_free(buf);
				buf = g_strconcat(GQMPEG_GLOBAL_SKIN_DIR, file, NULL);
				if (isdir(buf))
					{
					path = buf;
					}
				else
					{
					g_free(buf);
					}
				}
			}
		}

	if (path)
		{
		printf ("Using skin: %s\n", path);
		}
	else
		{
		printf ("Skin not found: %s\n", file);
		}

	return path;
}

/*
 *-----------------------------------------------------------------------------
 * help function
 *-----------------------------------------------------------------------------
 */

static void show_help()
{
	printf("GQmpeg version %d.%d.%d\n", GQMPEG_VERSION_MAJOR, GQMPEG_VERSION_MINOR, GQMPEG_VERSION_REV);
	printf("usage: gqmpeg [option(s)] [file(s) | ipc_command(s)]\n");
	printf(" standard options:\n");
	printf("  -skin:<skindir>              load specified skin\n");
	printf("  -dock                        enable docking behavior\n");
	printf("  -dockwm                      enable Window Maker docking\n");
	printf("  -skinhelp                    print mouse event coordinates\n");
	printf("  -debug                       enable debug output\n");
	printf("  -help                        displays this message\n");
	printf("  -blank                       start with empty playlist\n");
	printf(" available ipc_commands:\n");
	printf("  -p          --play\n");
	printf("  -s          --stop\n");
	printf("  -n          --next\n");
	printf("  -b          --back\n");
	printf("  -ps         --pause\n");
	printf("  -pladd      --playlist_add <f>[..]    add file to playlist\n");
	printf("  -pladdplay  --playlist_add_play <f>   add file to playlist and play it\n");
	printf("  -plrm       --playlist_remove <f>[..] remove file from playlist\n");
	printf("  -plclr      --playlist_clear <f>      clear playlist\n");
	printf("  -pl         --playlist <f>            load playlist\n");
	printf("  -plappend   --playlist_append <f>[..] append playlist\n");
	printf("  -f          --file <f>[..]            play file, do not add to playlist\n");
	printf("                                        or, if playlist, load playlist\n");
	printf("              * files can also be of the format http://server:port\n");
}

/*
 *-----------------------------------------------------------------------------
 * exit function
 *-----------------------------------------------------------------------------
 */

void gqmpeg_exit()
{
	gchar *session_file;

	playlist_window_hide();	/* to save the current pos/size */

	playback_stop(current_song_get_data());

	if (!dock_mode)
		{
		gint x = 0;
		gint y = 0;
		gdk_window_get_position (mainwindow->window, &x, &y);
		window_pos_x = x;
		window_pos_y = y;
		}

	if (skin_small && skin == skin_small)
		{
		alternate_skin = TRUE;
		}
	else
		{
		alternate_skin = FALSE;
		}

	initial_playlist_position = current_song_get_number();

	save_options();

	/* save playlist between sessions (even if option not enabled) */
	session_file = g_strconcat(homedir(), "/.gqmpeg/session-playlist.gqmpeg", NULL);
	playlist_save(session_file);
	g_free(session_file);

	/* remove the ~/.gqmpeg/command file, so that existance of this file
	   usually indicates gqmpeg is already running */
	ipc_off();

	gtk_main_quit();
}

/*
 *-----------------------------------------------------------------------------
 * main routine
 *-----------------------------------------------------------------------------
 */

int main (int argc, char *argv[])
{
	gchar *buf;
	gchar *skin_file = NULL;
	gchar *default_current_path = NULL;
	gint i;
	gint blank_startup = FALSE;
	gint dockwm_startup = FALSE;
	GList *list_buf = NULL;
	GList *playlist_buf = NULL;

	init_vars();

	/* setup random seed for shuffle mode */
	srand (time (0));

	/* check that we can find a valid home directory, if not exit semi-gracefully */
	buf = homedir();
	if (!buf)
		{
		printf("Unable to determine the home directory\n");
		return 0;
		}
	else if (!isdir(buf))
		{
		printf("Could not find home directory: %s\n", buf);
		return 0;
		}

	/* create gqmpeg dirs */
	check_for_home_path(GQMPEG_RC_DIR);
	check_for_home_path(GQMPEG_RC_DIR_SKIN);
	check_for_home_path(GQMPEG_RC_DIR_PLAYLIST);

	playlist_clear();

	if (argc > 1)
		{
		gint have_ipc_command = FALSE;
		i = 1;
		while (i < argc)
			{
			gchar *command_line_path = g_strconcat(current_path, "/", argv[i], NULL);
			if (!strcmp(argv[i],"-debug"))
				{
				debug_mode = TRUE;
				printf("debugging enabled\n");
				}
			else if ((isdir(command_line_path) || isdir(argv[i])) && !default_current_path)
				{
				if (isdir(argv[i]))
					default_current_path = g_strdup(argv[i]);
				else
					default_current_path = g_strdup(command_line_path);
				}
			else if (strncmp(argv[i], "-", 1) != 0)
				{
				if (isfile(argv[i]) || !isfile(command_line_path))
					{
					g_free(command_line_path);
					command_line_path = g_strdup(argv[i]);
					}
				if (is_playlist(command_line_path))
					{
					playlist_buf = g_list_prepend(playlist_buf, g_strdup(command_line_path));
					}
				else
					{
					list_buf = g_list_prepend(list_buf, g_strdup(command_line_path));
					}
				}
			else if (!strcmp(argv[i],"-blank"))
				{
				blank_startup = TRUE;
				i = argc;
				}
			else if (check_for_ipc_command(&i, argc, argv))
				{
				have_ipc_command = TRUE;
				}
			else if (!strcmp(argv[i],"-skinhelp"))
				{
				debug_skin = TRUE;
				printf("skin debugging enabled\n");
				}
			else if (!strcmp(argv[i],"-dock"))
				{
				dock_mode = TRUE;
				}
			else if (!strcmp(argv[i],"-dockwm"))
				{
				dock_mode = TRUE;
				dockwm_startup = TRUE;
				}
			else if (!strncmp(argv[i],"-skin:", 6))
				{
				if (skin_file)
					{
					printf("Ignoring multiple skins!\n");
					}
				else
					{
					skin_file = determine_skin_path(argv[i] + 6);
					}
				}
			else if (!strcmp(argv[i],"-help"))
				{
				show_help();
				return 0;
				}
			else
				{
				printf("unknown option: %s\nuse -help for options\n",argv[i]);
				}
			g_free(command_line_path);
			i++;
			}
		if (have_ipc_command)
			{
			return 0;
			}
		}

	if (playlist_buf) playlist_buf = g_list_reverse(playlist_buf);
	if (list_buf) list_buf = g_list_reverse(list_buf);

/* */
	/*
	 * the gtk inits are moved after the command line parser
	 * (for speed with ipc commands)
	 */
	gtk_init (&argc, &argv);
	gdk_imlib_init();

	/* push the correct color depths to gtk, (for 8-bit psuedo color displays)
	 * they should be popped, too, I guess...
	 */
	gtk_widget_push_visual(gdk_imlib_get_visual());
	gtk_widget_push_colormap(gdk_imlib_get_colormap());

	buf = g_strconcat(homedir(), "/.gqmpeg/gtkrc", NULL);
	if (isfile(buf)) gtk_rc_parse(buf);
	g_free(buf);

	/* call this before loading options, so that modules load options too */
	player_modules_init();

	load_options();

	/* now load/append playlists */

	if (playlist_buf)
		{
		GList *work = playlist_buf;
		while(work)
			{
			if (!work->prev)
				{
				printf("Loading playlist:%s\n", (gchar *)work->data);
				playlist_load(work->data, FALSE, TRUE);
				}
			else
				{
				printf("Appending playlist:%s\n", (gchar *)work->data);
				playlist_load(work->data, TRUE, TRUE);
				}
			g_free(work->data);
			work = work->next;
			}
		g_list_free(playlist_buf);
		}
	if (list_buf)
		{
		GList *work = list_buf;
		while(work)
			{
			if (typelist_determine_type_id(work->data) != -1 || isfile(work->data))
				{
				printf("Adding song:%s\n", (gchar *)work->data);
				playlist_add(work->data);
				if (current_song_get_number() == -1)
					{
					current_song_set(playlist_get_count() - 1, NULL);
					}
				}
			else
				{
				printf("Unable to determine type:%s\n", (gchar *)work->data);
				}
			g_free(work->data);
			work = work->next;
			}
		g_list_free(list_buf);
		}

	if (initial_playlist_mode == PLAYLIST_MODE_EMPTY)
		{
		}
	if (initial_playlist_mode == PLAYLIST_MODE_SESSION && playlist_get_count() == 0 && !blank_startup)
		{
		gchar *session_file;
		session_file = g_strconcat(homedir(), "/",
			GQMPEG_RC_FILE_SESSION, NULL);
		if (isfile(session_file))
			{
			if (playlist_load(session_file, FALSE, TRUE) && start_playing_on_startup)
				{
				if (initial_playlist_position > -1 && initial_playlist_position < playlist_get_count())
					{
					current_song_set(initial_playlist_position, NULL);
					}
				}
			}
		else
			{
			printf("Could not load session-playlist file\n");
			}
		g_free(session_file);
		g_free(playlist_pathname);
		playlist_pathname = g_strconcat(homedir(), "/", GQMPEG_RC_DIR_PLAYLIST, "/Untitled.gqmpeg", NULL);
		g_free(playlist_filename);
		playlist_filename = g_strdup("Untitled.gqmpeg");
		}
	if (initial_playlist_mode == PLAYLIST_MODE_FILE && playlist_get_count() == 0 && !blank_startup)
		{
		playlist_load(initial_playlist_pathname, FALSE, TRUE);
		}

	if (default_current_path)
		{
		change_to_path(default_current_path);
		g_free(default_current_path);
		}
	else if (initial_directory_enable)
		{
		if (isdir(initial_directory_path))
			{
			change_to_path(initial_directory_path);
			}
		else
			{
			printf("config file contains invalid startup path:\n%s\n", initial_directory_path);
			filelist_refresh();
			}
		}
	else
		{
		filelist_refresh();
		}

	/* set up mixer */

	if (!mixer_command) mixer_command = g_strdup("xmixer");
	mixer_init(mixer_device_id);

	/* set up main window */
	create_main_window(skin_file);
	g_free(skin_file);

	if (dockwm_startup) winmaker_setup_dock(argv);

	gtk_widget_show(mainwindow);

        if (remember_window_position && !dock_mode)
                {
                /* ensure that window is at least visible */
                if (window_pos_x < 0 - skin->width)
                        window_pos_x = 0;
                if (window_pos_x > gdk_screen_width())
                        window_pos_x = gdk_screen_width() - skin->width;
                if (window_pos_y < 0 - skin->height)
                        window_pos_y = 0;
                if (window_pos_y > gdk_screen_height())
                        window_pos_y = gdk_screen_height() - skin->height;
                gdk_window_move(mainwindow->window, window_pos_x, window_pos_y);
                }

	dnd_init();

	if (show_playlist_on_startup && !blank_startup) playlist_window_show();

	update_timeout_id = gtk_timeout_add(125, (GtkFunction)update_condition, NULL);

	if (start_playing_on_startup && !blank_startup) btn_play_pressed();

	if (allow_ipc) ipc_on();

	gtk_main ();
	return 0;
}

