# -*- perl -*-

#
# $Id: Cache.pm,v 5.1 2004/09/08 21:09:32 eserte Exp $
# Author: Slaven Rezic
#
# Copyright (C) 2002 Slaven Rezic. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# Mail: slaven@rezic.de
# WWW:  http://www.rezic.de/eserte/
#

package Tk::Pod::Cache;
use strict;
use vars qw($VERSION $MAX_CACHE %CACHE);

BEGIN {  # Make a DEBUG constant very first thing...
  if(defined &DEBUG) {
  } elsif(($ENV{'TKPODDEBUG'} || '') =~ m/^(\d+)/) { # untaint
    my $debug = $1;
    *DEBUG = sub () { $debug };
  } else {
    *DEBUG = sub () {0};
  }
}

$VERSION = sprintf("%d.%02d", q$Revision: 5.1 $ =~ /(\d+)\.(\d+)/);

$MAX_CACHE = 20; # documents # XXX not yet used, LRU etc...

sub add_to_cache {
    my($w, $pod) = @_;
    $pod = $w->cget(-path) if !defined $pod;
    return if !defined $pod;
    return if $CACHE{$pod}; # XXX check for recentness
    DEBUG and warn "Add contents for $pod to cache.\n";
    $CACHE{$pod} = $w->dump_contents;
}

sub get_from_cache {
    my($w, $pod) = @_;
    $pod = $w->cget(-path) if !defined $pod;
    return 0 if !$CACHE{$pod};
    # XXX check for recentness
    $w->delete("1.0", "end");
    DEBUG and warn "Restore contents for $pod from cache.\n";
    $w->restore_contents($CACHE{$pod});
    1;
}

sub delete_from_cache {
    my($w, $pod) = @_;
    $pod = $w->cget(-path) if !defined $pod;
    return if !defined $pod;
    DEBUG and warn "Delete contents for $pod from cache.\n";
    delete $CACHE{$pod};
}

sub clear_cache {
    %CACHE = ();
}

sub dump_contents {
    my $w = shift;
    my @dump = $w->dump('-all', "1.0", "end");
    if (@dump == 0) {
	warn "Workaround strange bug under RedHat 8.0 --- try dump again...";	
	@dump = $w->dump('-all', "1.0", "end");
	if (@dump == 0) {
	    warn "Giving up, cache disabled for current page";
	    return undef;
	}
    }
    my %tags_def;
    foreach my $tag ($w->tagNames) {
	# XXX check for used/existing tags missing
	my @tag_def;
	foreach my $item ($w->tagConfigure($tag)) {
	    my $value  = $item->[4];
	    my $option = $item->[0];
	    push @tag_def, $option, $value;
	}
	$tags_def{$tag} = \@tag_def;
    }
    return {Dump => \@dump,
	    Tags => \%tags_def,
	    Sections => $w->{'sections'},
	    PodTitle => $w->{'pod_title'},
	   };
}

sub restore_contents {
    my($w, $def) = @_;

    my $dumpref = $def->{Dump};
    my $tagref  = $def->{Tags};
    $w->{'sections'}  = $def->{Sections};
    $w->{'pod_title'} = $def->{PodTitle};

    $w->toplevel->title( "Tkpod: " . $w->{'pod_title'} . " (restoring)");
    $w->idletasks;
    # XXX  Is it bad form to manipulate the top level?

    my $process_no;
    $w->{ProcessNo}++;
    $process_no = $w->{ProcessNo};

    if ($tagref) {
	while(my($tag,$def) = each %$tagref) {
	    #XXX tagDelete?
	    $w->tagConfigure($tag, @$def);
	}
    }

    my @taglist;

    my $last_update = Tk::timeofday();
    for(my $i=0; $i<$#$dumpref; $i+=3) {
	my($key, $val, $index) = @{$dumpref}[$i..$i+2];
	if      ($key eq 'text') {
	    $w->insert($index, $val, [@taglist]);
	} elsif ($key eq 'tagon') {
	    unshift @taglist, $val;
	} elsif ($key eq 'tagoff') {
	    my $j;
	    for (0 .. $#taglist) {
		if ($taglist[$_] eq $val) {
		    $j = $_;
		    last;
		}
	    }
	    if (defined $j) {
		splice @taglist, $j, 1;
	    }
	    $w->tag('remove', $val, 'insert');
	} elsif ($key eq 'mark') {
	    $w->markSet($val, $index); # XXX ->see() to current or insert?
	} elsif ($key eq 'windows') {
	    die "not yet supported";
	} elsif ($key eq 'image') {
	    die "not yet supported";
	} elsif ($key eq 'imgdef') {
	    die "not yet supported";
	}

	if (Tk::timeofday() > $last_update+0.5) { # XXX make configurable
	    $w->update;
	    $last_update = Tk::timeofday();
	    do { warn "ABORT!"; return } if $w->{ProcessNo} != $process_no;
	}
    }

    $w->parent->add_section_menu if $w->parent->can('add_section_menu');
    $w->Callback('-poddone', $w->cget(-file));

    $w->toplevel->title( "Tkpod: " . $w->{'pod_title'});
}

1;

__END__

=head1 NAME

Tk::Pod::Cache - internal Tk-Pod module for cache control

=head1 DESCRIPTION

No user-servicable parts here.

=cut
