#!/usr/bin/perl -w

# Qemu Launcher is a GUI front-end to QEMU computer emulator.
# Copyright (C) 2004 - 2005, Erik Meitner <emeitner@f2o.org>
# Copyright (C) 2006 - 2007, Linas Žvirblis <0x0007@gmail.com>

# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

use strict;
use utf8;
use encoding 'utf8';
use Gtk2;
use Gtk2::GladeXML;
use POSIX;
use Locale::gettext;
use IO::File;
use Data::Dumper; # For debugging only!

# Modified by install script. Do not edit!
my $INSTALLED_UNDER = '/usr';

# Environment variables
our $home_dir = $ENV{'HOME'};
our $data_dir = undef;

our $config_dir = $home_dir.'/.config';
our $prefer_dir = $config_dir.'/qemu-launcher';
our $vmconf_dir = $prefer_dir.'/vmconfigs';

our $prefer_file = 'preferences';

# These are not real Perl modules,
# they are separated for convenience
my @modules = (
	'ql_subs_dialogs.pl',
	'ql_subs_diskimg.pl',
	'ql_subs_main.pl',
	'ql_subs_misc.pl',
	'ql_subs_prefer.pl',
	'ql_subs_redir.pl',
	'ql_subs_vmconfig.pl'
);

# Check if the application is properly installed
if ( $INSTALLED_UNDER )
{
	$data_dir = $INSTALLED_UNDER.'/share/qemu-launcher';
}
else
{
	$data_dir = '.';
}

# Load the modules
foreach my $module ( @modules )
{
	require( $data_dir.'/modules/'.$module );
}

# Locale definitions
textdomain( 'qemu-launcher' );
bindtextdomain( 'qemu-launcher', $data_dir.'/locale/' );
setlocale( LC_MESSAGES, "" );

# Global variables
our $app_name    = gettext( 'Qemu Launcher' );
our $app_version = '1.8.0-pre0';

our $glade_main  = undef;
our $window_main = undef;

our $glade_prefer  = undef;
our $window_prefer = undef;

our $glade_diskimg  = undef;
our $window_diskimg = undef;

our $window_vmconfig = undef;
our $glade_vmconfig  = undef;

our $window_main_w = 450;
our $window_main_h = 350;
our $window_vmconfig_w = 400;
our $window_vmconfig_h = 490;

our $icon_default = Gtk2::Gdk::Pixbuf->
	new_from_file( $data_dir.'/icons/qemu-launcher.svg' );

our $vmconfig_current = undef;
our %vmconfig_cache   = ();

our $treeview_main = undef;
our $diskimg_drive = undef;
our $idx = 0;

our @apps_main = (
	'qemu',
	'qemu-img',
	'nice',
	'dd'
);

our @apps_extra = (
	'qemu-system-x86_64',
	'qemu-system-arm',
	'qemu-system-armeb',
	'qemu-system-ppc',
	'qemu-system-ppc64',
	'qemu-system-sparc',
	'qemu-system-sparc64',
	'qemu-system-mips',
	'qemu-system-mipsel',
	'kvm'
);

our %arch_by_emu = (
	'qemu'                => 'x86',
	'qemu-system-x86_64'  => 'x86_64',
	'qemu-system-arm'     => 'arm',
	'qemu-system-armeb'   => 'armeb',
	'qemu-system-ppc'     => 'ppc',
	'qemu-system-ppc64'   => 'ppc64',
	'qemu-system-sparc'   => 'sparc',
	'qemu-system-sparc64' => 'sparc64',
	'qemu-system-mips'    => 'mips',
	'qemu-system-mipsel'  => 'mipsel'
);

our %emu_by_arch = (
	'x86'     => 'qemu',
	'x86_64'  => 'qemu-system-x86_64',
	'arm'     => 'qemu-system-arm',
	'armeb'   => 'qemu-system-armeb',
	'ppc'     => 'qemu-system-ppc',
	'ppc64'   => 'qemu-system-ppc64',
	'sparc'   => 'qemu-system-sparc',
	'sparc64' => 'qemu-system-sparc64',
	'mips'    => 'qemu-system-mips',
	'mipsel'  => 'qemu-system-mipsel'
);

our %text_by_arch = (
	'x86'     => gettext('PC, 32-bit (x86)'),
	'x86_64'  => gettext('PC, 64-bit (x86-64)'),
	'arm'     => gettext('ARM, little endian (arm)'),
	'armeb'   => gettext('ARM, big endian (armeb)'),
	'ppc'     => gettext('PowerPC, 32-bit (ppc)'),
	'ppc64'   => gettext('PowerPC, 64-bit (ppc64)'),
	'sparc'   => gettext('SPARC, 32-bit (sparc)'),
	'sparc64' => gettext('SPARC, 64-bit (sparc64)'),
	'mips'    => gettext('MIPS, big endian (mips)'),
	'mipsel'  => gettext('MIPS, little endian (mipsel)')
);

