#!/usr/bin/perl

# Copyright (C) 2011-2013 Zentyal S.L.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use strict;
use warnings;

use Getopt::Long;
use Cwd;
use HTML::Mason;
use File::Slurp;
use File::Copy;

use constant
    {
       MODULE_VERSION => '2.2',
       STUBS_PATH => '/usr/share/zentyal/stubs/zmoddev',
    };

sub types
{
    return qw/Text Int Boolean Port HostIP IPAddress Select DomainName/;
}

sub _printValidTypes
{
    for my $type (types()) {
        print STDERR "\t $type\n";
    }
    print STDERR "\n\tExample:\n";
    print STDERR "\t\t --field name:Text --field active:Boolean\n";
}

sub _printUsage
{
    print STDERR "\nUsage: $0 --main-class class --name name --field name1:type1 --field name2:type2 [Options]\n\n";
    print STDERR "Options:\n";
    print STDERR "\t--model form | table. Default: form\n";
    print STDERR "\t--destdir\n";
    print STDERR "Types:\n";
    _printValidTypes();
    exit 1;
}

sub _parseFields
{
    my ($fields) = @_;

    my %validTypes = map {$_ => 1} types();
    my %names;
    my  @fields;
    for my $pair (@{$fields}) {
        unless ($pair =~ /:/) {
            print STDERR "$pair is not a valid field:type\n";
            exit 1;
        }
        my ($name, $type) = split(/:/, $pair);
        unless (exists $validTypes{$type}) {
            print STDERR "$type from $name is not a valid type\n";
            exit 1;
        }
        if (exists $names{$name}) {
            print STDERR "$name already exists\n";
            exit 1;
        } else {
            $names{$name} = $type;
            push (@fields, { name => $name, type => $type});
        }
    }

    return \@fields;
}

sub _parseOptions
{
    my %options = (
            name =>   undef,
            fields => [],
            destdir => undef,
            model => 'form',
            mainClass => undef,
            modNameSpace => undef,
            );
    my $help;
    my $info;
    my $optionsOk = GetOptions(
            'main-class=s' => \$options{mainClass},
            'name=s' => \$options{name},
            'field=s' => $options{fields},
            'destdir=s' => \$options{destdir},
            'model=s' => \$options{model},
            'module-namespace=s' => \$options{modNameSpace},
            'help'  => \$help,
            'info'  => \$info,
            );

    $options{parsedFields} = _parseFields($options{fields});
    if (not $optionsOk or $info or $help) {
        _printUsage();
    }

    unless (defined($options{modNameSpace})) {
        $options{modNameSpace} = $options{mainClass};
    }

    unless (defined($options{name}) and
            defined($options{modNameSpace})
           ) {
        _printUsage();
    }

    if ($options{destdir}) {
        unless (-d $options{destdir}) {
            die "$options{destidir} does not exist!";
        }
    } else {
        $options{destdir} = getcwd;
    }

    unless ( -d $options{destdir} ) {
        die "$options{destdir} does not exist!";
    }

    return \%options;
}

sub _models
{
    my ($dir) = @_;

    my @models ;
    opendir ( my $dirFH, $dir );
    while ( defined ( my $file = readdir ( $dirFH ))) {
          next unless ( $file =~ m/.*\.pm/ );
          push(@models, $file);
    }

    return \@models;
}

sub _src_EBox_Module_Model
{
    my ($interp, $output, $options) = @_;

    my $modNameSpace = $options->{modNameSpace};
    my $dir = $options->{destdir} . '/src/EBox/' . $modNameSpace .  '/Model';

    my $modelClassFile = $options->{name} . '.pm';
    mkdir ($dir) unless (-d $dir);

    my $name = $options->{name};
    my $packageName = 'EBox::' . $modNameSpace . '::Model::' . $name;

    # TableModel or FormModel ?
    my $template = '/src/Model/FormModel.pm.mas';

    my $comp = $interp->make_component(
            comp_file => STUBS_PATH . $template);
    ${$output} = '';
    $interp->exec($comp,
            (packageName => $packageName,
             modelName => $name,
             printableTableName => $options->{name},
             printableRowName => $options->{name},
             rowName   => $name,
             modelDomain => $options->{mainClass},
             fields    => $options->{parsedFields},
             modelType => $options->{model}));
    write_file("$dir/$modelClassFile", ${$output});
}

sub _addModelsToMainClass
{
    my ($interp, $output, $options) = @_;

    my $mainClass = $options->{mainClass};
    my $file = $options->{destdir} . "/src/EBox/$mainClass.pm";
    my $modNameSpace =  $options->{modNameSpace};
    my $dir = $options->{destdir} . '/src/EBox/' .
                    $modNameSpace . '/Model';

    my $code = "    return [\n";
    for my $model (@{_models($dir)}) {
        $code .= ' ' x 8;
        $model =~ s/\.pm$//;
        $code .= "'EBox::${mainClass}::Model::$model',\n";
    }
    $code .= "    ];\n";

    my $fd;
    open($fd, $file) or die "Can open $file\n";
    my @oldFile = <$fd>;
    close($fd);

    open($fd, ">$file");
    my $inFunction = undef;
    for my $line (@oldFile) {
        if ($line =~ /sub modelClasses/) {
            $inFunction = 1;
        } elsif ($inFunction and $line =~ /{/) {
            print $fd "{\n$code";
            next;
        } elsif ($inFunction and $line =~ /}/) {
            $inFunction = undef
        }  elsif ($inFunction) {
            next;
        }
        print $fd $line;
    }
    close($fd);
}

sub _createModel
{
    my $options = _parseOptions();

    my $dir = $options->{destdir};

    my $output;
    my $interp = HTML::Mason::Interp->new(comp_root => STUBS_PATH,
            out_method => \$output);

    _src_EBox_Module_Model($interp, \$output, $options);
    _addModelsToMainClass($interp, \$output, $options);
}

_createModel();

