/*
    vidmode_parse.cc - vidmode parsing functions
    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 "vidmode.h"

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


string VidMode::str() const {
	ostringstream oss;
	oss << width << 'x' << height << '@' << fixed << setprecision(0) << refresh;
	return oss.str();
}

string VidMode::pretty_str() const {
	ostringstream oss;
	oss << setw(4) << width << 'x' << left << setw(4) << height << ' ' << fixed << setprecision(0) << refresh << "Hz";
	if (!info.empty())
		oss << " (" << info << ')';
	return oss.str();
}


int parse_mode(int &w, int &h, int &r, const string &arg) {
	r=-1;
	if (arg == "max")
		w = h = INT_MAX;
	else if (arg == "best")
		w = h = -2;
	else if (arg == "all")
		w = h = -1;
	else {	
		int x = arg.find('x');
		int a = arg.find('@');
		int hlen = ((a>=0)?a:arg.size())-x-1;
		if (x<0) return 1;
		if (parse_int(w,arg.substr(0,x)) || parse_int(h,arg.substr(x+1,hlen))) return 1;
		if (a>=0 && parse_int(r, arg.substr(a+1))) return 1;
		if (w<=0 || h<=0) return 1;
	}
	return 0;
}

int parse_arg(screen_list_t &screens, int &w, int &h, int &r, const string &arg, int num_screens, int cur_screen) {
	int c = arg.find(':');
	if (c>=0) {
		if (parse_screen(screens, arg.substr(0, c), num_screens, cur_screen)) return 1;
		if (parse_mode(w, h, r, arg.substr(c+1))) return 1;
	}
	else {
		screens.push_back(cur_screen);
		if (parse_mode(w, h, r, arg)) return 1;
	}
	return 0;	
}

int find_mode(mode_list_t &modes, int screen, int w, int h, int r, const screen_mode_list_t &available_modes) {
	assert(screen<(signed)available_modes.size());
	const mode_list_t &smodes = available_modes[screen];
	int errs=0;

	if (w==INT_MAX && h==INT_MAX) {
		unsigned long maxpixels = 0;
		int maxrefresh = 0;
		mode_list_t::const_iterator max_mode=smodes.end();
		for (mode_list_t::const_iterator modeitr=smodes.begin(); modeitr!=smodes.end(); ++modeitr) {
			const VidMode &mode = **modeitr;
			unsigned long pixelcount = mode.width * mode.height;
			if (pixelcount > maxpixels || 
					(pixelcount==maxpixels && mode.refresh>maxrefresh)) {
				maxpixels = pixelcount;
				maxrefresh = mode.refresh;
				max_mode = modeitr;
			}
		}
		modes.push_back(*max_mode);
	}
	else if (w==-2 && h==-2) {
		for (mode_list_t::const_iterator i=smodes.begin(); i!=smodes.end(); ++i) {
			const VidMode &imode = **i;
			bool best=true;
			for (mode_list_t::const_iterator j=smodes.begin(); j!=smodes.end(); ++j) {
				const VidMode &jmode = **j;
				if (j!=i && jmode.width==imode.width && jmode.height==imode.height && jmode.refresh>imode.refresh) {
					best=false;
					break;
				}
			}
			if (best)
				modes.push_back(*i);
		}
				
	}
	else if (w==-1 && h==-1) {
		modes.insert(modes.end(), smodes.begin(), smodes.end());
	}
	else {
		mode_list_t::const_iterator modeitr;
		VidMode *matchmode=NULL;
		for (modeitr=smodes.begin(); modeitr!=smodes.end(); ++modeitr) {
			VidMode *mode = *modeitr;
			if (mode->width==w && mode->height==h) {
				if (r<0) { // If no refresh specified, choose the best one.
					if (!matchmode || mode->refresh>matchmode->refresh)
						matchmode=mode;
				}
				else if (mode->refresh == r) {
						matchmode=mode;
						break;
				}
			}
		}
		if (matchmode)
			modes.push_back(matchmode);
		else
			errs++;
	}
	return errs;
}

int parse_arg_mode(screen_mode_list_t &arg_modes, const char *arg, const screen_mode_list_t &available_modes, int cur_screen) {
	screen_list_t screens;
	int w, h, r, errs = 0;

	if (parse_arg(screens, w, h, r, arg, available_modes.size(), cur_screen)) {
		fprintf(stderr, "invalid mode \"%s\"\n",arg);
		errs++;
	}
	else {
		for (screen_list_t::const_iterator screenitr=screens.begin(); screenitr!=screens.end(); ++screenitr) {
			int screen = *screenitr;
			if (find_mode(arg_modes[screen], screen, w, h, r, available_modes)) {
				fprintf(stderr, "screen %i mode %ix%i", screen, w, h);
				if (r>=0) fprintf(stderr, "@%i", r);
				fprintf(stderr, " not found\n");
				errs++;
			}
		}
	}
	return errs;
}

int parse_arg_modes(screen_mode_list_t &arg_modes, char const*const*arg0, char const*const*argend, const screen_mode_list_t &available_modes, int cur_screen) {
	int errs = 0;
	for (; arg0<argend; ++arg0) {
		errs += parse_arg_mode(arg_modes, *arg0, available_modes, cur_screen);
	}
	return errs;
}
