#include "xf86config.h"
#include "buildmenu.h"
#include "util.h"

/*
 * Monitor configuration.
 */

static char *monitorintro_text =
"Now we want to set the specifications of the monitor. The two critical\n"
"parameters are the vertical refresh rate, which is the rate at which the\n"
"the whole screen is refreshed, and most importantly the horizontal sync rate,\n"
"which is the rate at which scanlines are displayed.\n"
"\n"
"The valid range for horizontal sync and vertical sync should be documented\n"
"in the manual of your monitor.\n"
"\n";


static ListItem vsync_menu[] = {
    { "50-70",	"50 - 70 Hz", },
    { "50-90",	"50 - 90 Hz", },
    { "50-100",	"50 - 100 Hz", },
    { "40-150",	"40 - 150 Hz", },
    { "CUSTOM",	"None of the above", },
};

#define NRVSYNC	(sizeof vsync_menu/sizeof vsync_menu[0])

#define NU_MONITORTYPES 10

static ListItem monitortype[] = {
    { "31.5",		"Standard VGA, 640x480 @ 60 Hz", },
    { "31.5-35.1",	"Super VGA, 800x600 @ 56 Hz", },
    { "31.5,35.5",	"8514 Compatible, 1024x768 @ 87 Hz interlaced (no 800x600)", },
    { "31.5,35.15,35.5", "Super VGA, 1024x768 @ 87 Hz interlaced, 800x600 @ 56 Hz", },
    { "31.5-37.9",	"Extended Super VGA, 800x600 @ 60 Hz, 640x480 @ 72 Hz", },
    { "31.5-48.5",	"Non-Interlaced SVGA, 1024x768 @ 60 Hz, 800x600 @ 72 Hz", },
    { "31.5-57.0",	"High Frequency SVGA, 1024x768 @ 70 Hz", },
    { "31.5-64.3",	"Monitor that can do 1280x1024 @ 60 Hz", },
    { "31.5-79.0",	"Monitor that can do 1280x1024 @ 74 Hz", },
    { "31.5-82.0",	"Monitor that can do 1280x1024 @ 76 Hz", },
    { "CUSTOM",		"None of the above", },
};
#define NRMONITOR	(sizeof monitortype / sizeof monitortype[0])

static LIA monitormenu;

/*
 * crt_callback() sets all of the appropriate keys when a monitor
 * is chosen
 */
static int
crt_callback(void* obj, void* w)
{
    int k;
    Display *d;

    if (d = (Display*)getUserData(obj)) {
	getIndexedMenuSelection(obj, &d->key0, &d->key1);

	if (LIAlist(monitormenu)[d->key0].id == 0)
	    k = d->key0;
	else
	    sscanf(LIAlist(LIAlist(monitormenu)[d->key0].id)[d->key1].id,
		   "%d", &k);

	d->is(k);
    }
    return -1;
} /* crt_callback */


int
Display::select_from_menu()
{
    int rc;
    ndObject menu;
    ndObject chain;

    while (1) {
	menu = newIndexedMenu(0, 1,
			      65, 12, -1,
			      monitormenu,
			      0, crt_callback, 0/*help*/),
	setUserData(menu, this);

	setIndexedMenuSelection(menu, key0, key1);

	chain = ObjChain(menu, newCancelButton(0, "BACK", 0, 0));

	rc = MENU(chain, -1, -1, "Configure the monitor",
    "Please choose your monitor.  If you don't see it here, choose\n"
    "``Customize'' to manually configure it or ``VESA'' to choose\n"
    "some common video modes\n", 0);

	deleteObjChain(chain);

	if (rc != MENU_OK || rc < 0)
	    return 0;

	if (entry().custom())
	    return set_hsync();
	else if (configured())
	    return 1;

    } /* CHOOSE A MONITOR FROM DATABASE LOOP */
} /* select_from_menu */


int
Display::set_identity()
{
    int rc;
    void* chain;
    char product[80];

    if (entry().product()) strcpy(product, entry().product());

    chain = ObjChain(
	newString(0,1,28,28,product,0,"Monitor model |", 0, 0),
	newOKButton(0, "NEXT", 0, 0));
    chain = ObjChain(chain,
	newCancelButton(1, "BACK", 0, 0));

    rc = MENU(chain, -1, -1, TITLE,
	"Please give a name describing this monitor (usually the\n"
	"model name, but you may use any descriptive label.)", 0);


    deleteObjChain(chain);
    if (rc == MENU_OK) {
	entry().product(product[0] ? product : "Custom");
	done();
	return 1;
    }
    return 0;
} /* set_identity() */


int
Display::set_vsync()
{
    int rc;

    while (1) {
	rc = Xmenu(TITLE,
    "You must indicate the vertical sync range of your monitor. You can\n"
    "either select one of the predefined ranges below that correspond to\n"
    "industry-standard monitor types, or give a specific range. For\n"
    "interlaced modes, the number that counts is the high one (e.g. 87 Hz\n"
    "rather than 43 Hz).", 8,  vsync_menu, NRVSYNC, 0, 0);

	if (rc < 0)
	    return 0;

	if (rc == NRVSYNC-1) {
	    /* CUSTOM */
	    static char custom_vsync[80];

	    rc = dialog_inputbox(TITLE,
				 "Vertical sync range", -1, 40, custom_vsync);
	    if (rc != 0)
		continue;
	    monitor[0].vsync(custom_vsync);
	}
	else
	    monitor[0].vsync(vsync_menu[rc].id);

	if (set_identity())
	    return 1;
    } /* while loop around `indicate vsync' */
} /* set_vsync */


