/**
 * File:
 *   cdrom.ycp
 *
 * Module:
 *   Configuration of cdrom
 *
 * Summary:
 *   Main file
 *
 * Authors:
 *   Thoms Fehr <fehr@suse.de>
 *
 * $Id: cdrom.ycp 21914 2005-02-24 16:35:20Z fehr $
 *
 */

{
textdomain "storage";

import "CommandLine";
import "Wizard";
import "AsciiFile";
import "Partitions";

import "Popup";
import "Storage";
import "StorageDevices";

map<integer,map> cdrom = $[];
map<integer,map> sysfs = $[];
map<string,string> links = $[];
map fstab = $[];
integer num=0;
string sysfspath = StorageDevices::UdevCdromPath();

define list CreateTableList( map<any,map> cdrom )
    ``{
    list clist = maplist( any k, map entry, cdrom,
	``{
	term it = `item( `id(k) );
	it = add( it, entry["model"]:"" );
	it = add( it, entry["device"]:"" );
	if( entry["configured"]:false )
	    {
	    it = add( it, "/dev/"+entry["udev_links",0]:"" );
	    it = add( it, entry["mount"]:"" );
	    }
	else
	    {
	    it = add( it, "" );
	    it = add( it, "" );
	    }
	return( it );
	});
    y2milestone( "clist %1", clist );
    return( clist );
    }

map<integer,map> AddLinks( map<integer,map> cds )
    ``{
    list<map> clist = filter( map e, maplist( integer i, map v, cds, ``(v)), 
			      ``(e["configured"]:false) );
    clist = StorageDevices::AddNormalLinknames( clist );
    clist = maplist( map drive, clist,
	``{
	drive["udev_links"] = [ substring(drive["linkname"]:"",5) ];
	drive["changed"] = true;
	return( drive );
	});
    clist = StorageDevices::AddAlternateLinks( clist );
    foreach( map drive, clist,
	``{
	cds[drive["index"]:-1] = drive;
	y2milestone( "AddLinks drive %1", drive );
	});
    return( cds );
    }

define void EnableButtons( map entry )
    ``{
    UI::ChangeWidget( `id(`add), `Enabled, !entry["configured"]:false );
    UI::ChangeWidget( `id(`remove), `Enabled, entry["configured"]:false );
    }

define void UpdateSysfsLine( map e )
    ``{
    y2milestone( "UpdateSysfsLine bus:%1 bus_id:%2 links:%3",
                 e["bus_hwcfg"]:"", e["sysfs_bus_id"]:"", e["udev_links"]:[] );
    list<integer> idx = StorageDevices::FindUdevRulesLine( sysfs, e["bus_hwcfg"]:"",
							   e["sysfs_bus_id"]:"" );
    if( size(idx)==0 )
	{
	if( AsciiFile::NumLines( sysfs )==0 )
	    {
	    AsciiFile::AppendLine( sysfs, [ "# cdrom links generated by YaST2" ] );
	    AsciiFile::AppendLine( sysfs, [ "# " ] );
	    }
	AsciiFile::AppendLine( sysfs, [ StorageDevices::MakeUdevRulesLine(e) ] );
	}
    else
	{
	map l = AsciiFile::GetLine( sysfs, idx[0]:0 );
	string new = l["line"]:"";
	integer pos = find( new, "SYMLINK" );
	if( pos>=0 )
	    new = substring( new, 0, pos ) + StorageDevices::UdevSylinkEntry(e["udev_links"]:[]);
	else 
	    new = new + " " +  StorageDevices::UdevSylinkEntry(e["udev_links"]:[]);
	AsciiFile::ReplaceLine( sysfs, idx[0]:0, [ new ] );
	}
    AsciiFile::RewriteFile( sysfs, sysfspath );
    sysfs = StorageDevices::GetSysfsCdrom();
    }

define void RemoveSysfsLine( map e )
    ``{
    list<integer> idx = StorageDevices::FindUdevRulesLine( sysfs, e["bus_hwcfg"]:"",
							   e["sysfs_bus_id"]:"" );
    y2milestone( "RemoveSysfsLine line %1", idx );
    if( size(idx)>0 )
	{
	AsciiFile::RemoveLines( sysfs, idx );
	AsciiFile::RewriteFile( sysfs, sysfspath );
	sysfs = StorageDevices::GetSysfsCdrom();
	}
    y2milestone( "RemoveSysfsLine bus:%1 bus_id:%2 links:%3",
                 e["bus_hwcfg"]:"", e["sysfs_bus_id"]:"", e["udev_links"]:[] );
    }

