use strict;
use warnings;

use File::Basename;
use Cwd;
use lib Cwd::abs_path(File::Basename::dirname(__FILE__));

use NGCP::BulkProcessor::Globals qw($system_version
    $system_abbreviation
    $system_instance_label
    $application_path
);

our $umldiagrampath = Cwd::abs_path(File::Basename::dirname(__FILE__)) . '/uml/';

our $uml_diagram_format = 'dot'; #'png'

use NGCP::BulkProcessor::Utils qw(
    getscriptpath
    cleanupdir
    makepath
    changemod
    getscriptpath
);

use NGCP::BulkProcessor::Array qw(contains);

use NGCP::BulkProcessor::Logging qw(
    getlogger
    scriptinfo
);
use NGCP::BulkProcessor::LogError qw(
    fileerror
    filewarn
    done
);

use File::Find;
use File::Basename;
use UML::Class::Simple;

my $graphviz_dot_path;
if ($^O eq 'MSWin32') {
    # IPC run3 used in UML::Class::Simple obviously ignores windows (vista 32)
    # PATH variable. so we specify the path to graphviz dot.exe manually:
    #$graphviz_dot_path = 'C:\Program Files\Graphviz2.26.3\bin\dot.exe';
    $graphviz_dot_path = 'C:\Program Files (x86)\Graphviz2.26.3\bin\dot.exe';
} else {
    # unix probably ok - leave UML::Class::Simple alone:
    $graphviz_dot_path = undef;  
}

my $logger = getlogger(getscriptpath());

my @dirs_to_skip = ();
push @dirs_to_skip,$application_path . "Dao/mr553/";
push @dirs_to_skip,$application_path . "Dao/mr457/";
push @dirs_to_skip,$application_path . "Dao/mr441/";
push @dirs_to_skip,$application_path . "Dao/mr341/";
push @dirs_to_skip,$application_path . "Dao/mr103/";
#push @dirs_to_skip,$application_path . "Dao/mr102/";
push @dirs_to_skip,$application_path . "Dao/mr38/";
push @dirs_to_skip,$application_path . "Dao/Trunk/accounting";
push @dirs_to_skip,$application_path . "Dao/Trunk/billing";
push @dirs_to_skip,$application_path . "Dao/Trunk/kamailio";

push @dirs_to_skip,$application_path . "Redis/mr65/";
push @dirs_to_skip,$application_path . "RestRequests/";
push @dirs_to_skip,$application_path . "Service/";
push @dirs_to_skip,$application_path . "RestConnectors/";

push @dirs_to_skip,$application_path . "Projects/t/";
push @dirs_to_skip,$application_path . "Projects/ETL/";
push @dirs_to_skip,$application_path . "Projects/Export/";
push @dirs_to_skip,$application_path . "Projects/Disaster/";
push @dirs_to_skip,$application_path . "Projects/Migration/";
push @dirs_to_skip,$application_path . "Projects/Massive/Generator/";
push @dirs_to_skip,$application_path . "Projects/Massive/RegistrationMonitoring/";

#push @dirs_to_skip,$application_path . "FileProcessors/";

my $public_only = 1;
my $inherited_methods = 0;

#my @perlfileextensions = ('.pm'); #('.pl','.pm');
my @perlfileextensions = ('.pm');
my $rperlextensions = join('|', map { quotemeta } @perlfileextensions);

my @parsedfiles = ();

my $mode = $ARGV[0];