# Hash for external applications
# Assembled at startup by detect_apps_main() and detect_apps_extra()
our %apps = ();

# Hash for prefererences
our %prefer = ();
our %prefer_default = (
	'datadir' => $home_dir,
	'action'  => 'launch',
	'toolbar' => 'both'
);

# Hash for default configuration
our %vmconfig_entries = (
	'args'      => undef,
	'kernelcmd' => undef,
	'netip0'    => undef,
	'netip1'    => undef,
	'netip2'    => undef,
	'netip3'    => undef,
	'netip4'    => undef,
	'netip5'    => undef,
	'netip6'    => undef,
	'netip7'    => undef,
	'netmac0'   => undef,
	'netmac1'   => undef,
	'netmac2'   => undef,
	'netmac3'   => undef,
	'netmac4'   => undef,
	'netmac5'   => undef,
	'netmac6'   => undef,
	'netmac7'   => undef,
	'netname0'  => undef,
	'netname1'  => undef,
	'netname2'  => undef,
	'netname3'  => undef,
	'netname4'  => undef,
	'netname5'  => undef,
	'netname6'  => undef,
	'netname7'  => undef,
	'prelaunch' => undef
);

our %vmconfig_choosers = (
	'cdrom'      => '/dev/cdrom',
	'fda'        => undef,
	'fdb'        => undef,
	'hda'        => undef,
	'hdb'        => undef,
	'hdc'        => undef,
	'hdd'        => undef,
	'initrd'     => undef,
	'kernel'     => undef,
	'state'      => undef,
	'tapscript0' => undef,
	'tapscript1' => undef,
	'tapscript2' => undef,
	'tapscript3' => undef,
	'tapscript4' => undef,
	'tapscript5' => undef,
	'tapscript6' => undef,
	'tapscript7' => undef
);

our %vmconfig_dirchoosers = (
	'smb'  => undef,
	'tftp' => undef
);

our %vmconfig_checkbuttons = (
	'audio'      => 0,
	'fullscreen' => 0,
	'linuxboot'  => 0,
	'localtime'  => 1,
	'log'        => 0,
	'nogfx'      => 0,
	'smbserver'  => 0,
	'snapshot'   => 0,
	'usecdrom'   => 0,
	'tftpserver' => 0
);

our %vmconfig_togglebuttons = (
	'log_cpu'     => 0,
	'log_exec'    => 0,
	'log_in_asm'  => 0,
	'log_int'     => 0,
	'log_op_opt'  => 0,
	'log_op'      => 0,
	'log_out_asm' => 0,
	'log_pcall'   => 0
);

our %vmconfig_spinbuttons = (
	'netfd0'   => 0,
	'netfd1'   => 0,
	'netfd2'   => 0,
	'netfd3'   => 0,
	'netfd4'   => 0,
	'netfd5'   => 0,
	'netfd6'   => 0,
	'netfd7'   => 0,
	'netport0' => 1,
	'netport1' => 1,
	'netport2' => 1,
	'netport3' => 1,
	'netport4' => 1,
	'netport5' => 1,
	'netport6' => 1,
	'netport7' => 1,
	'netvlan0' => 0,
	'netvlan1' => 0,
	'netvlan2' => 0,
	'netvlan3' => 0,
	'netvlan4' => 0,
	'netvlan5' => 0,
	'netvlan6' => 0,
	'netvlan7' => 0,
	'numcpus'  => 1,
	'numnics'  => 1,
	'priority' => 0,
	'ram'      => 128
);

our %vmconfig_comboboxes = (
	'boot'       => 'c',
	'keyboard'   => undef,
	'monitordev' => undef,
	'nettype0'   => 'usermode',
	'nettype1'   => 'usermode',
	'nettype2'   => 'usermode',
	'nettype3'   => 'usermode',
	'nettype4'   => 'usermode',
	'nettype5'   => 'usermode',
	'nettype6'   => 'usermode',
	'nettype7'   => 'usermode',
	'serialdev'  => undef,
	'systype'    => 'x86'
);

our %vmconfig_radiobuttons = (
	'acceltype' => 'enable',	# disable, enable, full, kvm
	'gfxtype'   => 'pcivga',	# pcivga or vga
	'sndtype'   => 'sb16'		# sb16 or es1370
);

our %vmconfig_misc = (
	'portredirs' => undef,
	'name'       => gettext('No name'),
	'icon'       => 'default'
);

our %vmconfig_default = (
	%vmconfig_entries,
	%vmconfig_choosers,
	%vmconfig_dirchoosers,
	%vmconfig_checkbuttons,
	%vmconfig_togglebuttons,
	%vmconfig_spinbuttons,
	%vmconfig_comboboxes,
	%vmconfig_radiobuttons,
	%vmconfig_misc
);

our %boot_val_by_txt = (
	'a' => 0,
	'c' => 1,
	'd' => 2
);

