#!/usr/bin/perl
# 
# Copyright (C) 2005 Koji Nakamaru
#
# Author: Koji Nakamaru <maru@on.cs.keio.ac.jp>
# Created/Modified: Jan 2 2005
# Keywords: tex, latex, math, web
#
# 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, 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 GNU Emacs; see the file COPYING.  If not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#

use strict;
use File::Temp qw/ mkdtemp /;
use FCGI;

my $cache_size_limit = 5 * 1024 * 1024;

sub xerror;
sub getContents;
sub cacheCheck;
sub cacheAdd;

my $pngerr = &getContents("preverr.png");
if (! defined($pngerr)) {
    die "wrong installation or file system error\n";
}
my %cache;
my %cache_hit;
my $cache_size = 0;
my $tmpdir;
my $fcgi = FCGI::Request();
while ($fcgi->FCGI::Accept() >= 0) {
    my $req = $ENV{'QUERY_STRING'};
    my $png = &cacheCheck($req);
    if (! defined($png)) {
	my $res = 96;
	my $cmd = $req;
#	$cmd =~ tr/+/ /;
	$cmd =~ s/%([a-fA-F0-9]{2})/pack("C",hex($1))/eg;
	if ($cmd =~ /^\s*\[([^]]+)\](.+)\s*$/) {
	    $cmd = $2;
	    my $opt = $1;
	    $opt =~ s/\s+//g;
	    if ($opt =~ /res=(\d+)/) {
		$res = $1;
	    } else {
		&xerror("found unknown option: $1.");
		next;
	    }
	}
        if (! ($res =~ /\d+/ && 10 <= $res && $res <= 1200)) {
	    &xerror("found res out of range [10,1000].");
	    next;
	}
        if ($cmd eq '') {
	    &xerror("found no string.");
	    next;
	}
        $tmpdir = mkdtemp("imgtexwork/tXXXXXX");
        if (! open(FO, "> $tmpdir/t.tex")) {
	    &xerror("failed to generate a tex file.");
	    next;
	}
        print FO <<"EOF";
\\documentclass\{article\}
\\usepackage\{type1cm\}
\\usepackage[psamsfonts]\{amssymb\}
\\usepackage\{amsmath\}
\\begin\{document\}
\\thispagestyle{empty}
$cmd
\\end\{document\}
EOF
        close FO;
        if (system("ulimit -t 10 -f 1024 -c 0; cd $tmpdir ; latex '\\nonstopmode\\input' t.tex > /dev/null 2>&1 ; case \$? in 0) dvipng -q -D $res -T tight -M -pp 1 --noghostscript t.dvi -o t%03d.png > /dev/null 2>&1 ;; *) exit 1 ;; esac") != 0) {
	    &xerror("failed to generate a dvi file.");
	    next;
	}
        $png = &getContents("$tmpdir/t001.png");
    }
    if (defined($png)) {
	&cacheAdd($req, $png);
	print "Content-type: image/png\r\n\r\n", $png;
    } else {
	&xerror("failed to generate a png file.");
    }
    system("rm -rf $tmpdir");
}

sub xerror
{
    my ($msg) = @_;
    print "Content-type: image/png\r\n\r\n", $pngerr;
    system("rm -rf $tmpdir");
}

sub getContents
{
    my ($fname) = @_;
    my $contents = undef;
    open FI, "< $fname"
	or return $contents;
    {
	local $/;
	$contents = <FI>;
    }
    close FI;
    return $contents;
}

sub cacheCheck
{
    my ($req) = @_;
    if (defined($cache{$req})) {
	$cache_hit{$req} = localtime();
	return $cache{$req};
    }
    return undef;
}

sub cacheAdd
{
    my ($req, $png) = @_;
    $cache{$req} = $png;
    $cache_hit{$req} = localtime();
    $cache_size += length($png);
    my $now = localtime();
    while ($cache_size > $cache_size_limit) {
	my $key0 = undef;
	my $hit0 = $now;
	foreach my $key (keys(%cache_hit)) {
	    my $hit = $cache_hit{$key};
	    if ($hit < $hit0) {
		$key0 = $key;
		$hit0 = $hit;
	    }
	}
	if (defined($key0)) {
	    $cache_size -= length($cache{$key0});
	    $cache{$key0} = undef;
	    $cache_hit{$key0} = undef;
	}
    }
}
