#!/usr/bin/perl -w
use IPC::Run qw( run timeout );

my $suscript = '/usr/sbin/translations-enus1';

my @locales;
my %locale_to_domain_to_key_to_value = ();
my %key_to_domain_to_locale = ();
my %value_to_key_domain_locale = ();

sub zenity {
 my @args = @_;
 open (ZENITY, '-|', '/usr/bin/zenity', @args) || return;
 my $result = '';
 while (my $line = <ZENITY>) {
  $result .= $line;
 }
 close ZENITY;
 return $result;
}

sub getlocales {
 my @localedirs = </usr/share/locale/*/LC_MESSAGES>;
 my $dircount = scalar @localedirs;
 for (my $i = 0; $i < $dircount; ++$i) {
  my $progress = (100 * $i / $dircount) >> 0;
  my $localedir = $localedirs[$i];
  print STDERR "progress: $progress\n";
  $localedir =~ m!/usr/share/locale/(.*)/LC_MESSAGES!;
  my $locale = $1;
  $locale_to_domain_to_key_to_value{$locale} = ();
  push @locales, $locale;
  my %localespace = ();
  foreach my $mo (<$localedir/*.mo>) {
   $mo =~ m!.*/(.*?)\.mo!;
   my $domain = $1;
   $locale_to_domain_to_key_to_value{$locale}{$domain} = ();
   my %domain = ();
   my $text = `msgunfmt $mo 2> /dev/null`;
   my @lines = split /\n/, $text;
   my $line = shift @lines;
   while (defined $line) {
    while (defined $line &&
           ($line eq '' ||
            $line !~ /msgid ".*"/)) {
     $line = shift @lines;
    }
    last unless defined $line;
    $line =~ /msgid "(.*)"/;
    my $key = $1;
    $line = shift @lines;
    if ($key eq '') {
     while (defined $line &&
            $line =~ /^"(.*)"$/) {
      $key .= $1;
      $line = shift @lines;
     }
    }
    $line =~ /msgstr "(.*)"$/;
    $value = $1;
    while (($line = shift @lines) &&
           ($line =~ /^"(.*)"$/)) {
     $value .= $1;
    }
    $domain{$key} = $value;
    $key_to_domain_to_locale{$key} = () unless defined $key_to_domain_to_locale{$key};
    $key_to_domain_to_locale{$key}{$domain} = () unless defined $key_to_domain_to_locale{$key}{$domain};
    $key_to_domain_to_locale{$key}{$domain}{$locale} = $value;
    $locale_to_domain_to_key_to_value{$locale}{$domain}{$key} = $value;
    $value_to_key_domain_locale{$value} = () unless defined $value_to_key_domain_locale{$value};
    $value_to_key_domain_locale{$value}{"$key$domain$locale"} = [$key, $domain, $locale];
   }
  }
 }
 print "progress: 100\n";
}

my ($ipc_control, $ipc_status, $ipc_err);

sub zprogress {
 open (ZENITY, '|-', '/usr/bin/zenity', '--progress', '--title=Processing locales', '--text=Please wait');
 if (1) {
  close (ZENITY);
  open (ZENITY, '|-', '/bin/cat');
 }
 my $line = 1;
 while ($line) {
  print "<$ipc_status>\n";
  $line = <$ipc_status>;
  print ">> $line\n";
  last unless $line =~ /progress: (\d+)/;
  my $progress = $1;
  print ZENITY "$progress\n";
  print STDERR "Got progress: $progress\n";
 }
 close ZENITY;
}

