#!/usr/bin/perl -w

use warnings 'all';
use locale;
use strict;
use POSIX;
use Wx;

#**************************************************************************
# hack for wxMotif compatibility
if (Wx::wxMOTIF()) {
	eval 'sub Wx::Window::SetToolTip {}';
	eval 'sub Wx::ToolTip::new {}';
}

###########################################################################
package wx_pere;

use vars qw($VERSION);
$VERSION = "0.2.0";

use POSIX;
use Wx qw(
	wxOK
	wxICON_INFORMATION
	wxICON_ERROR
	wxICON_EXCLAMATION
	wxYES
	wxYES_NO
	wxYES_DEFAULT
	wxICON_QUESTION
);

use Lingua::Pere::Misc::Nums qw(
	min
	max
);

use vars qw($CFG $CHG $FRA);

$FRA = undef;

my %NAM = ();

#**************************************************************************
sub getname {
	my ($val) = @_;

	if (defined($val) && ($val ne '')) {
		if (exists($NAM{$val}) && defined($NAM{$val}) &&  ($NAM{$val} ne '')) {
			$val = $NAM{$val};
		}
	}

	return $val;
}

#**************************************************************************
sub row2names {
	my ($row) = @_;
	
	my ($iln, $oln, $thm, $pfx) = @{$row};

	return sprintf("%s => %s: %s", getname($iln), getname($oln), getname($thm || 'common'));
}

#**************************************************************************
sub hash2choice {
	my ($cho) = @_;

	$cho->Clear;
	my $par = $CFG->pairs;
	foreach my $iln (sort keys %{$par}) {
		my $ho = $par->{$iln};
		if (defined($ho) && (ref($ho) eq 'HASH')) {
			foreach my $oln (sort keys %{$ho}) {
				my $ht = $ho->{$oln};
				if (defined($ht)) {
					if (ref($ht) eq 'HASH') {
						foreach my $thm (sort keys %{$ht}) {
							my $pfx = $ht->{$thm};
							if (defined($pfx) && (ref($pfx) eq '')) {
								my $dat = [$iln, $oln, $thm, $pfx];
								$cho->Append(row2names($dat), $dat);
							}
						}
					}
				}
			}
		}
	}
}

#**************************************************************************
sub msgerror {
	my ($text, $capt) = @_;

	return Wx::MessageBox(
		$text,
		$capt || "Error",
		wxOK | wxICON_ERROR
		$FRA
	);
}

#**************************************************************************
sub msgwarn {
	my ($text, $capt) = @_;

	return Wx::MessageBox(
		$text,
		$capt || "Warning",
		wxOK | wxICON_EXCLAMATION
		$FRA
	);
}

#**************************************************************************
sub msgyesno {
	my ($text, $capt) = @_;

	return (Wx::MessageBox(
		$text,
		$capt || "Question",
		wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION
		$FRA
	) == wxYES) ? 1 : 0;
}

#**************************************************************************
sub msginfo {
	my ($text, $capt) = @_;

	return Wx::MessageBox(
		$text,
		$capt || "Information",
		wxOK | wxICON_INFORMATION
		$FRA
	);
}

#**************************************************************************
sub rect2right {
	my ($rect, $dlt) = @_;
	$dlt ||= 10;

	$rect = $rect->GetRect if (ref($rect) ne 'Wx::Rect');
	return Wx::Point->new($rect->GetRight+$dlt, $rect->GetTop);
}

#**************************************************************************
sub rect2down {
	my ($rect, $dlt) = @_;
	$dlt ||= 10;

	$rect = $rect->GetRect if (ref($rect) ne 'Wx::Rect');
	return Wx::Point->new($rect->GetLeft, $rect->GetBottom+$dlt);
}

#**************************************************************************
sub grow4rect {
	my $uni = shift;

	my ($l, $t, $r, $b) = ($uni->GetLeft, $uni->GetTop, $uni->GetRight, $uni->GetBottom);
	foreach my $obj (@_) {
		my $rect = (ref($obj) eq 'Wx::Rect') ? $obj : $obj->GetRect;

		$l = min($l, $rect->GetLeft);
		$t = min($t, $rect->GetTop);
		$r = max($r, $rect->GetRight);
		$b = max($b, $rect->GetBottom);
	}	

	$uni->SetX($l);
	$uni->SetY($t);
	$uni->SetWidth($r-$l+1);
	$uni->SetHeight($b-$t+1);

	return $uni;
}

