/*
    vidmode.cc - abstract handling of vidmode changing for gvidm
    Copyright (C) 2001-2004 Matthew Mueller <donut AT dakotacom DOT net>

    This program 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.

    This program 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "gvid.h"
#include "vidmode.h"

#include "_ios.h"
#include "_sstream.h"
#include <iomanip>

int round(float f) {
	//This is a cheesy yet effective workaround for the fact that
	//round/nearbyint/etc are all C99.  (And rint which is bsd.)
	//On the plus side, it guarantees that no matter which rounding method
	//iostreams use, user input will always match up to the output of --list.
	ostringstream oss;
	oss << fixed << setprecision(0) << f;
	return atoi(oss.str().c_str());
}


inline string menuline(int i, const VidMode &m)
{
	ostringstream oss;
	oss << (m.iscurmode?'*':' ');
	if (i < 36) oss << " _" << num2char36(i) << ". ";
	else oss << "    ";
	oss << m.pretty_str();
	return oss.str();
}

void fill_res_menu(GtkWidget *menu_ptr, int screen, const mode_list_t &modes) {
	int i=0;

	for (mode_list_t::const_iterator modeitr=modes.begin(); modeitr!=modes.end(); ++modeitr, ++i)
	{
		const VidMode &mode = **modeitr;
		string s = menuline(i, mode);

		add_menuitem_with_accel(s.c_str(), mode.data, menu_ptr);
	}
}

void create_popup(const screen_mode_list_t &screen_modes)
{
	int i, screen_count, mode_number, num_screens_in_menu=0;
	char label_str[80];
	GtkWidget *menuitem_ptr, *submenu_ptr, *menu_ptr;

	mode_number=0;
	screen_count = get_screen_count();

	menu_ptr = create_root_menu();

	for (i=0; i<screen_count; i++)
		if (!screen_modes[i].empty())
			num_screens_in_menu++;

	assert(num_screens_in_menu>0);
	if (num_screens_in_menu==1)
	{
		for (i=0; screen_modes[i].empty(); i++) { /*pass*/ }
		fill_res_menu(menu_ptr, i, screen_modes[i]);
	}
	else
	{
		for (i=0; i<screen_count; i++)
		{
			if (screen_modes[i].empty())
				continue;
			
			submenu_ptr = gtk_menu_new();
			sprintf(label_str, "Screen %s%i", i<10?"_":"", i);
			menuitem_ptr = create_menuitem_with_accel(label_str, menu_ptr);
			gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem_ptr), submenu_ptr);
			gtk_menu_append(GTK_MENU (menu_ptr), menuitem_ptr);
			gtk_widget_show(menuitem_ptr);
			gtk_widget_show(submenu_ptr);

			fill_res_menu(submenu_ptr, i, screen_modes[i]);
		}
	}

	gtk_menu_popup(GTK_MENU(menu_ptr), NULL, NULL, NULL, NULL, 0, 0);
}


int AbstractVidModeHandler::set(const char *const* argbegin, const char *const* argend) {
	int errs=0;

	screen_mode_list_t arg_modes(available_modes.size());
	if (argbegin != argend) {
		int want_menu = 0;
		errs += parse_arg_modes(arg_modes, argbegin, argend, available_modes, cur_screen);
		for (unsigned screen = 0; screen<arg_modes.size(); ++screen) {
			if (arg_modes[screen].empty())
				continue;
			else if (arg_modes[screen].size()==1) {
				doevent(arg_modes[screen].front()->data);
				arg_modes[screen].clear(); // clear it so it won't display in the popup
			}
			else
				want_menu = 1;
		}
		if (!want_menu)
			return errs;
		create_popup(arg_modes);
	}
	else {
		parse_arg_mode(arg_modes, "all:all", available_modes, cur_screen);
		create_popup(arg_modes);
	}
	gtk_main ();
	return errs;
}


int AbstractVidModeHandler::list(const screen_list_t &screens, bool onlycur) const {
	int num_screens=available_modes.size();
	for (screen_list_t::const_iterator screenitr=screens.begin(); screenitr!=screens.end(); ++screenitr) {
		int screen = *screenitr;
		assert(screen<num_screens);
		const mode_list_t &smodes = available_modes[screen];
		for (mode_list_t::const_iterator modeitr=smodes.begin(); modeitr!=smodes.end(); ++modeitr) {
			const VidMode &mode = **modeitr;
			if (onlycur && !mode.iscurmode)
				continue;
			if (num_screens > 1) printf("%i:", screen);
			printf("%s%s\n", mode.str().c_str(),
					(mode.iscurmode && !onlycur)?" *":"");
		}
		
	}
	return 0;
}

int AbstractVidModeHandler::list(const char *const* argbegin, const char *const* argend) const {
	screen_list_t screens;
	int errs = parse_arg_screens(screens, argbegin, argend, "all", available_modes.size(), cur_screen);
	errs += list(screens, false);
	return errs;
}

int AbstractVidModeHandler::list_current(const char *const* argbegin, const char *const* argend) const {
	screen_list_t screens;
	int errs = parse_arg_screens(screens, argbegin, argend, "all", available_modes.size(), cur_screen);
	errs += list(screens, true);
	return errs;
}


AbstractVidModeHandler::AbstractVidModeHandler() {
	cur_screen = get_current_screen();
}

AbstractVidModeHandler::~AbstractVidModeHandler() {
	for (screen_mode_list_t::iterator smli=available_modes.begin(); smli!=available_modes.end(); ++smli) {
		for (mode_list_t::iterator mi=smli->begin(); mi!=smli->end(); ++mi) {
			delete *mi;
		}
	}
}