int
Display::set_hsync()
{
    int rc;
    static char custom_hsync[80];

    while (1) {
	rc = Xmenu(TITLE,
    "You must indicate the horizontal sync range of your monitor. You can\n"
    "either select one of the predefined ranges below that correspond to\n"
    "industry- standard monitor types, or give a specific range.\n"
    "\n"
    "It is VERY IMPORTANT that you do not specify a monitor type with a\n"
    "horizontal sync range that is beyond the capabilities of your monitor.\n"
    "If in doubt, choose a conservative setting.",
		8, monitortype, NRMONITOR, 0, 0);

	if (rc < 0)
	    return 0;

	if (rc == NRMONITOR-1) {
	    /* CUSTOM MONITOR */

	    rc = dialog_inputbox(TITLE,
    "Please enter the horizontal sync range of your monitor. You can either\n"
    "specify one or more continuous ranges (e.g. 15-25, 30-50), or one or\n"
    "more fixed sync frequencies.", -1, 60, custom_hsync);

	    if (rc != 0)
		continue;
	    monitor[0].hsync(custom_hsync);
	}
	else
	    monitor[0].hsync(monitortype[rc].id);
	if (set_vsync())
	    return 1;
    }
}


/*
 * autodetect() asks the monitor about what video modes it supports
 */
#define PROBE	"/usr/lib/admin/get-edid 2>/dev/null | /usr/lib/admin/parse-edid 2>/dev/null"
int
Display::autodetect()
{
    FILE *f;
    char line[2048];
    char buf[80];
    char *p;
    int x;
    int maxrez, k;
    Monitor found;
    Monitor *mon = 0;
    ModeLine *m = 0;

    if ( (f = popen(PROBE, "r")) == 0)
	return 0;
    if (found.scan(f) && found.hsync() && found.vsync() && found.nrmodes()) {
	memmove(&monitor[1], &monitor[0], nr_monitor * sizeof monitor[0]);
	nr_monitor ++;

	mon = &monitor[0];
	memset(mon, 0, sizeof monitor[0]);

	mon->is(found);
	mon->autoconfigured(1);

	if (strieq(mon->vendor(), "Unknown"))
	    mon->vendor("(Autodetected)");
	else {
	    sprintf(line, "%s (Autodetected)", mon->vendor());
	    mon->vendor(line);
	}
	if (strieq(mon->product(), "Unknown")) {
	    m = mon->modes();
	    for (k=maxrez=x=0; x < mon->nrmodes(); x++) {
		if (m[x].hsize * m[x].vsize > maxrez) {
		    maxrez = m[x].hsize * m[x].vsize;
		    k = x;
		}
	    }
	    sprintf(line, "%dx%d resolution", m[k].hsize, m[k].vsize);
	    mon->product(line);
	}
	return 1;
    }
    return 0;
}

/* 
 * monitordb_configuration() gives the initial "pick and choose your
 * monitor!" blurb, then drops into a monitor menu screen.
 */
int
Display::configure(int stage2)
{
    int rc, wid;
    char message[1024];
    Monitor *p;

    static int detected = 0;


    /* try to autodetect the monitor
     */
    if (!detected) {
	detected = 1;

	if ( getuid() == 0 ) {

	    if (stage2) {
		dialog_msgbox(TITLE, "Detecting monitor video modes.\n"
				     "          Please wait", -1, -1, 0);
		rc = 0;
	    }
	    else {
		rc = dialog_yesno(TITLE,
		    "Would you like to have me attempt to\n"
		    "automatically detect your monitor?", -1, -1);
		if (rc < 0)
		    return 0;
	    }

	    if ( (rc == 0) && autodetect() ) {

		p = &monitor[0];
		wid = (strlen(p->product()) + strlen(p->vendor())) / 2;

		sprintf(message,"%*sVendor: %s\n"
				"%*sModel : %s\n"
				"The following graphics modes were\n"
				"detected:\n",
				(22 - wid)/2, "", p->vendor(),
				(22 - wid)/2, "", p->product());

		for (int i=0; i < p->nrmodes(); i++) {
		    sprintf(END(message),
			      "%9s%4dx%-4d\n", "",
				  p->modes()[i].hsize,
				  p->modes()[i].vsize);
		}

		if (dialog_yesno("Use this monitor definition?",
					    message, -1, -1) == 0) {
		    is(0);
		    return 1;
		}
	    }
	}
    }

    if (monitormenu == 0 && (nr_monitor > 0))
	monitormenu = Imenu<Monitor>::buildmenu(monitor, nr_monitor);

    if (LIAcount(monitormenu) == 0)
	return set_hsync();

    return select_from_menu();
} /* configure */