#**************************************************************************
sub rect2union {

	my ($l, $t, $r, $b) = (INT_MAX, INT_MAX, 0, 0);
	foreach my $obj (@_) {
		my $rect = (ref($obj) eq 'Wx::Rect') ? $obj : $obj->GetRect;

		$l = min($l, $rect->GetLeft);
		$t = min($t, $rect->GetTop);
		$r = max($r, $rect->GetRight);
		$b = max($b, $rect->GetBottom);
	}	

	return Wx::Rect->new($l, $t, $r-$l+1, $b-$t+1);
}

#**************************************************************************
sub align2union {
	my $uni = rect2union(@_);

	my ($t, $b, $h) = ($uni->GetTop, $uni->GetBottom, $uni->GetHeight);
	foreach my $obj (@_) {
		my $rect = $obj->GetRect;
		my $d = int(($h-$rect->GetHeight)/2);
		if ($d > 0) {
			$rect->SetY($t+$d);
			$obj->SetSize($rect);
		}
	}	

	return $uni;
}

###########################################################################
package ConfPanel;

use base qw(Wx::Panel);

use File::Spec;

use Wx qw(
	:textctrl
	:window
	:filedialog
	wxDefaultPosition
	wxDefaultSize
	wxID_CANCEL
	wxOPEN
	wxFILE_MUST_EXIST
	wxCHANGE_DIR
	wxSAVE
	wxOVERWRITE_PROMPT
	wxNOT_FOUND
	wxPD_AUTO_HIDE
	wxPD_APP_MODAL
);

use Wx::Event qw(
	EVT_BUTTON
	EVT_CHOICE
	EVT_CHECKBOX
	EVT_SPINCTRL
	EVT_TEXT
);
use Lingua::Pere::Dict::Trans;
use Lingua::Pere::Misc::MIME qw(
	mime2descr
	magic2mime
);
use Lingua::Pere::Misc::File qw(
	makedir
	openfile
);

my $defpfx  = '';
my $defsdir = '';
my $defsfil = '';

#**************************************************************************
sub OnTextCtrl_Enc {
	my ($this, $event) = @_;

	$wx_pere::CFG->enc($this->{ENCA}->GetValue);
	++$wx_pere::CHG;
}

#**************************************************************************
sub OnSpinCtrl_Mem {
	my ($this, $event) = @_;

	$wx_pere::CFG->mem($this->{MEMS}->GetValue || 0);
	++$wx_pere::CHG;
}

#**************************************************************************
sub OnCheckBox_Case {
	my ($this, $event) = @_;

	$wx_pere::CFG->cas(($event->GetInt() != 0) ? 1 : 0);
	++$wx_pere::CHG;
}

#**************************************************************************
sub OnCheckBox_Line {
	my ($this, $event) = @_;

	$wx_pere::CFG->lin(($event->GetInt() != 0) ? 1 : 0);
	++$wx_pere::CHG;
}

#**************************************************************************
sub OnChoiceButtons_SelPfx {
	my ($this, $event) = @_;

	my $dlg = Wx::DirDialog->new(
		$this,
		"Select database directory",
		$defpfx,
		0
	);

	unless ($dlg->ShowModal == wxID_CANCEL) {
		my $path = $dlg->GetPath;
		if (defined($path) && ($path ne '')) {
			$this->{TPFX}->Clear;
			$this->{TPFX}->AppendText($path);
			$defpfx = $path;
		}
	}

	$dlg->Destroy;
}

#**************************************************************************
sub OnChoiceButtons_SelSrc {
	my ($this, $event) = @_;

	my $dlg = Wx::FileDialog->new(
		$this,
		"Select a source file",
		$defsdir,
		$defsfil,
		'All files (*.*)|*.*|Pere dictionary source (*.src)|*.src|Pere dictionary dump (*.dmp)|*.dmp',
		wxOPEN | wxFILE_MUST_EXIST | wxCHANGE_DIR
	);

	unless ($dlg->ShowModal == wxID_CANCEL) {
		my $path = $dlg->GetPath;

		if (defined($path) && ($path ne '')) {
			$defsfil = $path;
			$this->{TSRC}->Clear;
			$this->{TSRC}->AppendText($path);
			my $file = $dlg->GetFilename;
			$path =~ s/[\\\/]*$file$//;
			$defsdir = $path;
		}
	}

	$dlg->Destroy;
}

