/**
 * File:	include/sound/joy_dialog.ycp
 * Package:	Joystick configuration
 * Summary:	Joystick dialogs
 * Authors:	Dan Meszaros <dmeszar@suse.cz>
 *		Ladislav Slezak <lslezak@suse.cz>
 *		Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: joy_dialog.ycp 18261 2004-07-28 11:02:37Z lslezak $
 */

{

    textdomain "sound";

    import "Wizard";
    import "Joystick";
    import "Sound";
    import "Joystick";
    import "Require";
    import "Label";
    import "Popup";

    include "sound/joysticks.ycp";
    include "sound/ui.ycp";
    include "sound/volume_routines.ycp";

    list joylist = [];

/**
 * Update the sound card configuration of joystick
 * @param start	should joystick be used?
 * @return was sound config changed?
 */
define boolean update_sound_card_joy_config (integer card_id, boolean start) ``{

    map sound_options	= Sound::modules_conf[card_id, "options"]:$[];
    string modname	= Sound::modules_conf[card_id, "module"]:"";
    if (!haskey (Sound::joystick_configuration, modname))
    {
	return false;
    }
    map<string,string> joy_option = Sound::joystick_configuration[modname]:$[];
    string opname	= "";
    string value	= "0";
    // there should be only one entry
    foreach (string name, string val, joy_option, ``{
	opname		= name;
	value		= val;
    });
    string default_value	=
	Sound::db_modules [modname, "params", opname, "default"]:"0";

    if ((start && sound_options[opname]:default_value == default_value) ||
	(!start && sound_options[opname]:default_value != default_value))
    {
	sound_options[opname]	= start ? value : default_value;
	Sound::modules_conf[card_id, "options"]	= sound_options;
	return true;
    }
    return false;
}

/**
 * Dialog for joystick testing.
 * @param card_id Sound card card_id
 * @param joymodidx model id
 * @param joymod joystick module
 * @return boolean false when Abort was pressed or some problems occured
 */
define boolean joy_test_popup (integer card_id, integer joymodidx,
				      string joymod) ``{

    any ret = nil;
    UI::OpenDialog(`opt(`decorated), `HBox(`HSpacing(1.5),
	`VBox(
	    `VSpacing(1),
	    // Wait text label
	    `Label(_("Initializing joystick -- please wait...")),
	    `VSpacing(1),
	    `PushButton(`id(`done), `opt(`default), Label::AbortButton()),
	    `VSpacing(1)
	),
	`HSpacing(1.5)
    ));

    // store old joystick configuration:
    list current_conf = (list) eval (Joystick::joystick);

    // if new joystick type was selected:
    boolean testing_new = false;

    string modname	= Sound::modules_conf [card_id, "module"]:"";
    map joy_entry	= Sound::GetJoystickSettings(modname);
    string model	= joylist [joymodidx, 1]:"";

    // do not translate model in /etc/sysconfig/joystick
    if (model == Joystick::generic_joystick_translated)
    {
	model = Joystick::generic_joystick;
    }
    joy_entry = add(joy_entry, "JOYSTICK_MODULE", joymod);
    joy_entry = add(joy_entry, "model", model);

    if (model != current_conf [card_id, "model"]:"")
	testing_new = true;

    Joystick::joystick[card_id] = joy_entry;
    y2milestone("Tested joystick configuration: %1", joy_entry);

    // unload old and load new modules if necessary:
    if (testing_new)
    {
        Joystick::Stop();
        Joystick::SaveOneJoystick(card_id);
        Joystick::WriteConfig();
        Joystick::StartAndEnable();
    }

    // sound card configuration must be changed...
    if (update_sound_card_joy_config (card_id, true))
    {
	// popup text (yes/no)
	if (Popup::YesNo (_("For proper joystick initialization,
a restart of the sound system is necessary.
Proceed?")))
	{
	    sound_stop ();
	    sound_start_tmp(true);
	}
    }

    integer js_id	= card_id; // default number of device
    list joysticks	= [];
    integer i		= 0;

    sleep (100);
    while (i < 4)
    {
        if (SCR::Execute(.target.bash,
	    sformat("echo 2>/dev/null < /dev/input/js%1",i)) == 0)
        {
            joysticks = add (joysticks, i);
            y2milestone("existing device: /dev/input/js%1", i);
        }
        i = i + 1;
    }
    i = 0;

    if (size (joysticks) == 0)
    {
	// popup message
        Popup::Message(_("No joystick is connected."));
        if (testing_new)
        {
            Joystick::joystick = current_conf;
            Joystick::Stop();
            Joystick::SaveOneJoystick(card_id);
            Joystick::WriteConfig();
            Joystick::StartAndEnable();
        }
        UI::CloseDialog();
        return false;
    }

    if (size (joysticks) > 1)
    {
        UI::CloseDialog ();
        UI::OpenDialog (`opt(`decorated), `HBox(`HSpacing(1.5), `VBox(
            `VSpacing(1),
            // text label
            `Label(_("More than one joystick is connected.
Select the one to test
and press some of its buttons several times.
")),
            `VSpacing(1),
            `PushButton(`id(`abort), `opt(`default), Label::AbortButton()),
            `VSpacing(1)),
        `HSpacing(1.5)
        ));

	// more joysticks are connected: run jstest alternately for all ones,
	// until user pushs some button of joystick he want to test
	while (ret == nil)
	{
	    ret		= UI::PollInput();
	    js_id	= joysticks [i]:0;

	    SCR::Execute (.background.run_output,
		sformat("/usr/bin/jstest --event /dev/input/js%1", js_id));
	    sleep (500);

	    if ((boolean) SCR::Read(.background.output_open) &&
		(integer) SCR::Read(.background.newlines)>0)
	    {
		list test_output	= (list)SCR::Read(.background.newout);
		string last_output	= test_output [size (test_output)-1]:"";
		last_output		= deletechars (last_output, ",");
		list listed_output	= splitstring (last_output," ");
		integer event_type	= tointeger (listed_output[2]:"0");
		integer value		= tointeger (listed_output [8]:"0");
                if (event_type == 1)
	        {
	            y2debug ("output from /dev/js%1: %2", js_id, listed_output);
		    ret = `found;
		}
	    }
	    SCR::Execute(.background.kill);
	    if (ret != `found)
	    {
		if (i == size (joysticks))
		    i = 0;
		else
		    i = i + 1;
	    }
        }
    }
    if (ret == `abort)
    {
        y2debug ("Abort pressed");
        if (testing_new)
        {
            Joystick::joystick = current_conf;
            Joystick::Stop();
            Joystick::SaveOneJoystick(card_id);
            Joystick::WriteConfig();
            Joystick::StartAndEnable();
        }
        UI::CloseDialog();
        return false;
    }

    // number of device could be different from card number...
    js_id	= joysticks [i]:0;
    SCR::Execute(.background.run_output,
        sformat("/usr/bin/jstest --event /dev/input/js%1",js_id));

    // read the output of joystick initialization
    while ((boolean)SCR::Read(.background.output_open) &&
	   (integer)SCR::Read(.background.newlines) == 0)
    {
        ret = UI::PollInput();
        if (ret != nil)
        {
            SCR::Execute(.background.kill);
            if (testing_new)
            {
                Joystick::joystick = current_conf;
                Joystick::Stop();
                Joystick::SaveOneJoystick(card_id);
                Joystick::WriteConfig();
                Joystick::StartAndEnable();
            }
            UI::CloseDialog();
            return false;
        }
    }
    // get the number of axes and buttons from the initial jstest output line
    list test_output	= (list)SCR::Read(.background.newout);
    string last_output	= test_output[0]:"";
    list<string> listed_output	= splitstring (last_output," ");
    integer axes	= 2;
    integer buttons	= 2;
    i = 0;

    while (i < size(listed_output))
    {
        // all text parts ("has") depend on output of jstest!
        if (listed_output[i]:"" == "has" && listed_output[i+2]:"" == "axes" )
            axes = tointeger (listed_output[i+1]:"2");

        if (listed_output[i]:"" == "and" && listed_output[i+2]:"" == "buttons.")
            buttons = tointeger (listed_output[i+1]:"2");
        i = i + 1;
    }

    // generate appropriate dialog for the joystick
    term joy_attrib	= `VBox();
    string widget_name	= "";
    string widget_id	= "";
    integer min		= -32767; // see js_event.value in linux/joystick.h
    integer max		= 32767;
    i = 0;

    while (i < axes)
    {
	// progress bar label
        widget_name	= sformat(_("Axis %1"), i);
        widget_id	= sformat("Axis %1", i);
        joy_attrib	= add (joy_attrib, `ProgressBar(`id(widget_id),
            `opt(`autoShortcut), widget_name, 2*max, max));
        joy_attrib	= add (joy_attrib, `VSpacing(1));
        i = i + 1;
    }

    i = 0;
    term joy_buttons	= `HBox();
    string not_pressed	= "    ";
    string pressed	= UI::Glyph(`CheckMark);
    while (i < buttons)
    {
	// label
        widget_name	= sformat(_("Button %1"), i);
        widget_id	= sformat ("Button %1", i);
        joy_buttons	= add (joy_buttons,
            `Label (`id(widget_id), widget_name + " " + not_pressed));
        joy_buttons	= add (joy_buttons, `HSpacing(2));
        i = i + 1;
    }
    joy_attrib = add (joy_attrib, joy_buttons);

    UI::CloseDialog();
    UI::OpenDialog(`opt(`decorated), `HBox(`HSpacing(1.5),
	`VSpacing(18),
	`VBox(
	    `HSpacing(50),
	    `VSpacing(1),
	    // Popup label
	    `Label(_("Joystick Test")),
	    `VSpacing(1),
	    joy_attrib,
	    `VSpacing(1),
	    `PushButton(`id(`done), `opt(`default), Label::OKButton()),
	    `VSpacing(1)
	),
	`HSpacing(1.5)
    ));

    // read the jstest output until "OK" button is pushed
    do
    {
        if ((boolean)SCR::Read(.background.output_open) &&
	    (integer)SCR::Read(.background.newlines)>0)
        {
            // read the output line from jstest:
            test_output		= (list)SCR::Read(.background.newout);
            // select the last item of the list:
            last_output		= test_output [ size(test_output) - 1 ]:"";
            last_output		= deletechars (last_output, ",");

            listed_output	= splitstring (last_output," ");
            listed_output	= filter (string l, listed_output, ``(l != ""));

            // positions in list depend on jstest output!
            integer type	= tointeger (listed_output[2]:"0");
            integer number	= tointeger (listed_output[6]:"0");
            integer val		= tointeger (listed_output[8]:"0");

            if (type == 1)
            {
                // button pressed
                if (val == 1)
                    UI::ChangeWidget(`id(sformat("Button %1", number)), `Value,
			// label text ("Button" is joystick's button no. %1)
                        sformat(_("Button %1"), number) + " " + pressed);
                else
                    UI::ChangeWidget(`id(sformat("Button %1", number)), `Value,
                        sformat(_("Button %1"), number) + " " + not_pressed);
            }
            else if (type == 2)
            {
                // change in some axis
                UI::ChangeWidget(`id(sformat ("Axis %1", number)), `Value, max + val) ;
            }
        }
	ret = UI::PollInput();
    }
    while (ret == nil);

    SCR::Execute(.background.kill);

    // back to the old values
    Joystick::joystick = current_conf;
    if (testing_new)
    {
        Joystick::Stop();
        Joystick::SaveOneJoystick(card_id);
        Joystick::WriteConfig();
        Joystick::StartAndEnable();
    }
    UI::CloseDialog();
    return true;
}


    /**
     * Joystick configuration dialog.
     * Configuration of joystick attached to specified sound card.
     * @param card_id Sound card card_id
     * @param button Label for `next button: `finish, `ok or `next
     * @return symbol Id of pressed button in the dialog
     */
    define symbol joy_dialog (integer card_id, symbol button) ``{

	// item in joystick types
	string nonejoy = _("No joystick");

	if (card_id == nil)
	{
	    return `back;
	}

	list jcards = Sound::GetSoundCardListWithJoy();

	// find card name
	string cardname = "";
	foreach (map<string,any> card, (list<map<string,any> >)jcards, ``{
	    if (card["card_no"]:-1 == card_id)
		cardname = card["name"]:"";
	});

	// dialog header
	string caption = _("Joystick configuration") +
	    sformat (" - %1 (snd-card-%2)", cardname, card_id);

	string helptext =
	// help text for joystick configuration 1/4
_("<P>In this dialog, specify your joystick type. If your
joystick type is not in the list, select <B>Generic Analog Joystick</B>.</p>
<p>You will not find any USB joysticks here. If you have a USB device, just plug in the joystick and start using it.</P>
") +

	// help text for joystick configuration 2/4
_("<P>To remove the configured joystick from the system
or if you do not have a joystick, select <B>No joystick</B>.</P>
") +

	// help text for joystick configuration 3/4
_("<P>Press <B>Test</B> to test the functionality of the selected joystick type.</P>") +

	// help text for joystick configuration 4/4
_("<P><B>Note:</B> Connect your joystick to your computer
before pressing <B>Test</B> or <B>Next</B>.</P>");

	map joy		= Joystick::joystick[card_id]:$[];
	string mod	= joy["model"]:"";

	y2milestone("Joystick configuration of snd-card-%1 started", card_id);

	// translate model from /etc/sysconfig/joystick
	if (mod == Joystick::generic_joystick)
	{
	    mod = Joystick::generic_joystick_translated;
	}

	// set none joystick
	if (mod == "")
	{
	    mod = nonejoy;
	}

	joylist = maplist(string modname, list<string> models, JoystickDB, ``(
	    maplist(string model, models, ``([modname, model]))
	));
	joylist = flatten ((list<list>)joylist);

	joylist = sort (list j1, list j2, (list<list<any> >) joylist, ``( j1[1]:"" < j2[1]:""));

	joylist = prepend (joylist,
	    ["analog", Joystick::generic_joystick_translated]);

	joylist = prepend(joylist, ["", nonejoy]);

	y2debug("joylist: %1", joylist);

	integer index = 0;
	list boxitems = [];

	foreach (list l, (list<list<any> >) joylist, ``{

	    string model = l[1]:"";
	    if (mod == model)
	    {
		// preselect item
		boxitems = add(boxitems, `item(`id(index), model, true));
	    }
	    else
	    {
		boxitems = add(boxitems, `item(`id(index), model));
	    }
	    index = index + 1;
	});

	y2debug("for widget: %1", boxitems);

	term contents = `VBox(
	    `VSpacing(3),
	    `HBox(
		`HSpacing(3),
		// label above list of joystick types
		`SelectionBox(`id(`os), _("&Select your joystick type:"),
		    boxitems),
		`HSpacing(3),
		// button label
		`PushButton(`id(`test), `opt(`key_F6), _("&Test")),
		`HSpacing(3)
	    ),
	    `VSpacing(3)
	);

	map nextbutton = $[
	    `finish	: Label::FinishButton(),
	    `ok		: Label::OKButton(),
	    `next	: Label::NextButton()
	];

	Wizard::SetContents (caption, contents, helptext, true, true);
	Wizard::SetNextButton(`next, nextbutton[button]:Label::NextButton() );

	symbol s = nil;
	do
	{
	    s = (symbol) UI::UserInput();

	    if (s == `abort && !ReallyAbort())
	    {
		s = `skip_event;
	    }

	    if (s == `next && UI::QueryWidget(`id(`os), `CurrentItem) == nil)
	    {
		// warning message - user did not select any joystick type
		Popup::Warning(_("Select the joystick type from the list
before pressing Next.

"));
		s = `skip;
	    }

	    if (s == `test)
	    {
		integer idx = (integer) UI::QueryWidget(`id(`os), `CurrentItem);
		string joymod = joylist[idx,0]:"";
		if (joymod == "")
		    // error message (no joystick selected)
		    Popup::Message(_("Select the joystick to test."));
		else if (Require::RequireAndConflict(["input-utils"],[],
// popup: %1 is required application
_("<p>To test your joystick, the <b>%1</b> package is required.
It is currently not installed. Install it now?</p>")))
		    joy_test_popup (card_id, idx, joymod);
	    }

	} while (!contains([`next, `back, `abort, `cancel], s));

	if (s == `next)
	{
	    string modname	= Sound::modules_conf[card_id, "module"]:"";
	    map joy_entry	= Sound::GetJoystickSettings (modname);
	    integer joymodidx = (integer)UI::QueryWidget(`id(`os),`CurrentItem);
	    string joymod	= joylist[joymodidx,0]:"";

	    if (joymod == "")
	    {
		// selected none joystick
		joy_entry = $[];
	    }
	    else
	    {
		string model = joylist[joymodidx, 1]:"";

		// do not translate model in /etc/sysconfig/joystick
		if (model == Joystick::generic_joystick_translated)
		{
		    model = Joystick::generic_joystick;
		}

		y2debug("selected joy module: %1, model: %2", joymod, model);

		joy_entry = add (joy_entry, "JOYSTICK_MODULE", joymod);
		joy_entry = add (joy_entry, "model", model);

	    }
	    update_sound_card_joy_config (card_id, joymod != "");

	    Joystick::joystick[card_id] = joy_entry;
	    Joystick::modified = true;

	    y2milestone("New joystick configuration: %1", joy_entry);
	}

	// restore previous `next button label (only if label was not Finish)
	if (button != `finish)
	{
	    Wizard::RestoreNextButton();
	}

	return s;
    }

/* EOF */
}
