#                                                         -*- Perl -*-
# Copyright (c) 2007, 20008  Kazuhiro Ito
#
# 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.
# 
# 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.
#

require 5.005;

use English;
use FreePWING::ColorGraphic;
use FreePWING::FPWUtils::FPWUtils;
use Getopt::Long;
use warnings;
use FileHandle;
use Compress::Raw::Zlib;

use vars qw ($name_pos $name_size $content_pos $content_size);
use vars qw ($unzipped $zipped $last_unzipped $last_zipped);
use vars qw ($contents);
use vars qw ($content_handle $content_index_handle);
use vars qw ($index_handle $name_handle);
use vars qw (%fpwoald7_conf @tags_table);

 MAIN: {
   my %tag_table;

#
# コマンド行を解析する。
#
   my ($srcdir, $conf_file);

   if (!GetOptions('workdir=s' => \$work_directory, 
		   'srcdir=s' => \$srcdir, 
		   'conf=s' => \$conf_file)) {
     exit 1;
   }

   require $conf_file;

   if (($fpwoald7_conf{'image_type'} == 2)
       || (($fpwoald7_conf{'image_type'} == 1)
	   && ($fpwoald7_conf{'image_max_width'}
	       || $fpwoald7_conf{'image_max_height'}))) {
     require Image::Magick;
   }

#
# fpwutils を初期化する。
#
   initialize_fpwutils();

#
# これから出力するファイルがすでにあれば、削除する。
#
   unlink($color_graphic_file_name);
   unlink($color_graphic_tag_file_name);

   $graphic = FreePWING::ColorGraphic->new();

#
# 生成側ファイルを開く。
#
   if (!$graphic->open($color_graphic_file_name,
		       $color_graphic_tag_file_name)) {
     die "$PROGRAM_NAME: " . $graphic->error_message() . "\n";
   }

   if($fpwoald7_conf{'image_type'} == 0) {
     goto FINISH;
   }

#
# 読み込みファイルを開く
#
   $srcdir =~ s/([^\/])$/$1\//;

   my $tmp;
   my ($content, $content_name);
   my $output_handle;
   my ($tag_name, $image_name);

   initialize_content_reader($srcdir);

   while (1){
     ($content_name, $content) = get_next_content();

     if (!length($content_name)) {
       last;
     }

     if(($content_name =~ s/^(.+)\.htm$/$1/)
	&& ($content =~ /\/([^\/]+)\.jpg/)) {
       $image_name = $1;
       if (defined($tag_table{$image_name})) {
	 push (@{$tag_table{$image_name}}, $content_name)
       } else {
	 $tag_table{$image_name} = [$content_name];
       }
     }
   }

   finalize_content_reader($srcdir);
   initialize_content_reader($srcdir);

   while (1) {
     ($content_name, $content) = get_next_content();

     if (!length($content_name)) {
       last;
     }

     if (($content_name =~ /(.+)\.jpg$/) &&
	 (defined($tag_table{$1}))) {
       $tag_name = $1;

#
# Resize and convert image if needed.
#

       if (($fpwoald7_conf{'image_type'} == 2)
	   || $fpwoald7_conf{'image_max_width'}
	   || $fpwoald7_conf{'image_max_height'}) {
	 $content = get_resized_image($content);
       }

#
# カラー図版データを追加する。
#
       foreach $tmp (@{$tag_table{$tag_name}}) { 
	 if (!$graphic->add_binary($tmp, $content)) {
	   die "$PROGRAM_NAME: " . $graphic->error_message() . "\n";
	 }
	 if (verbose_mode()) {
	   print "$tmp -> $content_name\n";
	 }
       }
     }
   }
   finalize_content_reader();

#
# カラー図版の生成側ファイルを閉じる。
# 
   printf ("%6d images ard registered.\n", $graphic->entry_count());
 FINISH:
   if (!$graphic->close()) {
     die "$PROGRAM_NAME: " . $graphic->error_message() . "\n";
   }

#
# fpwutils の後始末をする。
#
   finalize_fpwutils();

   exit 0;
}