define map IntegrateCd( map entry, map fstab )
    ``{
    map ret = $[];
    y2milestone( "IntegrateCd entry %1", entry );
    string link = "/dev/" + entry["udev_links",0]:"";
    string t = substring(entry["device"]:"",5);
    y2milestone( "IntegrateCd link(s) %1 to %2", entry["udev_links"]:[], t );
    UpdateSysfsLine( entry );
    t = entry["mount"]:"";
    if( SCR::Read( .target.size, t )<0 )
	{
	SCR::Execute( .target.bash, "mkdir " + t );
	}
    t = substring( t, findlastof(t,"/")+1 );
    ret["d"] = t;
    list lines = Storage::FindFstabLines( fstab, "", link, "", "" );
    y2milestone( "IntegrateCd fstab link %1 lines %2", link, lines );
    if( size(lines)>0 )
	{
	ret["l"] = lines[0]:-1;
	AsciiFile::ChangeLineField( fstab, lines[0]:-1, 1, entry["mount"]:"" );
	}
    else
	{
	map new = Storage::MakeCdromFstabEntry( entry );
	list fstlist = [ link, entry["mount"]:"",
			 new["vfstype"]:"", new["mntops"]:"", 
			 sformat("%1",new["freq"]:0),
			 sformat("%1",new["passno"]:0) ];
	y2milestone( "IntegrateCd fstlist %1", fstlist );

	AsciiFile::AppendLine( fstab, fstlist );
	}
    y2milestone( "IntegrateCd ret %1", ret );
    ret["m"] = fstab;
    return( ret );
    }

