use strict;
use warnings;

use IPC::Open2;

package GitFileServer;

use bytes;

##
## Some helper functions, not specific to git cat-file handling
##
sub TRACE
{
	my $fmt = shift;
	printf STDERR "[$$] $fmt\n", @_;
}

sub max
{
	my ($a, $b) = @_;
	return $a if $a > $b;
	return $b;
}

sub min
{
	my ($a, $b) = @_;
	return $a if $a < $b;
	return $b;
}

## this is ripped out gitweb, it could obviously be improved/cached
sub _mimetype
{
	my $filename = shift;
	my $mimemap = '/etc/mime.types';

        my %mimemap;
        open(MIME, $mimemap) or return undef;
        while (<MIME>) {
                next if m/^#/; # skip comments
                my ($mime, $exts) = split(/\t+/);
                if (defined $exts) {
                        my @exts = split(/\s+/, $exts);
                        foreach my $ext (@exts) {
                                $mimemap{$ext} = $mime;
                        }
                }
        }
        close(MIME);

        $filename =~ /\.([^.]*)$/;
        return $mimemap{$1};

}

sub _html_dir
{
	my ($name, @dir) = @_;
	my @output;
	push @output, "Content-type: text/html\n\n";
	push @output, "<html>\n";
	push @output, " <head><title>Directory listing for $name</title></head>\n";
	push @output, " <body>\n";
	push @output, "  <table>\n";
	push @output, "   <th><td>Name</td></th>\n";

	while ($#dir > 0)
	{
		my $n = shift @dir;
		my $m = shift @dir;
		if ($m =~ m/^1/)
		{
			push @output, "   <tr><td>[$m]</td><td><a href=\"$n\">$n</a></td></tr>\n";
		} else
		{
			push @output, "   <tr><td>[$m]</td><td><a href=\"$n/\">$n/</a></td></tr>\n";
		}
	}
	push @output, "  </table>\n";
	push @output, " </body>";
	push @output, "</html>\n";

	return @output;
}


##
## And now our git cat-file handling stuff
##

sub new
{
	my $class = shift;
	my @args = @_;
	my %opts = ();
	my $self;

        if (defined $args[0]) {
                if ($#args % 2 != 1) {
                        # Not a hash.
                        $#args == 0 or die "Bad usage\n";
                        %opts = ( repo => $args[0] );
                } else {
                        %opts = @args;
                }
        }

	
	$self->{repo} = $opts{repo} || '.';

	if (defined $opts{func_mimetype})
	{
		$self->{func_mimetype} = $opts{func_mimetype};
	} else
	{
		$self->{func_mimetype} = \&_mimetype;
	}
	if (defined $opts{func_dirlist})
	{
		$self->{func_dirlist} = $opts{func_dirlist};
	} else
	{
		$self->{func_dirlist} = \&_html_dir;
	}


	my ($r, $w);

	$self->{gitpid} = IPC::Open2::open2($r, $w, "git --git-dir=" . $self->{repo} . " cat-file --batch" );

	$self->{reader} = $r;
	$self->{writer} = $w;
	
	bless $self, $class;
}

sub get
{
	my $self = shift;
	my ($fh, $branch, $name) = @_;

	my ($got, $id, $type, $size);

	my $r = $self->{reader};
	my $w = $self->{writer};

	TRACE "WRITING to git-cat-file: %s:%s", $branch, $name;
	print $w "$branch:$name\n";

	while (1)
	{
		$got = <$r>;
		chomp $got;
		last if $got;
		TRACE "PHANTOM READ from git-cat-file, repeating ...";
	}

	($id, $type, $size) = split / /, $got;
	TRACE "READ: %s %s %d [%d]", $id, $type, $size, length($got);

	if ($type eq 'blob')
	{
		$self->_do_blob($fh, $name, $size);
	} elsif ($type eq 'tree')
	{
		$self->_do_tree($fh, $name, $size);
	} else
	{
		TRACE "Unknown object";
		return undef;
	}
	return 1;
}

sub _do_blob
{
	my ($self, $fh, $name, $size) = @_;

	printf $fh "Content-type: %s\n", $self->{func_mimetype}($name);
	printf $fh "Content-Length: %d\n", $size;
	printf $fh "Content-Disposition: inline; filename=\"%s\"\n", $name;
	print $fh "\n";
	my $r = $self->{reader};
	my $buf;
	while ($size > 0)
	{
		my $len = min($size, 4096);
		read($r, $buf, $len);
		print $fh $buf;
		$size -= $len;
	}
}

sub _do_tree
{
	my ($self, $fh, $name, $size) = @_;
	my $buffer;
	read($self->{reader}, $buffer, $size);

	my @d;

	while (length($buffer) > 0)
	{
		my ($string) = unpack "Z*", $buffer;
		my ($m, $n) = split / /, $string;
		push @d, $n; push @d, sprintf("%06d",$m);
		substr $buffer, 0, length($string)+21, '';
	}
	print $fh $self->{func_dirlist}($name, @d);
}
sub _html_dir_unused
{
	my $name = shift;
	my %dir = @_;

	print "<html>\n";
	print " <head><title>Directory listing for $name</title></head>\n";
	print " <body>\n";
	print "  <table>\n";
	print "   <th><td>Name</td></th>\n";
	foreach my $entry (keys %dir)
	{
		if ($dir{$entry} =~ m/^4/)
		{
			print "   <tr><td<a href=\"$entry/\">$name/$entry</a></td></tr>\n";
		} elsif ($dir{$entry} =~ m/^100/)
		{
			print "   <tr><td<a href=\"$entry\">$name/$entry</a></td></tr>\n";
		}
	}
	print "  </table>\n";
	print " </body>";
	print "</html>\n";

}

1;
