#!/usr/bin/perl
#
# Generate covers for books to use as cover in ePUBs
# 
# This program is copyright 2012, 2017 by Javier Fernandez-Sanguino <jfs@computer.org>
#
#    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# For more information please see
#  http://www.gnu.org/licenses/licenses.html#GPL


use strict;
use warnings;

use File::Path qw(mkpath);

my $PROGRAM_NAME    = 'gbtoepub';
my $USAGE           = "$PROGRAM_NAME [options] book-code\n\t--meta=[metadata file]\n\t--xml=[book XML]\n\t--language=[language area of input data (output determined by meta file)]\n\t--font-files=[font-files]\n\t--no-validate\n\t--verbose\n";

my $FILENAME_SEPARATOR = '/';

my $CONVERT    = qx{which convert};
my $CP         = qx{which cp};
my $MV         = qx{which mv};
my $RM         = qx{which rm};
my $CHMOD      = qx{which chmod};

chomp $CONVERT;
chomp $CP;
chomp $MV;
chomp $RM;
chomp $CHMOD;

# Check that all the binaries are were want them

my @BINARIES;
push @BINARIES, ($CONVERT, $CP, $MV, $RM, $CHMOD);

foreach (@BINARIES) {
    if ( ! -e $_ ) {
        die "$PROGRAM_NAME: Cannot find binary '".$_."'. Please install it.\n";
    }
}

###

my $OEBPS_DIR      = 'OEBPS';
my $META_INF_DIR   = 'META-INF';


###

my $bookCode     = '';
my $metaFile     = '';
my $bookXML      = '';
my $fontFiles    = "common/fonts";
my $language     = 'en';

my $verbose = 0;
my $noValidate = 0;

### read command line options

while( $#ARGV > -1 ) {
    my $cmdLineItem = shift @ARGV;
    if( $cmdLineItem =~ /^--meta=(.+)$/ ) {
        $metaFile = $1;
    }
    elsif( $cmdLineItem =~ /^--xml=(.+)$/ ) {
        $bookXML = $1;
    }
    elsif( $cmdLineItem =~ /^--language=(.+)$/ ) {
        $language = $1;
    }
    elsif( $cmdLineItem =~ /^--verbose/ ) {
        $verbose = 1;
    }
    elsif( $cmdLineItem =~ /^--font-files=(.+)$/ ) {
        $fontFiles = $1;
    }
    else { 
        $bookCode = $cmdLineItem;
    }
}

if( $bookCode eq '' ) { 
    die "$PROGRAM_NAME: Unspecified book code\n$USAGE";
}
if( $metaFile eq '' ) { $metaFile = "$language/.publisher/rules/epub"; }
if( $bookXML eq '' ) { $bookXML = "$language/xml/$bookCode.xml"; }


### read in metadata file

unless( -e $metaFile && -f $metaFile && -r $metaFile ) {
    die qq{$PROGRAM_NAME: Improper metadata file "$metaFile"\n};
}

open( META, '<', $metaFile ) or 
    die qq{$PROGRAM_NAME: Unable to open metadata file "$metaFile": $!\n};

my $meta = '';
while( my $line = <META> ) {
    $meta .= $line if $line !~ /^[[:space:]]*#/;
}
close META;

### interpret rules from metadata
my $rulesString = '';
if( $meta =~ /^[[:space:]]*$bookCode[[:space:]]*{([^}]*)}/sm ) {
    $rulesString = $1;
}
else {
    die "$PROGRAM_NAME: Book code ($bookCode) not found in metadata file or invalid file syntax\n";
}

my @rules = split( /[[:space:]\n]*;[[:space:]\n]*/, $rulesString );
my %rulesHash;
foreach my $rule (@rules) {
    if( $rule =~ /[[:space:]]*([^:]+)[[:space:]]*:[[:space:]]*(.+)$/s ) {
	$rulesHash{ $1 } = $2;
    }
    else {
	die "$PROGRAM_NAME: Unrecognized rule syntax:\n$rule\n";
    }
}

unless( defined $rulesHash{'book-series'} ) {
    die "$PROGRAM_NAME: no book series set\n";
}

my $SERIES = get_series($rulesHash{'book-series'}) ;
my $SERIES_NUMBER = get_series_number($bookCode);


### create output directories

my %outPath;
$outPath{'top'} = $rulesHash{'language'} . $FILENAME_SEPARATOR .
                     'epub' . $FILENAME_SEPARATOR .
                     $rulesHash{'book-series'} . $FILENAME_SEPARATOR .
                     $bookCode;