#**************************************************************************
sub OnChoice_Pair {
	my ($this, $event) = @_;

	$this->{ILNG}->Clear;
	$this->{OLNG}->Clear;
	$this->{THEM}->Clear;
	$this->{TPFX}->Clear;
	my $num = $this->{PAIR}->GetSelection;
	if ($num != wxNOT_FOUND) {
		my $dat = $this->{PAIR}->GetClientData($num);
		$this->{ILNG}->AppendText($dat->[0]);
		$this->{OLNG}->AppendText($dat->[1]);
		$this->{THEM}->AppendText($dat->[2]);
		$this->{TPFX}->AppendText($dat->[3]);
	}
}

#**************************************************************************
sub OnChoiceButtons_Add {
	my ($this, $event) = @_;

	my $pair = $this->{PAIR};

	my $iln = $this->{ILNG}->GetLineText(0);
	my $oln = $this->{OLNG}->GetLineText(0);
	my $thm = $this->{THEM}->GetLineText(0) || 'common';
	my $pfx = $this->{TPFX}->GetLineText(0);

	if (defined($iln) && ($iln ne '')) {
		if (wx_pere::getname($iln) eq $iln) {
			wx_pere::msgwarn('Unknown input language');
		}

		if (defined($oln) && ($oln ne '')) {
			if (wx_pere::getname($oln) eq $oln) {
						wx_pere::msgwarn('Unknown output language');
			}

			if (($thm ne '') && (wx_pere::getname($thm) eq $thm)) {
				wx_pere::msgwarn('Unknown theme');
			}

			if ($pfx ne '') {
				$pfx = File::Spec->rel2abs($pfx);
				if (defined($pfx) && ($pfx ne '') && (-d $pfx)) {
					my $ret = $wx_pere::CFG->pairs2add($iln, $oln, $thm, $pfx);
					if ($ret > 0) {
						my $dat = [$iln, $oln, $thm, $pfx];
						my $nam = wx_pere::row2names($dat);
						my $sel = $pair->FindString($nam);
						if ($sel == wxNOT_FOUND) {
							$pair->Append($nam, $dat);
							$pair->SetSelection($pair->GetCount-1);
						} else {
							$pair->SetClientData($sel, $dat);
							$pair->SetSelection($sel);
						}
						OnChoice_Pair($this, undef);


						$pair = $this->GetParent->GetParent->{PTRA}->{PAIR};
						$sel = $pair->FindString($nam);
						if ($sel == wxNOT_FOUND) {
							$pair->Append($nam, $dat);
						} else {
							$pair->SetClientData($sel, $dat);
						}

						++$wx_pere::CHG;
					}
				} else {
					wx_pere::msgerror('Wrong database directory');
					$this->{TPFX}->SetFocus;
				}
			} else {
				wx_pere::msgerror('Undefined database directory');
				$this->{TPFX}->SetFocus;
			}
		} else {
			wx_pere::msgerror('Undefined output language');
			$this->{OLNG}->SetFocus;
		}
	} else {
		wx_pere::msgerror('Undefined input language');
		$this->{ILNG}->SetFocus;
	}
}

