package LATMOS::Accounts::Web::Model::AttrForms;

use strict;
use warnings;
use base 'Catalyst::Model';
use LATMOS::Accounts::Log;
use HTML::Prototype;

=head1 NAME

LATMOS::Accounts::Web::Model::AttrForms - Catalyst Model

=head1 DESCRIPTION

Catalyst Model.

=cut

my $forms = {
    usersys => {
        name => 'Système',
        attrs => [ qw(
            sn givenName description
            uid uidNumber gidNumber gecos homeDirectory loginShell
            mail initials nickname
            expire
            locked
            create
            date
        ) ],
    },
    user => {
        name => 'Statut',
        attrs => [ qw(
            sn givenName description
            company
            department
            manager
            managerContact
            contratType
            expire
            snNative
            givenNameNative
            wWWHomePage
            halReference
            create
            date
        ) ],
    },
    useraddress => {
        name => 'Adresse',
        attrs => [ qw(
            initials
            mail
        ) ],
    },
    usermy => {
        name => 'My',
        attrs => [ qw(
            snNative
            givenNameNative
            wWWHomePage
            halReference
        ) ],
    },
    site => {
        name => 'Site',
        attrs => [ qw(
            description
            siteNick
            streetAddress
            postOfficeBox
            postalCode
            l
            st
            co
            facsimileTelephoneNumber
            create
            date
        ) ],
    },
    address => {
        name => 'Adresse',
        attrs => [ qw(
            isMainAddress
            telephoneNumber
            streetAddress
            postOfficeBox
            postalCode
            l
            st
            physicalDeliveryOfficeName
            site
            co
            unexported
            description
        ) ],
    },
    group => {
        name => 'Système',
        attrs => [ qw(
            gidNumber description
            managedBy
            managedAlsoBy
            autoMemberFilter
            sutype
            create
            date
        ) ],
    },
    nethost => {
        name => 'Ordinateur',
        attrs => [ qw(
            name
            description
            serialNumber
            owner
            user
            ip
            macaddr
            puppetClass
            noInheritPuppet
            noDynamic
            cname
            otherName
            reverse
            create
            date
            unexported
        ) ],
    },
    netzone => {
        name => 'Zone réseau',
        attrs => [ qw(
            name
            description
            type
            net
            netExclude
            puppetClass
            outputD
            templateD
            site
            allow_dyn
            dynFrom
            domain
            ipCount
            freeIPCount
            create
            date
            dnsRevision
            lastUpdate
            unexported
        ) ],
    },
    aliases => {
        name => 'Alias mail',
        attrs => [ qw(
            forward
            expire
            finalpoint
            parents
            created
            date
            unexported
        )],
    },
};

sub escape {
    my ($self, $text) = @_;
    $text ||= '';
    for ($text) {
        s/&/&amp;/g;
        s/</&lt;/g;
        s/>/&gt;/g;
        s/"/&quot;/g;
    }
    $text;
}

sub new {
    my ($class) = @_;
    bless({}, $class);
}

# call either OBJ or type + base

sub ACCEPT_CONTEXT {
    my ($self, $c, $form, $object, $base) = @_;
    my $new = {};
    $new->{c} = $c;
    $new->{form} = $form;
    $new->{object} = $object if (ref $object);
    $new->{base} = $base || ($object ? $object->base : undef) or return $self;
    $new->{otype} = ref $object ? $object->type : $object;
    bless($new, __PACKAGE__);
}

sub base {
    my ( $self ) = @_;
    $self->{base}
}

sub otype {
    my ($self) = @_;
    $self->{otype};
}

sub label {
    my ($self) = @_;
    $forms->{$self->{form}}->{name} || ''
}

sub attributes {
    my ($self, $for) = @_;
    grep { $_ }
    grep { $self->base->attribute($self->otype, $_) }
    @{ $forms->{$self->{form}}->{attrs} };
}

