#/*
# *  Copyright 2007-2008 hkrn <hikarin@users.sourceforge.jp>
# *
# *  Licensed under the Apache License, Version 2.0 (the "License");
# *  you may not use this file except in compliance with the License.
# *  You may obtain a copy of the License at
# *
# *      http://www.apache.org/licenses/LICENSE-2.0
# *
# *  Unless required by applicable law or agreed to in writing, software
# *  distributed under the License is distributed on an "AS IS" BASIS,
# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# *  See the License for the specific language governing permissions and
# *  limitations under the License.
# */
#
# $Id: Config.pm 1645 2008-02-06 15:40:18Z hikarin $
#

package Zeromin::Plugin::Config;

use strict;
use HTML::Element qw();

sub new {
    my ( $zClass, $zPlugin, $id, $bbs, $key ) = @_;
    bless {
        __bbs    => $bbs,
        __fkeys  => [],
        __html   => HTML::Element->new('div'),
        __id     => $id,
        __js     => '',
        __key    => $key,
        __plugin => $zPlugin,
    }, $zClass;
}

sub add_input_text_form {
    my ( $zPConfig, $name, $attrs ) = @_;
    $zPConfig->_add_input_form( $name, $attrs, 'text' );
}

sub add_input_password_form {
    my ( $zPConfig, $name, $attrs ) = @_;
    $zPConfig->_add_input_form( $name, $attrs, 'password' );
}

sub add_input_hidden_form {
    my ( $zPConfig, $name, $attrs ) = @_;
    $zPConfig->_add_input_form( $name, $attrs, 'hidden' );
}

sub _add_input_form {
    my ( $zPConfig, $name, $attrs, $type ) = @_;
    my $data
        = $zPConfig->{__plugin}
        ->retrive( $zPConfig->{__id}, $zPConfig->{__bbs}, $zPConfig->{__key},
        $name, );

    $attrs ||= {};
    my $tag = HTML::Element->new('input');
    while ( my ( $key, $value ) = each %{$attrs} ) {
        $tag->attr( $key, $value );
    }

    my $value = defined $data->{$name} ? $data->{$name} : $tag->attr('value');
    $tag->attr( 'id',    'plugin_config_' . $name );
    $tag->attr( 'name',  $name );
    $tag->attr( 'type',  $type );
    $tag->attr( 'value', $value );
    $zPConfig->{__html}->push_content($tag);
    push @{ $zPConfig->{__fkeys} }, $name;
    return;
}

sub add_textarea_form {
    my ( $zPConfig, $name, $attrs ) = @_;
    my $data
        = $zPConfig->{__plugin}
        ->retrive( $zPConfig->{__id}, $zPConfig->{__bbs}, $zPConfig->{__key},
        $name, );

    $attrs ||= {};
    my $tag = HTML::Element->new('textarea');
    while ( my ( $key, $value ) = each %{$attrs} ) {
        $tag->attr( $key, $value );
    }
    $tag->attr( 'name', $name );
    my $data_array = $data->{$name} || [];
    $tag->push_content( join "\n", @{$data_array} );
    $zPConfig->{__html}->push_content($tag);
    push @{ $zPConfig->{__fkeys} }, $name;
    return;
}

sub add_select_form {
    my ( $zPConfig, $name, $values, $multiple, $attrs ) = @_;
    my $data
        = $zPConfig->{__plugin}
        ->retrive( $zPConfig->{__id}, $zPConfig->{__bbs}, $zPConfig->{__key},
        $name, );

    $attrs  ||= {};
    $values ||= [];
    my $tag = HTML::Element->new('select');
    while ( my ( $key, $value ) = each %{$attrs} ) {
        $tag->attr( $key, $value );
    }
    $tag->attr( 'name', $name );
    $multiple and $tag->attr( 'multiple', 'multiple' );
    my $data_array = $data->{$name} || [];
    for my $value ( @{$values} ) {
        ( ref $value || '' ) eq 'ARRAY' or next;
        my $opt        = HTML::Element->new('option');
        my $value_attr = $value->[0];
        $opt->attr( 'value', $value_attr );
        if ( ( grep { $value_attr eq $_ } @{$data_array} ) > 0 ) {
            $opt->attr( 'selected', 'selected' );
        }
        $opt->push_content( $value->[1] );
        $tag->push_content($opt);
    }
    $zPConfig->{__html}->push_content($tag);
    push @{ $zPConfig->{__fkeys} }, $name;
    return;
}

sub add_checkbox_form {
    my ( $zPConfig, $name, $attrs ) = @_;
    my $data
        = $zPConfig->{__plugin}
        ->retrive( $zPConfig->{__id}, $zPConfig->{__bbs}, $zPConfig->{__key},
        $name, );

    $attrs ||= {};
    my $tag = HTML::Element->new('input');
    while ( my ( $key, $value ) = each %{$attrs} ) {
        $tag->attr( $key, $value );
    }

    $tag->attr( 'id',    'plugin_config_' . $name );
    $tag->attr( 'name',  $name );
    $tag->attr( 'type',  'checkbox' );
    $tag->attr( 'value', 1 );
    $data->{$name} and $tag->attr( 'checked', 1 );
    $zPConfig->{__html}->push_content($tag);
    push @{ $zPConfig->{__fkeys} }, $name;
    return;
}

sub add_text {
    my ( $zPConfig, $text ) = @_;
    my @container;
    $text =~ s/\A\s+//xms;
    $text =~ s/\s+\z//xms;
    $text =~ s/\A\n//xms;
    $text =~ s/\n\z//xms;
    if ( index( $text, "\n" ) >= 0 ) {
        my @texts = split "\n", $text;
        push @container, shift @texts;
        map { push @container, ['br'], $_ } @texts;
    }
    else {
        push @container, $text;
    }
    $zPConfig->{__html}->push_content(@container);
    return;
}