sub initialize_content_reader {
  my $srcdir = $_[0];
  my $content_filename = $srcdir.'CONTENT.tda';
  my $content_index_filename = $srcdir.'CONTENT.tda.tdz';
  my $index_filename = $srcdir.'files.dat';
  my $name_filename = $srcdir.'NAME.tda';
  my $tmp;

  $content_handle = new FileHandle;
  if (!$content_handle->open("$content_filename", 'r')) {
    die "$PROGRAM_NAME: Failed to open the file, $ERRNO: $content_filename\n";
  }
  binmode $content_handle;
    
  $content_index_handle = new FileHandle;
  if (!$content_index_handle->open("$content_index_filename", 'r')) {
    die "$PROGRAM_NAME: Failed to open the file, $ERRNO: $content_index_filename\n";
    }
  binmode $content_index_handle;
    
  $index_handle = new FileHandle;
  if (!$index_handle->open("$index_filename", 'r')) {
    die "$PROGRAM_NAME: Failed to open the file, $ERRNO: $index_filename\n";
  }
  binmode $index_handle;

  $name_handle = new FileHandle;
  if (!$name_handle->open("$name_filename", 'r')) {
    die "$PROGRAM_NAME: Failed to open the file, $ERRNO: $name_filename\n";
  }
  binmode $name_handle;

  ($name_size, $content_size) = (0, 0);
  ($unzipped, $zipped) = (0, 0);
  ($last_zipped, $last_unzipped) = (0, 0);

  ($name_pos, $content_pos) = get_next_index();
  if($name_pos == -1) {
    die "$PROGRAM_NAME: Unexpected index end.\n";
  }

  return 0;
}

sub finalize_content_reader {
  $content_handle->close();
  $content_index_handle->close();
  $index_handle->close();
  $name_handle->close();

  return 0;
}

sub get_next_index {
  my ($name, $content, $tmp);
  if (read($index_handle, $tmp, 10) == 10) {
    ($name, $content) = unpack("vxxV", $tmp);
  } else {
    # 最終エントリ
    ($name, $content) = (-1, -1);
  }

  return ($name, $content);
}

sub get_next_content {
  my $tmp;
  my ($inflater, $status);
  my ($content_name, $content);

  if($name_size == -1) {
    return ('', '');
  }

  $name_pos += $name_size;
  $content_pos += $content_size;

  ($name_size, $content_size) = get_next_index();
    
  if ($name_size != -1) {
    $name_size -= $name_pos;
    $content_size -= $content_pos;
  }

  if ($unzipped + $last_unzipped <= $content_pos) {
    if (read($content_index_handle, $tmp, 8) != 8) {
      die "$PROGRAM_NAME: Unexpected index structure.\n";
    }
    $unzipped += $last_unzipped;
    ($last_unzipped, $last_zipped) = unpack("VV", $tmp);
    if (read($content_handle, $tmp, $last_zipped) != $last_zipped) {
      die "$PROGRAM_NAME: failed to read file\n";
    }
    ($inflater, $status) = new Compress::Raw::Zlib::Inflate();
    if ($status != Z_OK) {
      die "$PROGRAM_NAME: Failed to initialize inflater\n";
    }
    $status = $inflater->inflate($tmp, $contents);
    if ($status != Z_OK && $status != Z_STREAM_END) {
      die "$PROGRAM_NAME: Failed to inflate\n";
    }
  }

  # ファイル名を得る。
  if ($name_size != -1) {
    if (read($name_handle, $content_name, $name_size) != $name_size) {
      die "$PROGRAM_NAME: failed to read file\n";
    }
  } else {
    if (read($name_handle, $content_name, 1024) == 0) {
      die "$PROGRAM_NAME: failed to read file\n";
    }
  }
  $content_name = substr($content_name, 0, -1);

  # コンテントを得る。
  if ($content_size != -1) {
    $content = substr($contents, $content_pos - $unzipped, $content_size - 1);
  } else {
    $content = substr($contents, $content_pos - $unzipped, -1);
  }

  return ($content_name, $content);
}

sub get_resized_image {
  my ($image) = @_;
  my $new_image = Image::Magick->new(magick=>'jpg');

  if ($new_image->BlobToImage($image) != 1) {
    print "warning: blob conversion was failed.\n";
    return $image;
  }

  if ($fpwoald7_conf{'image_max_width'}
      || $fpwoald7_conf{'image_max_height'}) {
    my ($width, $height) = $new_image->Get('width', 'height');
    $scale = min ($fpwoald7_conf{'image_max_width'} ?
		  $fpwoald7_conf{'image_max_width'} / $width : 1,
		  $fpwoald7_conf{'image_max_height'} ?
		  $fpwoald7_conf{'image_max_height'} / $height : 1);
    $scale = min ($scale, 1);
    if(debug_mode()) {
      print "scale factor is $scale.\n";
    }

    if ($scale != 1) {
      $width = int($width * $scale);
      $width = $width ? $width : 1;
      $height = int($height * $scale);
      $height = $height ? $height : 1;

      $new_image->Resize(geometry=>"$width"."x$height");
      if(verbose_mode()) {
	print "image is resized to $width x $height.\n";
      }
    }
  }

  if ($fpwoald7_conf{'image_type'} == 1) {
    return $new_image->ImageToBlob();
  } else {
    return $new_image->ImageToBlob(magick=>'bmp');
  }
}

sub min {
  return   ($_[0] < $_[1]) ? $_[0] : $_[1];
}