sub attr_label {
    my ($self, $attr, $label, $hint) = @_;
    $label ||= '';
    $hint ||= '';
    LATMOS::Accounts::Web->config->{attrs} ||= {};
    my $attribute = $self->base->attribute($self->otype, $attr);
    my $htmlname = $self->escape($self->attr_field_name($attr));
    if (my $attr_config = LATMOS::Accounts::Web->config->{attrs}{$attr}) {
        $label ||= $attr_config->{label} || '';
        #utf8::is_utf8($label) && utf8::encode($label);
        $hint ||= $attr_config->{hint} || '';
        #utf8::is_utf8($hint) && utf8::encode($hint);
    }
    return sprintf('<label %sfor="%s">%s</label>',
        ($hint ? sprintf('title="%s"', $self->escape($hint)) : ''),
        $self->escape($htmlname),
        $self->escape($label || $attr),
    );
}

sub attr_hint {
    my ($self, $attr) = @_;
    LATMOS::Accounts::Web->config->{attrs} ||= {};
    if (my $attr_config = LATMOS::Accounts::Web->config->{attrs}{$attr}) {
        return $attr_config->{hint} || '';
    }
    return;
}

sub attr_raw_value {
    my ($self, $attribute) = @_;
    my @val = grep { $_ }  
        ($self->{c}->req->param($attribute->name))
        || ($self->{object}
            ? ($self->{object}->get_attributes($attribute->name))
            : ());

    if (@val) {
        return @val;
    } else {
        return (grep { $_ } ($attribute->default_value))
    }
}

sub attr_field_name {
    my ($self, $attr) = @_;
    my $id = $self->{object}
        ? ($self->{object}->id || '')
        : '';
    $id .= '_' if ($id);
    $id =~ s/\./_/g;
    $id =~ s/-/_/g;
    $id . $attr;
}

sub attr_item {
    my ($self, $attribute, $value, $htmlid) = @_;

    my $html = $self->_attr_item($attribute, $value, $htmlid);
    
    if ($attribute->uniq) {
        $html .= qq|<span class="inputvalidate" id="${htmlid}_uniq"></span>|;
    }
    if (my $ref = $attribute->reference) {
        my $uri_part = $self->_uri_part($ref);
        if ($self->base->attribute($ref, 'displayName')) {
            my $text = '';
            if ($value &&
                (my $obj = $self->base->get_object($ref, $value))) {
                $text = $obj->get_attributes('displayName');
            }
            $html .= sprintf(
                qq|<span id="${htmlid}_ref" style="margin-left: 1em">$text</span>|,
                $self->escape($text),
            );
        } elsif($value && $uri_part) {
            $html .= sprintf(
                '<a href="%s"><img class="attr" src="%s" title="%s"></a>',
                $self->{c}->uri_for('/', $uri_part, $value),
                $self->{c}->uri_for('/static', 'images', 'arrow-right.png'),
                $value,
            );
        }
    }

    $html
}