sub add_label {
    my ( $zPConfig, $name, $text, $attrs ) = @_;
    my $tag = HTML::Element->new('label');
    while ( my ( $key, $value ) = each %{$attrs} ) {
        $tag->attr( $key, $value );
    }
    $tag->attr( 'for', 'plugin_config_' . $name );
    $tag->push_content($text);
    $zPConfig->{__html}->push_content($tag);
    return;
}

sub add_list {
    my ( $zPConfig, $text ) = @_;
    my @container = ();
    $text =~ s/\A\s+//xms;
    $text =~ s/\s+\z//xms;
    $text =~ s/\A\n//xms;
    $text =~ s/\n\z//xms;
    if ( index( $text, "\n" ) >= 0 ) {
        my @texts = split "\n", $text;
        map { push @container, HTML::Element->new('li')->push_content($_) }
            @texts;
    }
    else {
        my $li = HTML::Element->new('li');
        push @container, $li->push_content($text);
    }
    my $ul = HTML::Element->new('ul');
    map { $ul->push_content($_) } @container;
    $zPConfig->{__html}->push_content($ul);
    return;
}

sub parse_from_hash {
    my ( $zPConfig, $config ) = @_;
    my $funcs = {
        'text' => sub {
            my ( $zPConfig, $key, $value, $label ) = @_;
            $label and $zPConfig->add_label( $key, $label );
            $zPConfig->add_input_text_form( $key, { value => $value } );
        },
        'password' => sub {
            my ( $zPConfig, $key, $value, $label ) = @_;
            $label and $zPConfig->add_label( $key, $label );
            $zPConfig->add_input_password_form( $key, { value => $value } );
        },
        'hidden' => sub {
            my ( $zPConfig, $key, $value, $label ) = @_;
            $label and $zPConfig->add_label( $key, $label );
            $zPConfig->add_input_hidden_form( $key, { value => $value } );
        },
        'textarea' => sub {
            my ( $zPConfig, $key, $value, $label ) = @_;
            $label and $zPConfig->add_label( $key, $label );
            $zPConfig->add_textarea_form( $key, $value );
        },
        'checkbox' => sub {
            my ( $zPConfig, $key, $value, $label ) = @_;
            $zPConfig->add_checkbox_form($key);
            $label
                and $zPConfig->add_label( $key, $label,
                { style => 'display:inline' } );
            }
    };

    my $body           = $config->{layout};
    my $container_hash = {};
    while ( my ( $tag, $containers ) = each %$body ) {
        ( ( ref $containers || '' ) eq 'ARRAY' )
            or $containers = [$containers];
        for my $container (@$containers) {
            $container->{'-tag'} = $tag;
            $container_hash->{ $container->{'-layout-order'} } = $container;
        }
    }

    for my $container (
        map { $_->[1] } sort { $a->[0] <=> $b->[0] }
        map { [ $_, $container_hash->{$_} ] } keys %$container_hash
        )
    {
        my $tag = $container->{'-tag'};
        if ( $tag eq 'input' ) {
            my $func  = $funcs->{ $container->{type} } || next;
            my $name  = delete $container->{name};
            my $def   = delete $container->{default};
            my $label = delete $container->{label};
            $func->( $zPConfig, $name, $def, $label );
            $zPConfig->{__html}->push_content( ['br'] );
        }
        elsif ( $tag eq 'list' ) {
            $zPConfig->add_list( $container->{'#text'} );
        }
        elsif ( $tag eq 'description' || $tag eq 'text' ) {
            $zPConfig->add_text( $container->{'#text'} );
        }
    }
    return;
}

sub set_javascript_code {
    my ( $zPConfig, $jscode ) = @_;
    $zPConfig->{__js} = $jscode;
    return;
}

sub render_html {
    my ($zPConfig) = @_;
    my $he = $zPConfig->{__html};

    for my $name ( @{ $zPConfig->{__fkeys} } ) {
        my $hidden = HTML::Element->new('input');
        $hidden->attr( 'name',  'config' );
        $hidden->attr( 'type',  'hidden' );
        $hidden->attr( 'value', $name );
        $he->push_content($hidden);
    }
    $he->push_content(
        HTML::Element->new(
            'input',
            'name'  => 'bbs',
            'type'  => 'hidden',
            'value' => $zPConfig->{__bbs},
        ),
        HTML::Element->new(
            'input',
            'name'  => 'key',
            'type'  => 'hidden',
            'value' => $zPConfig->{__key},
        ),
        HTML::Element->new(
            'input',
            'name'  => 'id',
            'type'  => 'hidden',
            'value' => $zPConfig->{__id},
        ),
    );
    @{ $zPConfig->{__fkeys} } = ();

    my $html = $he->as_HTML('<>&"\'');
    $he->delete_content();
    return \$html;
}

sub render_javascript {
    my ($zPConfig) = @_;
    return \$zPConfig->{__js};
}

sub serialize {
    my ( $zClass, $param ) = @_;
    my $ret = {};
    while ( my ( $key, $value ) = each %$param ) {
        my $reftype = ref $value || '';
        if ( $reftype eq 'ARRAY' and @$value <= 1 ) {
            $ret->{$key} = join '', @$value;
            next;
        }
        elsif ( $reftype eq '' and index( $value, "\n" ) >= 0 ) {
            $value =~ s/\r\n/\n/gxms;
            $ret->{$key} = [ split "\n", $value ];
            next;
        }
        $ret->{$key} = $value;
    }
    return $ret;
}

sub get_plugin_instance { $_[0]->{__plugin} }

1;
__END__