$outPath{'meta-inf'} = $outPath{'top'} . $FILENAME_SEPARATOR . $META_INF_DIR;
$outPath{'oebps'} = $outPath{'top'} . $FILENAME_SEPARATOR . $OEBPS_DIR;

foreach my $directory (keys(%outPath)) {
    unless( -e $outPath{$directory} && -d $outPath{$directory} ) {
        mkpath $outPath{$directory}
            or die "$PROGRAM_NAME: Unknown error creating output directory " .
                   "\"$outPath{$directory}\"\n";
    }
}

### create content files


## write coverpage 

# Generate the cover image. This can be done in two ways:
# 1.- A file is available under the directory of JPEG files for the book
# 2.- A file is generated using imagemagick

# Cover filename
my $coverImage = $outPath{'oebps'} . $FILENAME_SEPARATOR . "cover.jpg"; 
# Cover filename generated by Project Aon
my $pa_coverImage = $rulesHash{'language'} . $FILENAME_SEPARATOR . "jpeg" . $FILENAME_SEPARATOR .$rulesHash{'book-series'}. $FILENAME_SEPARATOR .$bookCode . $FILENAME_SEPARATOR . "cover.jpg";

if ( -e  "$pa_coverImage") {
    # Copy the file here
    print STDERR "DEBUG: Using cover from $pa_coverImage\n" if $verbose;
    system "cp $pa_coverImage $coverImage";
} else {

# Use Imagemagick to generate the cover page

    print STDERR "DEBUG: Will generate cover with ImageMagick\n" if $verbose;
    my $TITLE = quote_shell(find_title($bookXML));
    my $AUTHOR = quote_shell(find_author($bookXML));
    my $ILLUSTRATOR = quote_shell(find_illustrator($bookXML));
    my $convert_cmd = "";

    if ( -e "$fontFiles/SouvenirStd-Demi.otf" && -e "$fontFiles/SouvenirStd-Light.otf" ) { 
        $convert_cmd="$CONVERT -size 600x800 -background white  -font $fontFiles/SouvenirStd-Demi.otf -pointsize 32 -fill '#006633' -gravity north caption:\"\" -annotate +0+218 \"$TITLE\"  -font $fontFiles/SouvenirStd-Light.otf -pointsize 22 -fill black -annotate +0+304 '$AUTHOR' -annotate +0+333 '$ILLUSTRATOR' $coverImage"
    } else {
        print STDERR "WARN: Fontfiles not found, using standard font\n";
        $convert_cmd="$CONVERT -size 600x800 -background white -pointsize 32 -fill '#006633' -gravity north caption:\"\" -annotate +0+218 \"$TITLE\"  -pointsize 22 -fill black -annotate +0+304 '$AUTHOR' -annotate +0+333 '$ILLUSTRATOR' $coverImage";
    }

    print STDERR "DEBUG: Will run '$convert_cmd'\n" if $verbose;
    system $convert_cmd;
}


exit 0;

################################################################################
# Subroutines
################################################################################


# Determine series long name by the series acronym
sub get_series {
    my ($series) = @_;
    my $series_name = "";
    if ($series eq "lw" ) {
        $series_name = "Lone Wolf";
    } elsif ($series eq "ls" ) {
        $series_name = "Lobo Solitario";
    } elsif ($series eq "gs" ) {
        $series_name = "Grey Star the Wizard";
    } elsif ($series eq "fw" ) {
        $series_name = "Freeway Warrior";
    } else {
        print STDERR "WARN: Undefined series. Short name given: '$series'\n";
        $series_name = "[undefined]";
    }
    return $series_name;
}

# Determine the series number based on book code
sub get_series_number {
    my ($bookCode) = @_;
    my $series_number = "";
    if ( $bookCode =~ /^(\d\d)/ ) {
        $series_number = $1;
    } else {
        print STDERR "WARN: Undefined series number. Book code is '$bookCode'.\n";
        $series_number = "xx";
    }
    return $series_number;
}

# Determine the book title by reading the book meta information
sub find_title {
    my ($book) = @_;
    my $title = ""; my $line = "";
    open (BOOK, "head -100 $book | ") || die ("Could not read $book: $!");
    while ($title eq "" && ( $line = <BOOK> ) ) {
        chomp $line;
        if ( $line =~ /<title>(.*?)<\/title>/ ) {
            $title = $1;
        }
    }
    close BOOK;

    if ( $title eq "" ) {
        print STDERR "WARN: Cannot find title for book '$book'\n";
        $title = "[Undefined]";
    }

    return convert_entities($title);
}