sub _attr_item {
    my ($self, $attribute, $value, $htmlid) = @_;

    my $htmlname = $self->escape($self->attr_field_name($attribute->name));
    $htmlid ||= $htmlname;

    for ($attribute->form_type) {
            /^textarea$/i and do {
                return sprintf(
                    '<textarea id="%s" name="%s" cols="40">%s</textarea>',
                    $self->escape($htmlid),
                    $self->escape($htmlname),
                    $self->escape($value || ''),
                );
                last;
            };

            /^label$/i and do {
                $value or last;
                my $html_field = $self->escape($value);
                $html_field =~ s/\n/<br>/g;
                $html_field .= sprintf('<input type="hidden" name="%s" value="%s">',
                    $self->escape($htmlname), ($value || ''));
                return $html_field;
                last;
            };

            /^date$/i and do {
                my ($date, $time) = split(/ /, $value || '');
                if ($date && $date =~ /^(\d+)-(\d+)-(\d+)$/) {
                    $date = "$3/$2/$1";
                }
                my $html = "\n" . q{<SCRIPT LANGUAGE="JavaScript" ID="js13">
                var cal13 = new CalendarPopup();
                </SCRIPT>} . "\n";
                $html .= sprintf(
                    '<input type="text" id="%s" name="%s" value="%s" size="12">',
                    $self->escape($htmlid),
                    $self->escape($htmlname),
                    $self->escape($date)
                );
                $html .= q{<DIV ID="testdiv1" STYLE="position:absolute;visibility:hidden;background-color:white;layer-background-color:white;"></DIV>};
                $html .= qq|
                <A HREF="#"
                    onClick="cal13.select(document.getElementById('$htmlid'),'${htmlid}_anc','dd/MM/yyyy');return false;"
                    TITLE="Date"
                    NAME="${htmlid}_anc" ID="${htmlid}_anc">
                    <img class="attr" src="| . $self->{c}->uri_for(qw(/static images view-calendar-day.png))
                    . qq{" style="ref"></A>
                    } . "\n";
                return $html;
                last;
            };

            /^checkbox/i and do {
                my $options = $1 || '';
                my $html = sprintf('<input type="checkbox" id="%s" name="%s"%s>',
                    $htmlid,
                    $self->escape($htmlname),
                    $value ? '  checked="yes"' : ''
                ); 
                $html .= sprintf('<input type="hidden" name="%s">',
                    $self->escape($htmlname));
                if ($attribute->form_option('rawvalue')) {
                    $html .= $value
                        ? ' ' . $value
                        : ''; 
                }
                return $html;
                last;
            };

            /^LIST/i and do {
                my $select = sprintf('<select id="%s" name="%s">',
                    $self->escape($htmlid),
                    $self->escape($htmlname)) . "\n";
                $select .= '<option value="">--</option>' . "\n"
                    unless($attribute->mandatory);
                $value ||= '';
                my $initial_observed = '';
                my @valslist;
                foreach my $val (sort $attribute->can_values) {
                    push(@valslist, {
                        val => $val,
                        disp => $attribute->display($val || ''),
                    });
                }
                foreach (sort { $a->{disp} cmp $b->{disp} } @valslist) {
                    $select .= sprintf(
                        '    <option value="%s"%s>%s</option>',
                        $self->escape($_->{val} || ''),
                        $value eq $_->{val} ? ' selected="selected"' : '',
                        $self->escape($_->{disp} || ''),
                    );
                    $select .= "\n";
                }
                $select .= "</select>\n";

                return $select;
                last;
            };

            /^text/i and do {
                return sprintf(
                    '<input type="text" id="%s" name="%s" value="%s" size="%d">',
                    $self->escape($htmlid),
                    $self->escape($htmlname),
                    $self->escape($value),
                    $attribute->form_option('length') || 30,
                );
                last;
            };
        }
    return '';
}

sub _uri_part {
    my ($self, $ref) = @_;
    my $uri_part = {
        user => 'users',
        group => 'groups',
        nethost => 'nethosts',
        netzone => 'netzones',
        site => 'sites',
        aliasess => 'aliases',
    }->{$ref};
}

sub attr_js {
    my ($self, $attribute, $value, $htmlid) = @_;

    my (@js);

    if (my $ref = $attribute->reference) {
        my $uri_part = $self->_uri_part($ref);
        if ($self->base->attribute($ref, 'displayName')) {
            push(@js, 
                $self->{c}->prototype->observe_field( $htmlid,
                    {
                        update => "${htmlid}_ref",
                        url => $self->{c}->uri_for('/ajax', 'rawattr', $ref),
                        frequency => 1,
                        with   => "'attr=displayName&id=' + element.options[element.selectedIndex].text",
                    }
                )
            ) if ($attribute->form_type =~ /list/i);
        }
    }

    if ($attribute->uniq) {
        push(@js,
            $self->{c}->prototype->observe_field( $htmlid,
                {
                    update => "${htmlid}_uniq",
                    url => $self->{c}->uri_for('/ajax', 'objattrexist',
                        $self->otype, $attribute->name),
                    frequency => 1,
                    with => "'val='+element.value" .
                    ($self->{object} ?
                        "+'&exclude=" . ($self->{object}->id || '') . "'" 
                        : ''),
                }
            )
        );
    }

    return(join("\n", @js));
}

