#! /usr/bin/perl

use Getopt::Long;

sub help;
sub read_po;
sub join_id;
sub new_tmp_file;
sub cleanup;

GetOptions(
  'help' => \&help,
  'compress' => \$opt_compress,
);

END { cleanup }     
$SIG{INT} = \&cleanup;
$SIG{TERM} = \&cleanup;

my @tmp_files;

$espeak = "espeak";

$magic = 0x692741e8;

help if @ARGV != 2;

$po = read_po $ARGV[0];

$tmp_txt = new_tmp_file;
$tmp_wav = new_tmp_file;
$tmp_snd = new_tmp_file;

for $msg (@{$po}) {
  $id = join_id $msg, 'id';
  $str = join_id $msg, 'str';

  next unless $id ne "";

  eval '$id = "' . $id . '"';
  eval '$str = "' . $str . '"';

  $str = $id if $str eq "";

  if(!$snd{$str}) {
    open W, ">$tmp_txt";
    print W $str;
    close W;

    $punc = length($id) == 1 ? "--punc" : "";

    system "$espeak $punc -f $tmp_txt -w $tmp_wav";
    system "sox $tmp_wav -b 8 -e unsigned-integer -c 1 -r 16000 -t .wav $tmp_snd";
    if($opt_compress) {
      system "./sc $tmp_snd $tmp_snd";
    }

    open F, $tmp_snd;
    sysread F, $snd_buf, -s $tmp_snd;
    close F;

    $snd{$str} = $snd_buf;
  }

  $snd_id{$id} = $str;
}

$file_ofs = 0;
@snds = sort keys %snd;
@snd_ids = sort keys %snd_id;

for $snd_id (@snd_ids) {
  $txt_ofs{$snd_id} = $file_ofs;
  $file_ofs += length($snd_id) + 1;
}

for $snd (@snds) {
  $snd_ofs{$snd} = $file_ofs;
  $file_ofs += length($snd{$snd});
}

$head_size = 8 + 8 * @snd_ids;

open W, ">$ARGV[1]";

print W pack("VV", $magic, scalar(@snd_ids));
for $snd_id (@snd_ids) {
  print W pack("VV", ($txt_ofs{$snd_id} + $head_size, $snd_ofs{$snd_id{$snd_id}} + $head_size));
}
for $snd_id (@snd_ids) {
  print W $snd_id, "\x00";
}
for $snd (@snds) {
  print W $snd{$snd};
}

close W;


sub help
{
  print STDERR
  "Usage: po2talk [options] po_file talk_file\n" .
  "Run po file through espeak.\n";

  exit 0;
}


sub read_po
{
  local $_;
  my ($msg, $cnt, $id, $f);

  $cnt = 0;

  open F, $_[0];
  while(<F>) {
    s/^\s*|\s*$//g;
    next if $_ eq "";
    if(/^#,/) {
      s/^#,\s*//;
      for $f (split /\s*,\s*/) {
        ${$msg->[$cnt]{flags}{$f}} = 1 if $f ne "";
      }
      next
    }
    if(/^#(\s|$)/) { push @{$msg->[$cnt]{u_comment}}, $_ ; next }
    if(/^#/) { push @{$msg->[$cnt]{a_comment}}, $_; next }
    if(s/^msg(id\b|id_plural\b|str(\[\d+\])?)\s*//) {
      $id = $1;
      if($id eq 'id') {
        $msg->[$cnt]{line} = $. unless exists $msg->[$cnt]{$line};
        $cnt++;
      }
    }
    if($cnt && /^"(.*)"$/) {
      push @{$msg->[$cnt - 1]{$id}}, $1;
    }
    else {
      print STDERR "$_[0]:$.: invalid po format\n";
      return undef;
    }
  }
  close F;

  # trailing comments
  pop @$msg if @$msg > 0 && !$msg->[-1]{id};

  return $msg;
}


sub join_id
{
  my ($msg, $id);

  ($msg, $id) = @_;

  return join '', @{$msg->{$id}};
}


# create new temporary file
sub new_tmp_file
{
  local $_;

  chomp ($_ = `mktemp /tmp/po2talk.XXXXXXXXXX`);
  die "error: mktemp failed\n" if $?;

  push @tmp_files, $_;

  return $_;
}


# remove temporary files
sub cleanup
{
  local $_;

  for (@tmp_files) {
    next unless defined $_;
    unlink;
    $_ = undef;
  }

  undef @tmp_files;
}

