#!/bin/perl
# 
#  Compare generated files with frozen reference files
#
use strict qw(vars);
use warnings;
use vars   qw($VERSION);

#  Don't let local WEBDYNE_CONF be loaded
#
BEGIN {
    $ENV{'WEBDYNE_CONF'}='.' unless (($ENV{'WEBDYNE_TEST_FILE_PREFIX'} ||= '') eq '03');
}

#  Load
#
use Test::More qw(no_plan);
use FindBin qw($RealBin $Script);
use File::Temp qw(tempfile);
use File::Find qw(find);
use Data::Dumper;
use IO::File;
use IO::String;
use Cwd qw(abs_path);
$Data::Dumper::Indent=1;
$Data::Dumper::Sortkeys=1;
use Storable qw(lock_retrieve freeze);
$Storable::canonical=1;


#  Load WebDyne
#
require_ok('WebDyne::Compile');
require_ok('WebDyne::Request::Fake');
use WebDyne::Util;


#  Setup environment for this test if not already present
#
$ENV{'WEBDYNE_TEST_FILE_PREFIX'} ||= '02';
#die Dumper(\%INC, \@INC);


#  Run
#
exit(${&main(\@ARGV) || die err ()} || 0);    # || 0 stops warnings

#==================================================================================================



sub err_carp {

    require Carp;
    $Carp::CarpLevel=0;
    $Carp::RefArgFormatter = sub {
        require Data::Dumper;                                                                                                                                                
        $Data::Dumper::Indent=1;
        Data::Dumper->Dump(\@_); # not necessarily safe                                                                                                                    
    };
    &Carp::confess &diag(@_);
    
}