#**************************************************************************
sub OnChoiceButtons_Cre {
	my ($this, $event) = @_;

	my $pair = $this->{PAIR};

	my $src = $this->{TSRC}->GetLineText(0);
	my $pfx = $this->{TPFX}->GetLineText(0);

	if ($src ne '') {
		$src = File::Spec->rel2abs($src);
		if (defined($src) && ($src ne '') && (-f $src)) {
			if ($pfx ne '') {
				$pfx = File::Spec->rel2abs($pfx);
				if (defined($pfx) && ($pfx ne '')) {
					makedir($pfx);
					if (-d $pfx) {
						my $ihn = openfile($src, 'r');
						if (defined($ihn)) {
							my $dsc = '';
							my $mag = '';
							$ihn->read($mag, 1024);
							$ihn->seek(0, 0);
							my $mim = magic2mime($mag);
							if ($mim) {
								$dsc = mime2descr($mim);
								my $fun = '';
								if ($mim eq 'application/vnd.pere.source') {
									$fun = 'src2dic';
								} elsif ($mim eq 'application/vnd.pere.dump') {
									$fun = 'dmp2dic';
								} else {
									wx_pere::msgerror(sprintf("Unsupported document type: %s", $dsc));
								}
								
								if ($fun ne '') {
									my $cfg = $wx_pere::CFG;
									my $dic = Lingua::Pere::Dict::Trans->new($pfx, 1, $cfg->mem);

									my $max = 100;
									my $dlg = Wx::ProgressDialog->new(
										'Import source',
										sprintf("Import from %s. Please wait", $dsc),
										$max,
										$this,
										wxPD_AUTO_HIDE | wxPD_APP_MODAL
									);

									my $i = 0;
									my $c = 0;
									my $prg = sub { 
										my $z = int(++$i/10000) % $max;
										if ($z != $c) {
											$dlg->Update($z);
											$c = $z;
										}
									};

									$dic->$fun($ihn, $prg);

									$dlg->EndModal(0);
									$dlg->Update($max);
									$dlg->Destroy;
									if (wx_pere::msgyesno("Translation completed.\nAdd dictionary to list?") > 0) {
										OnChoiceButtons_Add($this, $event);
									}
								}
							} else {
								wx_pere::msgerror('Unrecognized file type');
							}
						} else {
							wx_pere::msgerror(sprintf("Can not open file: %s", $src));
						}
					} else {
						wx_pere::msgerror('Can not create database directory');
						$this->{TPFX}->SetFocus;
					}
				} else {
					wx_pere::msgerror('Wrong database directory');
					$this->{TPFX}->SetFocus;
				}
			} else {
				wx_pere::msgerror('Undefined database directory');
				$this->{TPFX}->SetFocus;
			}
		} else {
			wx_pere::msgerror('Wrong source file');
			$this->{TSRC}->SetFocus;
		}
	} else {
		wx_pere::msgerror('Undefined source file');
		$this->{TSRC}->SetFocus;
	}
}

#**************************************************************************
sub OnChoiceButtons_Del {
	my ($this, $event) = @_;

	my $pair = $this->{PAIR};

	my $num = $pair->GetSelection;
	if ($num != wxNOT_FOUND) {
		my $dat = $pair->GetClientData($num);
		if (defined($dat)) {
			if ($wx_pere::CFG->pairs2del(@{$dat})) {
				wx_pere::hash2choice($pair);
				$pair->SetSelection($num);
				OnChoice_Pair($this, undef);
	
				$pair = $this->GetParent->GetParent->{PTRA}->{PAIR};
				my $sel = $pair->GetSelection;
				wx_pere::hash2choice($pair);
				$pair->SetSelection($sel);

				++$wx_pere::CHG;
			}
		}
	}
}

#**************************************************************************
sub OnChoiceButtons_Sav {
	my ($this, $event) = @_;

	$wx_pere::CFG->save;
	$wx_pere::CHG = 0;
	wx_pere::msginfo('Configuration saved');
}