eval {

    my $painter;
    my $outputfilepath;

    if ('cleanup' eq $mode) {
	cleanupdir($umldiagrampath,0,\&scriptinfo,\&filewarn,$logger);
    } elsif ('all' eq $mode) {
	find({ wanted => \&dir_names, follow => 1 }, $application_path);

	$outputfilepath = $umldiagrampath . '_' . $system_abbreviation . '_' . $system_instance_label . '.' . $uml_diagram_format;

	$painter = getpainter(\@parsedfiles);
    
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('dao_ngcp' eq $mode) {
	$outputfilepath = $umldiagrampath . '_dao_ngcp.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'SqlRecord.pm',
	    $application_path . 'Dao/Trunk/provisioning/voip_subscribers.pm',
	]);
    
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('dao_openser' eq $mode) {
	$outputfilepath = $umldiagrampath . '_dao_openser.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'SqlRecord.pm',
	    $application_path . 'Dao/mr102/openser/location.pm',
	    $application_path . 'Dao/mr102/openser/voicemail_users.pm',	
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('dao_regmon' eq $mode) {
	$outputfilepath = $umldiagrampath . '_dao_regmon.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'SqlRecord.pm',
	    $application_path . 'Projects/Massive/VodafoneRO/RegistrationMonitoring/Dao/Proc4SwDynamic.pm',
	    $application_path . 'Projects/Massive/VodafoneRO/RegistrationMonitoring/Dao/Proc4SwStatic.pm',
	    $application_path . 'Projects/Massive/VodafoneRO/RegistrationMonitoring/Dao/SwStatus.pm',
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('dao_cmtsmon' eq $mode) {    
	$outputfilepath = $umldiagrampath . '_dao_cmtsmon.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'SqlRecord.pm',
	    $application_path . 'Projects/Massive/VodafoneRO/CmtsMonitoring/Dao/SwInputDynamic.pm',
	    $application_path . 'Projects/Massive/VodafoneRO/CmtsMonitoring/Dao/SwOutput.pm',
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('sql_connectors' eq $mode) {    
	$outputfilepath = $umldiagrampath . '_sql_connectors.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'SqlConnector.pm',
	    $application_path . 'SqlProcessor.pm',
	    $application_path . 'SqlRecord.pm',
	    $application_path . 'SqlConnectors/MySQLDB.pm',
	    $application_path . 'SqlConnectors/OracleDB.pm',
	    $application_path . 'SqlConnectors/SQLiteDB.pm',
	    $application_path . 'SqlConnectors/CSVDB.pm',
	    $application_path . 'SqlConnectors/SQLServerDB.pm',
	    $application_path . 'SqlConnectors/PostgreSQLDB.pm',
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('no_sql_connectors' eq $mode) {    
	$outputfilepath = $umldiagrampath . '_no_sql_connectors.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'NoSqlConnector.pm',
	    $application_path . 'NoSqlConnectors/Redis.pm',
	    $application_path . 'NoSqlConnectors/RedisEntry.pm',
	    $application_path . 'NoSqlConnectors/RedisProcessor.pm',
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('redis' eq $mode) {    
	$outputfilepath = $umldiagrampath . '_redis.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'NoSqlConnectors/RedisEntry.pm',
	    $application_path . 'Redis/mr755/location/usrdom.pm',
	    $application_path . 'Redis/mr755/location/entry.pm',
	]);
	
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}    
    } elsif ('framework1' eq $mode) {
	$outputfilepath = $umldiagrampath . '_framework1.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'Array.pm',
	    $application_path . 'Calendar.pm',
	    $application_path . 'ConnectorPool.pm',
	    $application_path . 'DSPath.pm',
	    $application_path . 'DSSorter.pm',
	]);
    
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('framework3' eq $mode) {
	$outputfilepath = $umldiagrampath . '_framework3.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'FileProcessor.pm',
	    $application_path . 'Globals.pm',
	    $application_path . 'LoadConfig.pm',
	    $application_path . 'LogError.pm',
	    $application_path . 'Logging.pm',
	    $application_path . 'Mail.pm',
	]);
    
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    } elsif ('framework2' eq $mode) {
	$outputfilepath = $umldiagrampath . '_framework2.' . $uml_diagram_format;
	$painter = getpainter([
	    $application_path . 'RestConnector.pm',
	    $application_path . 'RestItem.pm',
	    $application_path . 'RestProcessor.pm',
	    $application_path . 'Serialization.pm',
	    $application_path . 'Table.pm',
	    $application_path . 'Utils.pm',
	]);
    
	if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
	    scriptinfo($outputfilepath . ' created',$logger);
	} else {
	    print STDERR "Could not create $outputfilepath.\n";
	}
    }
};
if ($@) {
    exit(1); 
} else {
    done(undef,undef,$logger);
    exit(0); 
}