sub attr_field {
    my ($self, $attr, $type) = @_;

    my $attribute;
    if ($type) {
        if (ref $type) {
            $attribute = $self->base->attribute(
                $self->otype,
                { %$type, name => $attr },
            );
        } else {
            $attribute =  $self->base->attribute(
                $self->otype,
                { formtype => $type , name => $attr },
            );
        }
    } else {
        if ($self->{object}) {
            $attribute = $self->{object}->attribute($attr);
        } else {
            $attribute = $self->base->attribute(
                $self->otype, $attr,
                { formtype => $type }
            );
        }
    }
    $attribute ||= $self->base->attribute($self->otype, { name => $attr });


    my $htmlname = $self->escape($self->attr_field_name($attr));

    my @html_fields;

    # Force Array context:
    my @attr_raw_value = ();

    if ($attribute->multiple) {
        @attr_raw_value = grep { $_ } $self->attr_raw_value($attribute);
    } else {
        my ($val) = $self->attr_raw_value($attribute);
        @attr_raw_value = ($val || '');
    }

    foreach my $attr_raw_value (@attr_raw_value) {

        my $html_id = $htmlname .
            (scalar(@html_fields) ? scalar(@html_fields) : '');
        push(@html_fields,
            join("\n",
                $self->attr_item(
                    $attribute, $attr_raw_value, $html_id
                ),
                $self->attr_js(
                    $attribute, $attr_raw_value, $html_id
                )
            )
        );
    }

    if ($attribute->{multiple} && !$attribute->readonly) {
        my $html_id = $htmlname . '_m_SUBID';

        my $attr = $self->attr_item($attribute, '', $html_id);
        # JS: string are on the line only
        $attr =~ s/\n//mg;
        $attr =~ s/'/\'/g;

        my $js = $self->attr_js($attribute, '', 'NANAR');
        $js =~ s!'NANAR([_\w]*)'!('$html_id$1'.replace(/SUBID/g, cn))!gm;
        $js =~ s!<script[^>]+>!!gm;
        $js =~ s!</script>!!gm;
        $js =~ s!-->!!gm;
        $js =~ s/<!--//gm;

        my $addempty = sprintf('<div id="%s"></div>', "${htmlname}_div_0");
        $addempty .= "\n";
        $addempty .= sprintf(qq#
            <script type="text/javascript">
            var cn_${htmlname} = 0;
            function add_${htmlname} () {
            cn_${htmlname}++;
            // It work only using temporary variable
            var cn = cn_${htmlname};
            document.getElementById('${htmlname}_div_' + (cn - 1)).innerHTML +=
                '$attr'.replace(/SUBID/g, cn );
            document.getElementById('${htmlname}_div_' + (cn - 1)).innerHTML +=
                '<div id="${htmlname}_div_'+ cn +'" style="display:block"></div>';
            %s
            }
            </script>
            #, $js
        );

        $addempty .= "\n";
        $addempty .=
            qq|<button type="button" style="display:block" onclick="add_${htmlname}()">+</button>|,

        push(@html_fields, $addempty);
    }

    return join("<br>\n", @html_fields);
}

sub submit {
    my ($self) = @_;
    return sprintf(
        '<input type="submit" name="%s" value="Enregistrer">',
        $self->escape($self->label),
    );
}

sub write_attributes {
    my ($self) = @_;
    my @attrs;
    foreach ($self->attributes) {
        my $attr = ($self->{object}
            ? $self->{object}->attribute($_)
            : $self->base->attribute($self->otype, $_)) or next;
        $attr->readonly and next;
        push(@attrs, $_);
    }
    @attrs;
}

sub set_attrs {
    my ($self, $attrs) = @_;
    $self->{c}->req->param($self->label) || $attrs or return;
    my $prefix = $self->attr_field_name('');
    my %fields;
    foreach ($attrs ? @{ $attrs } : $self->write_attributes) {
        my $attr = ($self->{object}
            ? $self->{object}->attribute($_)
            : $self->base->attribute($self->otype, $_)) or next;
        if ($attr->{multiple}) {
            $fields{$_} = [ grep { $_ } $self->{c}->req->param("$prefix$_") ];
        } else {
            $fields{$_} = $self->{c}->req->param("$prefix$_");
        }
    }
    $self->{object}->set_c_fields(%fields) or do {
        $self->{c}->stash->{page}{error} =
            LATMOS::Accounts::Log::lastmessage(LA_ERR);
        $self->{object}->base->rollback;
        return;
    };
    $self->{object}->base->commit;
}

=head1 AUTHOR

Thauvin Olivier

=head1 LICENSE

This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

1;