#**************************************************************************
sub new {
	my $class = shift;

	my $this = $class->SUPER::new(@_);

	my $row = Wx::Rect->new(10, 0, 10, 10);
	my $uni = $row;
	#---------------------------------------------------------------------------
	my $siln = Wx::StaticText->new($this, -1, "&Input Language:", wx_pere::rect2down($row), wxDefaultSize);
	my $ilng = $this->{ILNG} = Wx::TextCtrl->new($this, -1, '', wx_pere::rect2right($siln), [60, -1]);

	my $soln = Wx::StaticText->new($this, -1, "&Output Language:", wx_pere::rect2right($ilng), wxDefaultSize);
	my $olng = $this->{OLNG} = Wx::TextCtrl->new($this, -1, '', wx_pere::rect2right($soln), [60, -1]);

	my $sthm = Wx::StaticText->new($this, -1, "&Theme:", wx_pere::rect2right($olng), wxDefaultSize);
	my $them = $this->{THEM} = Wx::TextCtrl->new($this, -1, '', wx_pere::rect2right($sthm), [160, -1]);
	
	$row = wx_pere::align2union($siln, $ilng, $soln, $olng, $sthm, $them);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $tpfx = $this->{TPFX} = Wx::TextCtrl->new($this, -1, "", wx_pere::rect2down($row), [400, -1]);
	my $bpfx = Wx::Button->new($this, -1, '&Database directory', wx_pere::rect2right($tpfx), [160, -1]);

	$row = wx_pere::align2union($tpfx, $bpfx);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $badd = Wx::Button->new($this, -1, '&Add to list', wx_pere::rect2down($row), [180, -1]);

	$row = wx_pere::align2union($badd);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $pair = $this->{PAIR} = Wx::Choice->new($this, -1, wx_pere::rect2down($row), [400, -1]);
	wx_pere::hash2choice($pair);
	$pair->SetSelection(0);
	$this->OnChoice_Pair(undef);
	my $bdel = Wx::Button->new($this, -1, '&Remove from list', wx_pere::rect2right($pair), [160, -1]);

	$row = wx_pere::align2union($pair, $bdel);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $tsrc = $this->{TSRC} = Wx::TextCtrl->new($this, -1, "", wx_pere::rect2down($row), [400, -1]);
	my $bsrc = Wx::Button->new($this, -1, '&Source file', wx_pere::rect2right($tsrc), [160, -1]);

	$row = wx_pere::align2union($tsrc, $bsrc);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $bcre = Wx::Button->new($this, -1, '&Create from source', wx_pere::rect2down($row), [180, -1]);

	$row = wx_pere::align2union($bcre);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $senc = Wx::StaticText->new($this, -1, "Text encoding:", wx_pere::rect2down($row), wxDefaultSize);
	my $enca = $this->{ENCA} = Wx::TextCtrl->new($this, -1, $wx_pere::CFG->enc, wx_pere::rect2right($senc), [80, -1]);

	my $smem = Wx::StaticText->new($this, -1, "Cache size:", wx_pere::rect2right($enca), wxDefaultSize);
	my $mems = $this->{MEMS} = Wx::SpinCtrl->new($this, -1, '', wx_pere::rect2right($smem), [80, -1]);
	$mems->SetRange(0, 256);
	$mems->SetValue($wx_pere::CFG->mem || 0);

	my $case = $this->{CASE} = Wx::CheckBox->new($this, -1, '&Case sensitive', wx_pere::rect2right($mems));
	$case->SetValue($wx_pere::CFG->cas || 0);
	$case->SetToolTip("Click here to enable case sensitive translation");

	my $line = $this->{LINE} = Wx::CheckBox->new($this, -1, '&Para as line', wx_pere::rect2right($case));
	$line->SetValue($wx_pere::CFG->lin || 0);
	$line->SetToolTip("Click here to assume paragraph as line");

	$row = wx_pere::align2union($senc, $enca, $smem, $mems, $case, $line);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------
	my $bsav = Wx::Button->new($this, -1, '&Save configuration', wx_pere::rect2down($row), [180, -1]);

	$row = wx_pere::align2union($bsav);
	wx_pere::grow4rect($uni, $row);

	#---------------------------------------------------------------------------

	my $fra = $this->GetParent->GetParent;
	$fra->SetClientSize($uni->GetWidth+20, $uni->GetHeight+60);	

	EVT_TEXT($this, $enca, \&OnTextCtrl_Enc);
	EVT_SPINCTRL($this, $mems, \&OnSpinCtrl_Mem);

	EVT_CHOICE($this, $pair, \&OnChoice_Pair);

	EVT_BUTTON($this, $bpfx, \&OnChoiceButtons_SelPfx);
	EVT_BUTTON($this, $bsrc, \&OnChoiceButtons_SelSrc);
	EVT_BUTTON($this, $badd, \&OnChoiceButtons_Add);
	EVT_BUTTON($this, $bcre, \&OnChoiceButtons_Cre);
	EVT_BUTTON($this, $bdel, \&OnChoiceButtons_Del);
	EVT_BUTTON($this, $bsav, \&OnChoiceButtons_Sav);

	EVT_CHECKBOX($this, $case, \&OnCheckBox_Case);
	EVT_CHECKBOX($this, $line, \&OnCheckBox_Line);

	return $this;
}

###########################################################################
package TranPanel;

use base qw(Wx::Panel);

use File::Spec;

use vars qw($filetypes);

use Wx qw(
	:textctrl
	:window
	:filedialog
	wxDefaultPosition
	wxDefaultSize
	wxID_CANCEL
	wxOPEN
	wxFILE_MUST_EXIST
	wxCHANGE_DIR
	wxSAVE
	wxOVERWRITE_PROMPT
	wxNOT_FOUND
	wxPD_AUTO_HIDE
	wxPD_APP_MODAL
);