sub dir_names {
    
    my $path = $File::Find::dir;
    $path .= '/' . $_ if ($_ ne '.' and $_ ne '..');
    if (-d $path) {
        my $dir = $path . '/';
        unless (scalar grep { index($dir, $_, 0) == 0; } @dirs_to_skip) {
            #print 'creating perldoc html files for source files in ' . $path . "\n";
            push @parsedfiles,createumldiagramsofdir($dir,$umldiagrampath . substr($dir,length($application_path)));
            #push @dirs_to_skip,$dir;
        }
    }
}

sub createumldiagramsofdir {
  
    my ($inputdir,$outputdir) = @_;
    makepath($outputdir);
    
    my $docdirh;
    if (not opendir($docdirh, $inputdir)) {
        fileerror('cannot opendir ' . $inputdir . ': ' . $!,$logger);
        return;
    }
    my @files = grep { /$rperlextensions$/ && -f $inputdir . $_} readdir($docdirh);
    closedir $docdirh;
    
    my @inputfilepaths = ();
  
    foreach my $file (@files) {
        my $inputfilepath = $inputdir . $file;
        my ($inputfilename,$inputfiledir,$inputfilesuffix) = fileparse($inputfilepath, $rperlextensions);
        my $outputfilepath = $outputdir . $inputfilename . '.' . $uml_diagram_format; #'.png';
  #unlink $outputfilepath;

        my $painter = getpainter($inputfilepath); 

        # we can explicitly specify the image size
        #$painter->size(5, 3.6); # in inches

        # ...and change the default title background color:
        #$painter->node_color('#ffffff'); # defaults to '#f1e1f4'
    
        # only show public methods and properties
        #$painter->public_only(1);
    
        # hide all methods from parent classes
        #$painter->inherited_methods(0);
    
        if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
          changemod($outputfilepath);
          push @inputfilepaths,$inputfilepath;
          scriptinfo('uml diagram ' . $outputfilepath . ' created for source file ' . $inputfilepath,$logger);
        } else {
          # write message to STDERR make it look the same as perldoc
          print STDERR 'Could not create uml diagram for "' . $inputfilepath . "\".\n";
        }
    }

    if (scalar @inputfilepaths > 0) {
      
        my @dirparts = File::Spec->splitdir($inputdir);
        my $outputfilepath = $outputdir . $dirparts[$#dirparts - 1] . '.' . $uml_diagram_format; #'.png';  
        
        my $painter = getpainter(\@inputfilepaths);
    
        # we can explicitly specify the image size
        #$painter->size(5, 3.6); # in inches
    
        # ...and change the default title background color:
        #$painter->node_color('#ffffff'); # defaults to '#f1e1f4'
    
        # only show public methods and properties
        #$painter->public_only(1);
    
        # hide all methods from parent classes
        #$painter->inherited_methods(0);

        if (outputpainter($painter,$outputfilepath) and -e $outputfilepath) {
            scriptinfo('uml diagram ' . $outputfilepath . ' created for source dir ' . $inputdir,$logger);
        } else {
            # write message to STDERR make it look the same as perldoc
            print STDERR 'Could not create uml diagram for dir "' . $inputdir . "\".\n";
        }
    }
    
    return @inputfilepaths;
  
}

sub getpainter {
  
    my $inputfilepaths = shift;
  
    my @classes = classes_from_files($inputfilepaths);
    my $painter = UML::Class::Simple->new(\@classes);
    if ($graphviz_dot_path) {
        $painter->{dot_prog} = $graphviz_dot_path;
    }
    
    $painter->public_only($public_only);
    $painter->inherited_methods($inherited_methods);
    #$painter->size(8.3, 11.7);
    
    return $painter;
  
}

sub outputpainter {
  
  my ($painter,$outputfilepath) = @_;
    
  if (scalar @{$painter->as_dom()->{classes}} > 0) {
    if ($uml_diagram_format eq 'png') {
      $painter->as_png($outputfilepath);
    } elsif ($uml_diagram_format eq 'dot') {
      $painter->as_dot($outputfilepath);
    }
    return 1;
  } else {
    return 0;
  }
  
}