sub zquote {
 my @line = ();
 foreach my $str (@_) {
  push @line, '', next unless defined $str;
  $str =~ s/([\\\$"'])/\\$1/g;
  push @line, $str;
 }
 return @line;
}

sub getinput {
 my ($title, $prompt) = @_;
 my $input = zenity('--entry',
                    "--title=$title",
                    "--text=$prompt");
 chomp $input;
 return $input;
}

sub warning {
 my ($message) = @_;
 zenity('--warning',
        "--text=$message");
}

sub choice {
 my ($title, $prompt, @pairs) = @_;
 my @options = ();
 while ((scalar @pairs) > 1) {
  $key = shift @pairs;
  $value = shift @pairs;
  push @options, zquote($key, $value);
 } 
 my $action = zenity('--list',
                     "--title=$title",
                     qw(--text= --hide-column=1 --column=),
                     "--column=$prompt",
                     @options);
 chomp $action;
 return $action;
}

sub lookup {
 my $value = getinput("Retrieve identifier", "Enter user interface string");
 return unless $value ne '';
 unless (defined $value_to_key_domain_locale{$value}) {
  warning("No matching string");
  return;
 }
 my @tripples = ();
 my $id = 0;
 my @options = ();
 foreach $key (keys %{$value_to_key_domain_locale{$value}}) {
  my @tripple = @{$value_to_key_domain_locale{$value}{$key}};
  push @tripples, ++$id, $tripple[2], $tripple[1], $tripple[0];
  push @options, zquote($id, $tripple[2], $tripple[1], $tripple[0]);
 }
 my $row = zenity('--list',
                  "--title=Matching identifiers",
                  "--text=For: $value",
                  qw(--hide-column=1 --column= --column=Locale --column=Domain --column=ID),
                  @options);
}

sub view {
 my $key = getinput("Review identifier translations", "Enter identifier");
 return unless $value ne '';
 unless (defined $key_to_domain_to_locale{$key}) {
  warning("No such identifier");
  return;
 }
 my @tripples = ();
 my $id = 0;
 my @options = ();
 foreach $domain (keys %{$key_to_domain_to_locale{$key}}) {
  foreach $locale (keys %{$key_to_domain_to_locale{$key}{$domain}}) {
   my $value = $key_to_domain_to_locale{$key}{$domain}{$locale};
   push @tripples, ++$id, $locale, $domain, $value;
   push @options, zquote($id, $locale, $domain, $value);
  }
 }
 my $row = zenity('--list',
                  "--title=Matching strings",
                  "--text=For: $key",
                  qw(--hide-column=1 --column= --column=Locale --column=Domain --column=Value),
                  @options);
}

sub browse {
 my @loptions = ();
 for my $locale (sort keys %locale_to_domain_to_key_to_value) {
  push @loptions, zquote($locale);
 }
 my $locale;
 while (($locale = zenity('--list',
                          '--title=Select locale',
                          qw(--text= --column=Locale),
                          @loptions)) ne '') {
  chomp $locale;
  my %domain_to_key_to_value = %{$locale_to_domain_to_key_to_value{$locale}};
  my @doptions = ();
  for my $domain (sort keys %domain_to_key_to_value) {
   push @doptions, zquote($domain);
  }
  my $domain;
  while (($domain = zenity('--list',
                           '--title=Select domain',
                           "--text=Locale ($locale)",
                           '--column=Domain',
                           @doptions)) ne '') {
   chomp $domain;
   my %key_to_value = %{$domain_to_key_to_value{$domain}};
   my @koptions = ();
   my @key_values = ();
   for my $key (sort keys %key_to_value) {
    my $value = $key_to_value{$key};
    push @key_values, $key, $value; 
    push @koptions, zquote($key, $value);
   }
   zenity('--list',
          "--title=Domain: $domain",
          "--text=Locale: $locale",
          qw(--column=ID --column=Value),
          @koptions);
  }
 }
}

sub searchids {
 my $text = getinput("Search Localization IDs", "Enter substring");
 return unless $text ne '';
 my @rows = ();
 my @list = sort grep (/\Q$text\E/, keys %key_to_domain_to_locale);
 foreach my $key (@list) {
  next unless defined $key_to_domain_to_locale{$key};
  my %domain_to_locale = %{$key_to_domain_to_locale{$key}};
  foreach my $domain (sort keys %domain_to_locale) {
   my %locale_to_string = %{$domain_to_locale{$domain}};
   foreach my $locale (sort keys %locale_to_string) {
    push @rows, zquote($locale, $domain, $key, $locale_to_string{$key});
   }
  }
 }
 zenity('--list',
        "--title=ID Search",
        "--text=For: $text",
        qw(--column=Locale --column=Domain --column=ID --column=Value),
        @rows);
}

sub searchstrings {
 my $text = getinput("Search Localization Messages", "Enter substring");
 return unless $text ne '';
 my @rows = ();
 my @list = sort grep (/\Q$text\E/, keys %value_to_key_domain_locale);
 foreach my $value (@list) {
  next unless defined $value_to_key_domain_locale{$value};
  my %token_to_key_domain_locale = %{$value_to_key_domain_locale{$value}};
  foreach my $token (sort keys %token_to_key_domain_locale) {
   my ($key, $domain, $locale) = @{$token_to_key_domain_locale{$token}};
   push @rows, zquote($locale, $domain, $key, $locale_to_domain_to_key_to_value{$locale}{$domain}{$key});
  }
 }
 zenity('--list',
        '--title=Translation Search',
        "--text=For: $text",
        qw(--column=Locale --column=Domain --column=ID --column=Value),
        @rows);
}

sub transtools {
 my $action;
 do {
  $action=choice("Translator Tool", "What would you like to do?",
                 "searchstrings", "Search within translations",
                 "searchids", "Search within identifiers",
                 "lookup", "Lookup identifier",
                 "read", "View identifier translations",
                 "browse", "Browse translations");
  for ($action) {
   /^lookup$/ && lookup();
   /^read$/ && view();
   /^browse$/ && browse();
   /^searchids$/ && searchids();
   /^searchstrings$/ && searchstrings();
  }
 } while ($action ne '');
}

my $apppath = $0;
my $childpid;
sub main {
 my @cmd = ($apppath, 'daemon');
 $childpid = run(\@cmd, '<pty<', \$ipc_control, '>pty>', \$ipc_status);
 print ">> $childpid \n";
 my @zenityargs = qw(--list --radiolist --title);
 my ($new, $old) = split /\n/, `$suscript status`;
 my $default = $new == 0 ? 'TRUE' : 'FALSE';
 my $native = $old == 0 ? 'TRUE' : 'FALSE';
 push @zenityargs, "Select preferred source for English",
                   '--text',
                   '',
                   '--column=',
                   '--column=Provider',
                   $default,
                   'Default',
                   $native,
                   'Native Speaker',
                   'FALSE',
                   'Advanced options';
 push @zenityargs, 'TRUE', "Mixed (Default: $old, Native: $new)" if $new && $old;
 my $provider = zenity(@zenityargs);
 exit unless defined $provider;
 chomp $provider;

 my $action = "";
 if ($provider eq 'Native Speaker') {
   $action = "install";
 } elsif ($provider eq 'Default') {
   $action = "uninstall";
 } elsif ($provider eq 'Advanced options') {
   zprogress();
   print $ipc_control "Go!\n";
   close $ipc_status;
   close $ipc_control;
   close $ipc_err;
   waitpid ($childpid, &WNOHANG);
   exit;
 } else {
   exit;
 }
 system('/usr/bin/sudo', $suscript, $action);
}

sub ipc {
 getlocales();
 my $cmd;
 while ($cmd = <STDIN>) {
  transtools();
 }
}

if (scalar @ARGV) {
 shift;
 ipc();
} else {
 main();
}