# Determine the book author by reading the book meta information
sub find_author {
    my ($book) = @_;
    my $author = ""; 
    my $line = "";
    open (BOOK, "head -100 $book |") || die ("Could not read $book: $!");

    my $find_line = 0;
    while ($author eq "" && ( $line = <BOOK> ) ) {
        chomp $line;
        if ( $find_line == 1 && $line =~ /<line>(.*?)<\/line>/ ) {
            $author = $1;
        }
        $find_line = 1 if ( $line =~ /<creator class="medium">/ );
        $find_line = 0 if ( $line =~ /<\/creator>/ );
        if ( $line =~ /<creator class="author">(.*?)<\/title>/ ) {
            $author = $1;
        }
    }
    close BOOK;

    if ( $author eq "" ) {
        print STDERR "WARN: Cannot find author for book '$book'\n";
        $author = "[Undefined]";
    }


    return $author;
}

# Determine the book illustrator by reading the book meta information
sub find_illustrator {
    my ($book) = @_;
    my $illustrator = "";
    my $line = "";
    open (BOOK, "head -100 $book | ") || die ("Could not read $book: $!");

    my $find_line = 0;
    while ($illustrator eq "" && ( $line = <BOOK> ) ) {
        chomp $line;
        if ( $find_line == 1 && $line =~ /<line>Illustrated by (.*?)<\/line>/ ) {
            $illustrator = $1;
        }
        if ( $find_line == 1 && $line =~ /<line>Ilustrado por (.*?)<\/line>/ ) {
            $illustrator = $1;
        }
        $find_line = 1 if ( $line =~ /<creator class="medium">/ );
        $find_line = 0 if ( $line =~ /<\/creator>/ );
        if ( $line =~ /<creator class="illustrator">(.*?)<\/title>/ ) {
            $illustrator = $1;
        }
    }
    close BOOK;

    if ( $illustrator eq "" ) {
        print STDERR "WARN: Cannot find illustrator for book '$book'\n";
        if ( $language eq "en" ) {
            $illustrator = "[Unknown]";
        } elsif ( $language eq "es" ) {
            $illustrator = "[Desconocido]";
        }
    }
    if ( $language eq "en" ) {
        $illustrator = "Illustrated by ".$illustrator;
    } elsif ( $language eq "es" ) {
        $illustrator = "Illustrado por ".$illustrator;
    }

    return $illustrator;
}

sub convert_entities {
# Convert character entities to their correspondent values
    my ($text) = @_;

    $text =~ s/\<ch.apos\/\>/'/g; 
    $text =~ s/\<ch.nbsp\/\>/ /g;
    $text =~ s/\<ch.plusmn\/\>/+-/g;
    $text =~ s/\<ch.aacute\/\>/á/g;
    $text =~ s/\<ch.eacute\/\>/é/g;
    $text =~ s/\<ch.iacute\/\>/í/g;
    $text =~ s/\<ch.oacute\/\>/ó/g;
    $text =~ s/\<ch.uacute\/\>/ú/g;
    $text =~ s/\<ch.ntilde\/\>/ñ/g;
    $text =~ s/\<ch.Aacute\/\>/Á/g;
    $text =~ s/\<ch.Eacute\/\>/É/g;
    $text =~ s/\<ch.Iacute\/\>/Í/g;
    $text =~ s/\<ch.Oacute\/\>/Ó/g;
    $text =~ s/\<ch.Uacute\/\>/Ú/g;
    $text =~ s/\<ch.auml\/\>/ä/g;
    $text =~ s/\<ch.euml\/\>/ë/g;
    $text =~ s/\<ch.iuml\/\>/ï/g;
    $text =~ s/\<ch.ouml\/\>/ö/g;
    $text =~ s/\<ch.uuml\/\>/ü/g;
    $text =~ s/\<ch.Ntilde\/\>/Ñ/g;
    $text =~ s/\<ch.acute\/\>/´/g;
    $text =~ s/\<ch.iexcl\/\>/¡/g;
    $text =~ s/\<ch.iquest\/\>/¿/g;
    $text =~ s/\<ch.laquo\/\>/«/g;
    $text =~ s/\<ch.raquo\/\>/»/g;
    $text =~ s/\<ch.ampersand\/\>/&/g;

    return $text;
}

# Quote metacaracters for shell use
sub quote_shell {
    my ($text) = @_;
    $text =~ s/'/\\'/g; 
    return $text;
}