define string HelpText()
    ``{
    // help text
    return( _("<p>This module configures the CD/DVD drives in your system.</p>
<p>The list contains all detected CD/DVD drives. If there are entries for
the name of the link and the mount point displayed, the CD-ROM is integrated
into the system. Integration into the system means that a symbolic link
(e.g., /dev/cdrom, /dev/dvd, etc.) to the device name (e.g., /dev/hdc, /dev/sr0,
etc.) is present, an entry in /etc/fstab exists, a subdirectory below /media 
is present, and possibly a kernel command-line parameter is provided 
(for IDE devices).</p>
<p>Use \"Add\" and \"Remove\" to change the state 
of integration of an entry in the list.</p>
"));
    }

/**
 * Read CDROM settings
 */
define boolean CDROMRead () 
    {
    sysfs = StorageDevices::GetSysfsCdrom();
    y2milestone( "sysfs %1", sysfs );

    fstab = Partitions::GetFstab( "/etc/fstab" );

    list<map> tmp = maplist( map entry, (list<map>)SCR::Read(.probe.cdrom),
	``{
	map cd = StorageDevices::GetCdromEntry( entry["dev_name"]:"" );
        cd = union( cd, entry );
	y2milestone( "CDROMRead entry %1 is %2", entry["dev_name"]:"", cd );
	map config = (map)SCR::Read(.probe.status, entry["unique_key"]:"");
	cd["configured"] = config["configured"]:`no == `yes;
	cd["changed"] = false;
	string device = cd["dev_name"]:(entry["dev_name"]:"");
	cd["device"] = device;
	list<string> links = [];
	list<integer> idx = StorageDevices::FindUdevRulesLine( sysfs, cd["bus_hwcfg"]:"",
							       cd["sysfs_bus_id"]:"" );
        if( size(idx)>0 )
	    {
	    map l = AsciiFile::GetLine( sysfs, idx[0]:0 );
	    y2milestone( "CDROMRead l %1", l );
	    integer pos = find( l["line"]:"", "SYMLINK" );
	    if( pos>=0 )
		{
		string new = substring( l["line"]:"", pos+9 );
		y2milestone( "CDROMRead pos %1 new %2", pos, new );
		pos = find( new, "\"" );
		y2milestone( "CDROMRead pos %1", pos );
		if( pos>=0 )
		    new = substring( new, 0, pos );
		y2milestone( "CDROMRead new %1", new );
		links = filter( string e, splitstring( new, " " ), ``(size(e)>0));
		links = sort( string a, string b, links, 
	             ``(StorageDevices::TypeNameIndex(a)>=StorageDevices::TypeNameIndex(b)) );
		y2milestone( "CDROMRead links %1", links );
		}
	    }
	cd["udev_links"] = links;
	cd["configured"] = cd["configured"]:false && size(cd["udev_links"]:[])>0;
	list lines = [];
	foreach( string l, links,
	    ``{
	    lines = union( lines, Storage::FindFstabLines( fstab, "", "/dev/"+l,
							   "", "" ));
	    });
	if( size(lines)>0 )
	    {
	    map fse = AsciiFile::GetLine( fstab, lines[0]:-1 );
	    cd["mount"] = fse["fields",1]:"";
	    }
	y2milestone( "CDROMRead %1", cd );
	return( cd );
	});

    tmp = sort( map a, map b, tmp,
	``{
	boolean ret = true;
	ret = (a["configured"]:false || !b["configured"]:false) && 
	       a["configured"]:false != b["configured"]:false;
	y2milestone( "a:%1 b:%2 ret %3", a["configured"]:false, 
                     b["configured"]:false, ret );
	return( ret );
	});

    num=0;
    cdrom = listmap( map e, tmp, 
		     ``{num=num+1; e["index"]=num-1; return $[num-1:e];});
    return true;
    }

define any CDROMSequence () {

    CDROMRead ();

    y2milestone( "cdrom=%1", cdrom );

    Wizard::OpenAbortApplyFinishDialog();

    list<map> tmp	= [];

    term dialog =
	`VBox( 
	    `VSpacing(3),
	    `HBox( 
		`HWeight(3, `HSpacing() ),
		`HWeight(60, 
		    `VBox(
			// label text
		        `Left(`Label(_("Detected CD/DVD Devices"))),
			`Table( `id(`cdroms), 
			        `opt(`notify,`immediate,`keepSorting),
				// table header texts
				`header( _("Name"), _("Device"), _("Link"), _("Mount Point")),
				CreateTableList( cdrom ) ))), 
		`HWeight(2, `HSpacing() ),
		`HWeight(15, `Top( `VBox( `VSpacing(3),
					  `PushButton( `id(`add), 
						       // button text
					               _("A&dd") ),
					  `PushButton( `id(`remove), 
						       // button text
					               _("&Remove") )))),
		`HWeight(2, `HSpacing() )),
	    `VSpacing(3) 
	    );

    if( size(cdrom)==0 )
	{
        // popup text
	Popup::Message( _("No CD/DVD device found") );
	return `cancel;
	}
    // header text
    Wizard::SetContents( _("CD/DVD device integration"), dialog, HelpText(), 
                         true, true );
    Wizard::SetDesktopIcon( "cdrom" );

    EnableButtons( cdrom[0]:$[] );
    symbol ret = `none;
    do
	{
	ret = (symbol)UI::UserInput();
	y2milestone( "ret = %1", ret );
	if( ret == `add || ret == `remove )
	    {
	    num = (integer)UI::QueryWidget( `id(`cdroms), `CurrentItem );
	    cdrom[num,"changed"] = true;
	    cdrom[num,"configured"] = ret == `add;
	    if( ret == `add )
		{
		string type = substring( cdrom[num,"linkname"]:"", 5 );
		type = deletechars( type, "0123456789" );
		y2milestone( "type %1", type );
		tmp = maplist( any k, map e, 
                               filter( integer l, map f, cdrom, 
				       ``(l!=num && f["configured"]:false)),
			       ``(e) );
		y2milestone( "tmp %1", tmp );
		tmp = filter( map e, tmp, 
		                  ``(find(e["linkname"]:"", "/dev/"+type)==0));
		y2milestone( "tmp %1", tmp );
		list lnames = [];
		foreach( map e, tmp, 
		    ``{
		    if( size(e["udev_links"]:[])>0 )
			lnames = add(lnames, e["udev_links",0]:"");
		    });
		y2milestone( "lnames %1", lnames );
		lnames = filter( string e, (list<string>)lnames, ``(find(e, type)==0));
		y2milestone( "lnames %1", lnames );
		lnames = maplist( string e, (list<string>)lnames, ``(substring(e, size(type))));
		y2milestone( "lnames %1", lnames );
		lnames = maplist( string e, (list<string>)lnames, 
				  ``{
				  integer ret = 1;
				  if( e=="1" )
				      ret = 2;
				  else if( size(e)>0 )
				      ret = tointeger(e);
				  return( ret );
				  });
		y2milestone( "lnames %1", lnames );
		integer max = 1;
		while( contains( lnames, max ) )
		    {
		    max = max+1;
		    }
		if( max>1 )
		    {
		    type = type + sformat("%1",max);
		    }
		cdrom[num,"udev_links"] = [ type ];
		cdrom[num,"mount"] = "/media/" + type;
		}
	    UI::ChangeWidget( `id(`cdroms), `Items, CreateTableList( cdrom ) );
	    UI::ChangeWidget( `id(`cdroms), `CurrentItem, num );
	    EnableButtons( cdrom[num]:$[] );
	    y2milestone( "entry %1", cdrom[num]:$[] );
	    }
	if( ret == `cdroms )
	    {
	    num = (integer)UI::QueryWidget( `id(`cdroms), `CurrentItem );
	    EnableButtons( cdrom[num]:$[] );
	    }
	else if( ret == `abort || ret == `cancel )
	    {
	    if( size(filter( any k, map e, cdrom, ``(e["changed"]:false)))>0 )
		{
		// popup text
		if( !Popup::YesNo( _("You have unsaved changes.
These changes will be lost if you abort now.

Discard changes?
")))
		    {
		    ret = `continue;
		    }
		}
	    }
	else if( ret == `apply || ret == `finish )
	    {
	    list<map> ch = maplist( integer l, map v, 
	                       filter( integer k, map e, cdrom, ``(e["changed"]:false)),
			       ``(v));
	    y2milestone( "changed %1", ch );
	    list<integer> remfstab = [];
	    list<string> remdirs = [];
	    foreach( map e, filter( map v, ch, ``(!v["configured"]:false)),
		``{
		list lines = [];
		foreach( string l, e["udev_links"]:[], 
		    ``{
		    lines = union( lines,
				   Storage::FindFstabLines( fstab, "", "/dev/"+l,
							    "", "" ));
		    });
		remfstab = (list<integer>)union( remfstab, lines );
		remdirs = (list<string>) union( remdirs, e["udev_links"]:[] );
		RemoveSysfsLine( e );
		cdrom[e["index"]:-1,"udev_links"] = [];
		});
	    cdrom = AddLinks( cdrom );
	    ch = maplist( integer l, map v, 
			  filter( integer k, map e, cdrom, ``(e["changed"]:false)),
				  ``(v));
	    foreach( map e, filter( map v, ch, ``(v["configured"]:false)),
		``{
		string device = e["dev_orig"]:(e["dev_name"]:"");
		device = substring(device,5);
		map ret = IntegrateCd( e, fstab );
		fstab = ret["m"]:fstab;
		remdirs = filter( string e, remdirs, ``(e!=ret["d"]:""));
		remfstab = filter( integer e, remfstab, ``(e!=ret["l"]:-1));
		});
	    remdirs = filter( string e, remdirs, ``(e!="cdrom"));
	    if( size(remdirs)>0 )
		{
		string cmd = "cd /media && rmdir " + mergestring( remdirs, " ");
		y2milestone( "cmd=%1", cmd );
		SCR::Execute( .target.bash, cmd );
		}
	    y2milestone( "remfstab %1 remdirs %2", remfstab, remdirs );
	    if( size(ch)>0 )
		{
		if( size(remfstab)>0 )
		    {
		    AsciiFile::RemoveLines( fstab, remfstab );
		    }
		AsciiFile::RewriteFile( fstab, "/etc/fstab" );
		fstab = Partitions::GetFstab( "/etc/fstab" );
		}
	    foreach( map e, ch, ``{ cdrom[e["index"]:-1,"changed"] = false; });
	    foreach( any k, map entry, cdrom,
		``{
		if( size(entry["unique_key"]:"")>0 )
		    {
		    SCR::Write( .probe.status.configured, 
			        entry["unique_key"]:"", 
				entry["configured"]:false?`yes:`no );
		    }
		});
	    if( size(ch)>0 )
		{
		// popup text
		string text = _("CD/DVD device changes performed.");
		Popup::Message( text );
		}
	    y2milestone( "cdrom %1", cdrom );
	    }
	}
    while( ret != `abort && ret != `finish );
    UI::CloseDialog();
    return ret;
}

/**
 * print cdrom summary
 */
define boolean CDROMSummaryHandler (map options) {

    boolean more	= false;
    foreach (any k, map entry, cdrom, {

	if (more)
	    CommandLine::Print ("");

	// summary item
	CommandLine::Print (sformat (_("Model: %1"), entry["model"]:""));
	// summary item
	CommandLine::Print (sformat (_("Device: %1"), entry["device"]:""));

	if (entry["configured"]:false)
	{
	    // summary item
	    CommandLine::Print (sformat(_("Link: /dev/%1"),entry["udev_links",0]:""));
	    // summary item
	    CommandLine::Print(sformat(_("Mount Point: %1"),entry["mount"]:""));
	}
	more	= true;
    });
    return false;
}

/* -- the command line description map -------------------------------------- */
map cmdline = $[
    "id"		: "cdrom",
    // translators: command line help text for ccdrom module
    "help"		: _("CD-ROM configuration."),
    "guihandler"	: CDROMSequence,
    "initialize"	: CDROMRead,
    "actions"		: $[
	"summary" :$[
	    "handler"	: CDROMSummaryHandler,
	    // command line help text for 'summary' action
	    "help"	: _("CD-ROM configuration summary."),
	],
    ],
];

CommandLine::Run (cmdline);
return true;

}