use Wx::Event qw(
	EVT_BUTTON
	EVT_CHOICE
);

use Lingua::Pere::Misc::File qw(
	openfile
);
use Lingua::Pere::Misc::MIME qw(
	magic2mime
	mime2descr
);
use Lingua::Pere::Util qw(
	funcs4trans
);

$filetypes = '';

my $defidir = '';
my $defifil = '';
my $defodir = '';
my $defofil = '';


#**************************************************************************
sub OnChoiceButtons_SelIFil {
	my ($this, $event) = @_;

	my $dlg = Wx::FileDialog->new(
		$this,
		"Select an input file",
		$defidir,
		$defifil,
		$filetypes,
		wxOPEN | wxFILE_MUST_EXIST | wxCHANGE_DIR
	);

	unless ($dlg->ShowModal == wxID_CANCEL) {
		my $path = $dlg->GetPath;

		if (defined($path) && ($path ne '')) {
			$defifil = $path;
			$this->{IFIL}->Clear;
			$this->{IFIL}->AppendText($path);
			my $file = $dlg->GetFilename;
			$path =~ s/[\\\/]*$file$//;
			$defidir = $path;
		}
	}

	$dlg->Destroy;
}

#**************************************************************************
sub OnChoiceButtons_SelOFil {
	my ($this, $event) = @_;

	my $dlg = Wx::FileDialog->new(
		$this,
		"Select an output file",
		$defodir,
		$defofil,
		$filetypes,
		wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR
	);

	unless ($dlg->ShowModal == wxID_CANCEL) {
		my $path = $dlg->GetPath;

		if (defined($path) && ($path ne '')) {
			$defofil = $path;
			$this->{OFIL}->Clear;
			$this->{OFIL}->AppendText($path);
			my $file = $dlg->GetFilename;
			$path =~ s/[\\\/]*$file$//;
			$defodir = $path;
		}
	}

	$dlg->Destroy;
}

#**************************************************************************
sub OnChoice_Pair {
	my ($this, $event) = @_;

	my $pair = $this->{PAIR};
	
	my $num = $pair->GetSelection;
	if ($num != wxNOT_FOUND) {
		my $dat = $pair->GetClientData($num);
		$wx_pere::CFG->pfx($dat->[3]);
	}
}

#**************************************************************************
sub OnChoiceButtons_Translate {
	my ($this, $event) = @_;

	my $txt = $this->{IFIL}->GetLineText(0);
	if (defined($txt) && ($txt ne '')) {
		$txt = File::Spec->rel2abs($txt);
		if (defined($txt) && ($txt ne '') && (-r $txt)) {
			my $res = $this->{OFIL}->GetLineText(0);
			if (defined($res) && ($res ne '')) {
				$res = File::Spec->rel2abs($res);
				if (defined($res) && ($res ne '')) {
					if ($res ne $txt) {
						my $pair = $this->{PAIR};
						my $num = $pair->GetSelection;
						if ($num != wxNOT_FOUND) {
							my $dat = $pair->GetClientData($num);
							my $pfx = File::Spec->rel2abs($dat->[3]);
							if (defined($pfx) && ($pfx ne '') && (-d $pfx)) {
#----------------------------
								my $ihn = openfile($txt, 'r');
								if (defined($ihn)) {
									my $dsc = '';
									my $mag = '';
									$ihn->read($mag, 1024);
									$ihn->seek(0, 0);
									my $mim = magic2mime($mag);
									if ($mim) {
										$dsc = mime2descr($mim);
									} else {
										$mim = 'text/plain';
										$dsc = mime2descr($mim);
									}

									my $fus = funcs4trans();
									if (exists $fus->{$mim}) {
										my $max = 100;
										my $dlg = Wx::ProgressDialog->new(
											'Translation progress',
											sprintf("%s: %s. Please wait", wx_pere::row2names($dat), $dsc),
											$max,
											$this,
											wxPD_AUTO_HIDE | wxPD_APP_MODAL
										);

										my $i = 0;
										my $c = 0;
										my $prg = sub { 
											my $z = int(++$i/100) % $max;
											if ($z != $c) {
												$dlg->Update($z);
												$c = $z;
											}
										};

										my $cfg = $wx_pere::CFG;
										$fus->{$mim}->($ihn, $res, $prg, $pfx, $cfg->mem, $cfg->cas, $cfg->enc, $cfg->lin);

										$dlg->EndModal(0);
										$dlg->Update($max);
										$dlg->Destroy;
										wx_pere::msginfo('Translation completed');
									} else {
										wx_pere::msgerror(sprintf("Unsupported document type: %s", $dsc));
									}
								} else {
									wx_pere::msgerror(sprintf("Can not open file: %s", $txt));
								}
#----------------------------
							} else {
								wx_pere::msgerror('Wrong database path');
#								$this->{BOFL}->SetFocus;
							}
						} else {
							wx_pere::msgerror('Please select translation direction');
							$pair->SetFocus;
						}
					} else {
						wx_pere::msgerror('Input and output files are equival');
						$this->{BOFL}->SetFocus;
					}
				} else {
					wx_pere::msgerror('Wrong output file');
					$this->{BOFL}->SetFocus;
				}
			} else {
				wx_pere::msgerror('Undefined output file');
				$this->{BOFL}->SetFocus;
			}
		} else {
			wx_pere::msgerror('Wrong input file');
			$this->{BIFL}->SetFocus;
		}
	} else {
		wx_pere::msgerror('Undefined input file');
		$this->{BIFL}->SetFocus;
	}
}