our %boot_val_by_num = (
	0 => 'a',
	1 => 'c',
	2 => 'd'
);

our %boot_values_dev = (
	'a' => 'fda',
	'c' => 'hda',
	'd' => 'cd'
);

our %net_val_by_num = (
	0 => 'usermode',
	1 => 'tuntap',
	2 => 'tuntapfd',
	3 => 'tcplisten',
	4 => 'tcpfd',
	5 => 'tcpconnect',
	6 => 'multicast',
	7 => 'multicastfd',
	8 => 'dummy'
);

our %net_val_by_txt = (
	'usermode'    => 0,
	'tuntap'      => 1,
	'tuntapfd'    => 2,
	'tcplisten'   => 3,
	'tcpfd'       => 4,
	'tcpconnect'  => 5,
	'multicast'   => 6,
	'multicastfd' => 7,
	'dummy'       => 8
);

our %sys_val_by_num = (
	0 => 'x86',
);

our %sys_val_by_txt = (
	'x86' => 0,
);

our %kbd_val_by_num = (
	1  => 'ar',
	2  => 'da',
	3  => 'de',
	4  => 'de-ch',
	5  => 'en-gb',
	6  => 'en-us',
	7  => 'es',
	8  => 'et',
	9  => 'fi',
	10 => 'fo',
	11 => 'fr',
	12 => 'fr-be',
	13 => 'fr-ca',
	14 => 'fr-ch',
	15 => 'hr',
	16 => 'hu',
	17 => 'is',
	18 => 'it',
	19 => 'ja',
	20 => 'lt',
	21 => 'lv',
	22 => 'mk',
	23 => 'nl',
	24 => 'nl-be',
	25 => 'no',
	26 => 'pl',
	27 => 'pt',
	28 => 'pt-br',
	29 => 'ru',
	30 => 'sl',
	31 => 'sv',
	32 => 'th',
	33 => 'tr'
);

our %kbd_val_by_txt = (
	'ar'    => 1,
	'da'    => 2,
	'de'    => 3,
	'de-ch' => 4,
	'en-gb' => 5,
	'en-us' => 6,
	'es'    => 7,
	'et'    => 8,
	'fi'    => 9,
	'fo'    => 10,
	'fr'    => 11,
	'fr-be' => 12,
	'fr-ca' => 13,
	'fr-ch' => 14,
	'hr'    => 15,
	'hu'    => 16,
	'is'    => 17,
	'it'    => 18,
	'ja'    => 19,
	'lt'    => 20,
	'lv'    => 21,
	'mk'    => 22,
	'nl'    => 23,
	'nl-be' => 24,
	'no'    => 25,
	'pl'    => 26,
	'pt'    => 27,
	'pt-br' => 28,
	'ru'    => 29,
	'sl'    => 30,
	'sv'    => 31,
	'th'    => 32,
	'tr'    => 33
);

our %dev_val_by_num = (
	1 => 'vc',
	2 => 'pty',
	3 => 'null',
	4 => 'stdio'
);

our %dev_val_by_txt = (
	'vc'    => 1,
	'pty'   => 2,
	'null'  => 3,
	'stdio' => 4
);

our %tool_val_by_num = (
	0 => 'both',
	1 => 'icons',
	2 => 'text'
);

our %tool_val_by_txt = (
	'both'  => 0,
	'icons' => 1,
	'text'  => 2
);

# Quit application
# Args: none
sub app_quit
{
	Gtk2->main_quit();
	return(1);
}

# Performs basic sanity checks
sub app_init
{
	if ( ! -d $config_dir )
	{
		mkdir( $config_dir );
	}

	if ( ! -d $prefer_dir )
	{
		mkdir( $prefer_dir );
	}

	if ( ! -d $vmconf_dir )
	{
		mkdir( $vmconf_dir );
	}

	return(1);
}

# Run application
sub app_main_gui
{
	app_init();
	Gtk2->init();

	if ( detect_apps_main() )
	{
		prefer_load();
		detect_apps_extra();

		$glade_main = Gtk2::GladeXML->new( $data_dir.'/glade/ql_window_main.glade' );

		vmconfig_window_init();
		vmconfig_window_widgets();
		vmconfig_window_signals();

		main_window_show();
		main_window_signals();

		vmconfig_cache();
		vmconfig_populate();
		vmconfig_selectfirst();

		Gtk2->main();
	}
}

sub app_main_cmd
{
	app_init();

	if ( detect_apps_main() )
	{
		prefer_load();
		detect_apps_extra();

		vmconfig_cache();

		foreach my $item ( %vmconfig_cache )
		{
			if ( $vmconfig_cache{$item} )
			{
				if ( $vmconfig_cache{$item}{'name'} eq $ARGV[0] )
				{
					vmconfig_launch( $item );
	
					return(1);
				}
			}
		}
	}

	return(0);
}

if ( scalar @ARGV )
{
	app_main_cmd();
}
else
{
	app_main_gui();
}

exit(1);