sub main {

    #  Get list of files either from command line or from *.psp if no
    #  command line given
    #
    my @test_fn=@{shift()};
    if (my $test_fn=$ENV{'WEBDYNE_TEST_FILE'}) {
        @test_fn=map { glob $_ } split(/[;,]/, $test_fn);
    }
    my $wanted_cr=sub { push (@test_fn, $File::Find::name) if /\.psp$/ };
    find($wanted_cr, $RealBin) unless @test_fn;


    #  Create a new compile instance
    #
    my $compile_or=WebDyne::Compile->new() ||
        return err();
        

    #  Data dir
    #
    my $data_freeze_dn='data';


    #  Iterate over files
    #
    diag('');
    FILE: foreach my $test_fn (sort {$a cmp $b } @test_fn) {


        #  Create WebDyne render of PSP file and capture to file
        #
        debug("processing $test_fn");
        my $test_cn=abs_path($test_fn) ||
            return err("unable to determine full path of $test_fn");
        (-f $test_cn) ||
            return err("unable to find file: $test_fn");
        diag("processing: $test_fn");
        
        #my $compile_or=WebDyne::Compile->new() ||
        #    return err();
        #  Hack for testing
        #
        $compile_or->{'_r'}{'filename'}=$test_fn;
        

        #  Iterate twice to make sure no change over multiple iterations
        #
        foreach my $count (1..2) {



            foreach my $stage ((0..5), 'final') {


                #  Get data file
                #
                my ($data_dn, $data_fn)=(File::Spec->splitpath($test_cn))[1,2];


                #  Enables variations on a single source file
                #
                $data_fn=join('-', grep {$_} $ENV{'WEBDYNE_TEST_FILE_PREFIX'},  $data_fn);
                my $data_cn=File::Spec->catfile($data_dn, $data_freeze_dn, $data_fn);
                $data_cn=~s/\.psp$/\.dat\.${stage}/;


                #  Compile to desired stage
                #
                my $stage_name=($stage eq 'final') ? $stage : "stage${stage}";
                #diag("count: $count, stage_name: $stage_name");


                #  Options. Use test_fn rather than test_fp so manifest only has file name
                #
                my %opt=(

                    srce        	=> $test_cn,
                    nofilter	=> 1,
                    noperl		=> 1,
                    notimestamp	=> 1,
                    nomanifest	=> 1,
                    $stage_name     => 1
                    
                );
                

                #  Get it
                #
                my $data_live_ar=$compile_or->compile(\%opt) ||
                    return err ();
                debug("data_live_ar %s", Dumper($data_live_ar));
                
                
                #  Get previous version
                #
                (-f $data_cn) || do {
                    diag("skipping $test_fn, no data file - run maketest.pl");
                    return err();
                    #next FILE;
                };
                my $data_thaw_ar=lock_retrieve($data_cn) ||
                    return err();


                #  Now compare
                #
                #my $string_live=freeze($data_live_ar);
                #my $string_thaw=freeze($data_thaw_ar);
                
                #  New comparison - Storable format not reliable across different perl versions
                #
                my $string_actual=Data::Dumper->Dump([$data_live_ar],['$VAR1']);
                my $string_expect=Data::Dumper->Dump([$data_thaw_ar],['$VAR1']);
                
                if ($string_actual eq $string_expect) {
                    pass("$test_fn pass on stage: $stage");
                }
                else {
                    fail(diag("$test_fn fail on stage: $stage"));
                    diag("ACTUAL: $string_actual");
                    diag("EXPECT: $string_expect");
                    eval { require Text::Diff } || do {
                        diag('unable to load Text::Diff module to show comparison');
                        next;
                    };
                    my $diff=Text::Diff::diff(
                        \(my $actual=Data::Dumper->Dump([$data_live_ar],['$ACTUAL'])),
                        \(my $expect=Data::Dumper->Dump([$data_thaw_ar],['$EXPECT'])),
                        { STYLE => 'Unified' }
                    );
                    diag("diff: $diff");
                    #diag(sprintf('%s:%s', Dumper($data_live_ar, $data_thaw_ar)));
                }

            } #foreach stage
            
            
            #  Now HTML
            #
            #diag("processing: $test_fn stage: HTML render");
            my ($data_dn, $data_fn)=(File::Spec->splitpath($test_cn))[1,2];
            $data_fn=join('-', grep {$_} $ENV{'WEBDYNE_TEST_FILE_PREFIX'},  $data_fn);

            my $data_cn=File::Spec->catfile($data_dn, $data_freeze_dn, $data_fn);
            $data_cn=~s/\.psp$/\.html/;


            my $html_live_sr=&render($test_cn) ||
                return err();
            #diag("render: *${$html_live_sr}*");

            (-f $data_cn) || do {
                diag("skipping $test_fn, no data file - run maketest.pl");
                next;
            };
            my $html_thaw_fh=IO::File->new($data_cn, O_RDONLY) ||
                return err("unable to open $data_cn, $!");
            local $/;
            my $html_thaw=<$html_thaw_fh>;
            $html_thaw_fh->close();

            if (${$html_live_sr} eq $html_thaw) {
                pass("$test_fn pass on stage: HTML render");
            }
            else {
                fail(diag("$test_fn fail on stage: HTML render"));
                eval { require Text::Diff } || do {
                    diag('unable to load Text::Diff module to show comparison');
                    next;
                };
                my $diff=Text::Diff::diff(
                    \Data::Dumper->Dump([$html_live_sr], ['$ACTUAL']),
                    \Data::Dumper->Dump([\$html_thaw], ['$EXPECT']),
                    { STYLE => 'Unified' }
                );
                diag("diff: $diff");
                #diag(sprintf('%s:%s', Dumper($html_live_sr, \$html_thaw)));
            }

        }

        #ok(${$html_sr} eq $html, "$test_fn pass on stage: render");
        ##die ${$html_sr};

    }
    


    #  Done
    #
    return \undef
    
}


sub render {


    #  Where is our source and dest
    #
    my $srce_fn=shift();


    #  Get scalar we can select to
    #
    my $html;
    my $html_fh=IO::String->new($html);


    #  Render to dest file
    #
    my $r=WebDyne::Request::Fake->new( 
        filename	=> $srce_fn, 
        select		=> $html_fh,
        noheader	=> 1 
    );
    defined(WebDyne->handler($r)) ||
        return err('render error');
    $r->DESTROY();
    $html_fh->close();


    #  Manual cleanup
    #
    #diag('render: ok');


    #  Done, return success
    #
    return \$html;

}