#**************************************************************************
sub new {
	my $class = shift;

	my $this = $class->SUPER::new(@_);

	my $row = Wx::Rect->new(10, 0, 10, 10);
	#---------------------------------------------------------------------------
	my $ifil = $this->{IFIL} = Wx::TextCtrl->new($this, -1, "", wx_pere::rect2down($row), [400, -1]);
	my $bifl = $this->{BIFL} = Wx::Button->new($this, -1, '&Input File', wx_pere::rect2right($ifil), [160, -1]);

	$row = wx_pere::align2union($ifil, $bifl);

	#---------------------------------------------------------------------------
	my $ofil = $this->{OFIL} = Wx::TextCtrl->new($this, -1, "", wx_pere::rect2down($row), [400, -1]);
	my $bofl = $this->{BOFL} = Wx::Button->new($this, -1, '&Output File', wx_pere::rect2right($ofil), [160, -1]);

	$row = wx_pere::align2union($ofil, $bofl);

	#---------------------------------------------------------------------------
	my $pair = $this->{PAIR} = Wx::Choice->new($this, -1, wx_pere::rect2down($row), [400, -1]);
	wx_pere::hash2choice($pair);
	$pair->SetSelection(0);
	$this->OnChoice_Pair(undef);
	my $tran = Wx::Button->new($this, -1, '&Translate', wx_pere::rect2right($pair), [160, -1]);

	$row = wx_pere::align2union($pair, $tran);

	#---------------------------------------------------------------------------
	EVT_BUTTON($this, $bifl, \&OnChoiceButtons_SelIFil);
	EVT_BUTTON($this, $bofl, \&OnChoiceButtons_SelOFil);
	EVT_BUTTON($this, $tran, \&OnChoiceButtons_Translate);

	EVT_CHOICE($this, $pair, \&OnChoice_Pair);

	return $this;
}

###########################################################################
package PereFrame;

use base qw(Wx::Frame);

use Wx qw(
	:textctrl
	:window
	wxDefaultPosition
	wxDefaultSize
);
use Wx::Event qw(
	EVT_MENU
	EVT_SIZE
	EVT_MOVE
);

use vars qw($ID_ABOUT $ID_EXIT);
($ID_ABOUT, $ID_EXIT) = (10001, 10002);

#**************************************************************************
sub OnQuit {
	my ($this, $event) = @_;

	if (($wx_pere::CHG > 0) && (wx_pere::msgyesno("Configuration changed\nSave it?") > 0)) {
		$wx_pere::CFG->save;
	}

	$this->Close(1);

  $event->Skip() if ($event);
}

#**************************************************************************
sub OnSize {
	my( $this, $event ) = @_;

  $event->Skip() if ($event);
}

#**************************************************************************
sub OnMove {
	my( $this, $event ) = @_;

  $event->Skip() if ($event);
}

#**************************************************************************
sub OnAbout {
	my ($this, $event) = @_;

	wx_pere::msginfo(
		"NAME: WxPere\n".
			"VERSION: ".$wx_pere::VERSION."\n".
			"AUTHOR: Valentyn Solomko <val\@pere.org.ua>\n".
			"COPYRIGHT: Copyright (C) 2006 Valentyn Solomko, All Rights Reserved.\n".
			"LICENCE: This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.\n",
		"About WxPere",
	);
}

