#!/usr/bin/perl -w
#
# Zymonic Business Process and Information Management System
# Copyright Zednax Limited 2008 -
# For Authors and Changelog see the subversion history
use strict;

# Modules
use Zymonic::Config;
use Zymonic::SystemDefinition;
$Zymonic::system  = '';
$Zymonic::session = '';

use XML::Simple;
use Text::Diff;

# Library path
BEGIN
{
    use Zymonic::Utils qw(clean death_handler debug get_array);

    #$main::SIG{__DIE__} = \&death_handler;
}

$Zymonic::system = clean( $ARGV[0], '_' );
my $type     = clean( $ARGV[1], '_\*' );
my $zname    = clean( $ARGV[2], '_\*' );
my $log_file = clean( $ARGV[3], '-_./\\' );
unless ( $Zymonic::system and $type and $zname )
{
    print "get_def.pl [system] [type] [zname] [logfile (optional)]\n";
    print "type and zname will allow wildcard '*', but it must be quoted to avoid shell expansion\n";
    exit(1);
}

# make sure user understands the risks of running this
if ($log_file)
{
    my $fh;
    if ($log_file)
    {
        if ( $log_file eq '-' )
        {
            open $fh, ">&STDOUT" or die "Unable to open STDOUT for writing";
        }
        else
        {
            open $fh, '>', $log_file or die "Unable to open $log_file for writing";
        }
        $Zymonic::Utils::debugfile = $fh;
    }
    else
    {
        $Zymonic::Utils::debugs_written = 1;
    }
}

$Zymonic::ZCONFIG{$Zymonic::system} = Zymonic::Config->new(
    system_name => $Zymonic::system,
    config_dir  => "/etc/zymonic",
    ip_address  => '127.0.0.1',
    protocol    => 'http'
);

my $sys_def = Zymonic::SystemDefinition->new( config_dir => "/etc/zymonic", definition_name => $Zymonic::system );

$sys_def->load_most_recent();

my @definitions = map { $_->{reference} } values( %{ $Zymonic::ZCONFIG{$Zymonic::system}->{SysDef}->{zname_list} } );

my @new_definitions = ();

foreach my $def_item (@definitions)
{
    # TODO consider whether de-duplication may be needed.
    @new_definitions = strip_embedded($def_item);
}

my $count     = 0;
my $no_match  = 0;
my $not_found = 0;

foreach my $def_item ( sort { ( $a->{ZName}->{content} || '' ) cmp( $b->{ZName}->{content} || '' ) }
    ( @definitions, @new_definitions ) )
{
    my $new_def = $sys_def->get_def( '*', $def_item->{ZName}->{content}, 'no_error' );

    if ( !$new_def )
    {
        print "$def_item->{ZName}->{content} not found.\n";
        $not_found += 1;
        next;
    }

    strip_embedded($new_def);

    $no_match += compare( $def_item, $new_def );
    $count += 1;
}

print "\n===SUMMARY====\n\nObjects: $count\nNot matched: $no_match\nNot found $not_found\n";

sub compare
{
    my $old_def = shift;
    my $new_def = shift;

    delete $new_def->{ZZmetadata};
    delete $new_def->{type};
    delete $old_def->{autocreated};
    delete $old_def->{maintainer};
    delete $old_def->{sourcefile};

    foreach
      my $missing ( 'ClassOptions', 'Hidden', 'SQLFieldType', 'DesiredValue', 'MaximumLength', 'ConditionCombination' )
    {
        delete $old_def->{$missing}
          if !defined( $new_def->{$missing} )
          and scalar( keys( %{ $old_def->{$missing} } ) ) == 0;

    }

    my $old_xml = XMLout(
        { map { $_->{ZName}->{content} => $_ } get_array($old_def) },
        RootName => 'Zymonic',
        KeyAttr  => []
    );
    my $new_xml = XMLout(
        { map { $_->{ZName}->{content} => $_ } get_array($new_def) },
        RootName => 'Zymonic',
        KeyAttr  => []
    );

    my $diff = diff( \$old_xml, \$new_xml );

    print "ZName: " . $old_def->{ZName}->{content} . "\n" . $diff . "\n" if $diff;

    return $diff ? 1 : 0;

}

sub strip_embedded
{
    my $def_item = shift;

    # work through the definition and remove any definitions (i.e. anything with a ZName)
    # and replace them with reference definition only. Removed definitions should be
    # returned in an array.

    my @stripped = ();

    foreach my $key ( keys( %{$def_item} ) )
    {
        if ( ref( $def_item->{$key} ) eq 'HASH' )
        {
            if ( defined( $def_item->{$key}->{ZName} ) )
            {
                # This needs removing if it is more than just a reference
                if ( scalar( keys( %{ $def_item->{$key} } ) ) > 1 )
                {
                    push( @stripped, \%{ $def_item->{$key} } );
                    $def_item->{$key} = { ZName => { content => $def_item->{$key}->{ZName}->{content} } };
                }
            }
            elsif ( !defined( $def_item->{$key}->{content} ) )
            {
                # This needs recursively inspecting if it has more than just 'content'
                push( @stripped, strip_embedded( $def_item->{$key} ) );
            }
        }
        elsif ( ref( $def_item->{$key} ) eq 'ARRAY' )
        {
            foreach my $i ( 0 .. $#{ $def_item->{$key} } )
            {
                if ( defined( $def_item->{$key}->[$i]->{ZName} ) )
                {
                    # This needs removing if it is more than just a reference
                    if ( scalar( keys( %{ $def_item->{$key}->[$i] } ) ) > 1 )
                    {
                        push( @stripped, \%{ $def_item->{$key}->[$i] } );
                        $def_item->{$key}->[$i] =
                          { ZName => { content => $def_item->{$key}->[$i]->{ZName}->{content} } };
                    }
                }
                elsif ( !defined( $def_item->{$key}->[$i]->{content} ) )
                {
                    # This needs recursively inspecting if it has more than just 'content'
                    push( @stripped, strip_embedded( $def_item->{$key}->[$i] ) );
                }
            }
        }
    }

    return @stripped;
}