#**************************************************************************
sub new {
	my ($class, $frame, $tit, $pos, $siz) = @_;

	my $this = $class->SUPER::new($frame, -1, $tit, $pos, $siz);

	$this->SetIcon(Wx::GetWxPerlIcon());

  my $mfile = Wx::Menu->new();
	$mfile->Append($ID_EXIT, "E&xit\tAlt-X", "Quit this program");

  my $mhelp = Wx::Menu->new();
	$mhelp->Append($ID_ABOUT, "&About...\tCtrl-A", "Show about dialog");

	my $mbar = Wx::MenuBar->new();

	$mbar->Append($mfile, "&File");
	$mbar->Append($mhelp, "&Help");

	$this->SetMenuBar($mbar);

	EVT_MENU($this, $ID_EXIT, \&OnQuit);
	EVT_MENU($this, $ID_ABOUT, \&OnAbout);
	EVT_SIZE($this, \&OnSize );
	EVT_MOVE($this, \&OnMove );

	my $note = $this->{NOTE} = Wx::Notebook->new($this, -1);

	# Translation
	my $ptra = $this->{PTRA} = TranPanel->new($note, -1);
	$note->AddPage($ptra, "Translation", 0, -1);

	# Configuration
	my $pcfg = $this->{PCFG} = ConfPanel->new($note, -1);
	$note->AddPage($pcfg, "Configuration", 1, -1);

  return $this;
}

###########################################################################
package PereApp;

use base qw(Wx::App);

use Wx qw(
	:frame
	:window
);

#**************************************************************************
sub OnInit {
	my ($this) = @_;

	my $frame = PereFrame->new(
		undef, 
		"WxPere", 
		Wx::Point->new(100, 250), 
		Wx::Size->new(600, 350), 
		(wxDEFAULT_FRAME_STYLE | wxICONIZE) & ~ (wxRESIZE_BORDER | wxMAXIMIZE_BOX)
	);

	$wx_pere::FRA = $frame;

	$this->SetTopWindow($frame);
	$frame->Show(1);

	return 1;
}

###########################################################################
package wx_pere;

use Lingua::Pere::Config;
use Lingua::Pere::Util qw(
	funcs4trans
);
use Lingua::Pere::Misc::MIME qw(
	mime2descr
	mime2ext
);

#**************************************************************************
sub doit {
	$| = 1;

	$CHG = 0;

	while(<DATA>) {
		chomp;
		s/^\s+//;
		s/\s+$//;
		s/\s+/ /g;
		next if ($_ eq '');

		my ($k, @a) = split(m/\s+/, $_);
		$NAM{$k} = join(' ', @a);
	}

	$CFG = Lingua::Pere::Config->new('pere-trans.config');
	$CFG->def('pfx', '');

	my $fus = funcs4trans();
	my @fta = ();
	push(@fta, 'All files (*.*)|*.*');
	foreach my $mim (sort keys %{$fus}) {
		my $ext = mime2ext($mim);
		if (scalar(@{$ext}) > 0) {
			push(@fta, sprintf("%s (*.%s)|*.%s", mime2descr($mim), $ext->[0], $ext->[0]));
		}
	}
	$TranPanel::filetypes = join("|", @fta);

	my $app = PereApp->new();
	$app->MainLoop;
}

#**************************************************************************
&doit;
exit(0);
#**************************************************************************
__DATA__
uk-ua	Ukrainian
be-by	Belarusian
ru-ru	Russian
pl-pl	Polish
en-us	English
bg-bg	Bulgarian
cs-cz	Czech
da-dk	Danish
de-de	German
el-gr	Greek
es-es	Spanish
et-ee	Estonian
fi-fi	Finnish
fr-fr	French
hr-hr	Croatian
hu-hu	Hungarian
is-is	Icelandic
it-it	Italian
lt-lt	Lithuanian
lv-lv	Latvian
mk-mk	Macedonian
nl-nl	Dutch
pt-pt	Portuguese
ro-ro	Romanian
sk-sk	Slovak
sl-si	Slovenian
sr-sp	Serbian
sv-se	Swedish

common           Common
soc-polit        Social&Political
soc-polit-combin Social&Political Combined
