# Movable Type (r) Open Source (C) 2001-2010 Six Apart, Ltd.
# This program is distributed under the terms of the
# GNU General Public License, version 2.
#
# $Id: TemplateSet.pm 5229 2010-01-27 08:53:11Z asawada $
package MT::Theme::TemplateSet;
use strict;
use MT;
use MT::Util qw( dirify );

sub apply {
    my ( $element, $theme, $obj_to_apply, $opts ) = @_;
    my $set = $element->{data};
    if ( !ref $set ) {
        $set = MT->registry('template_sets', $set);
    }

    ## deep localize for labels
    $theme->__deep_localize_labels($set);

    ## taken from MT::CMS::Template
    my $backup = exists $opts->{backup} ? $opts->{backup} : 1;
    my $blog = $obj_to_apply;
    my $blog_id = $blog->id;
    my $t = time;
    my @ts = MT::Util::offset_time_list( $t, $blog_id );
    my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
        $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];

    my $tmpl_iter = MT->model('template')->load_iter({
        blog_id => $blog->id,
        type => { not => 'backup' },
    });

    while (my $tmpl = $tmpl_iter->()) {
        if ($backup) {
            # zap all template maps
            require MT::TemplateMap;
            MT::TemplateMap->remove({
                template_id => $tmpl->id,
            });
            $tmpl->name( $tmpl->name
                    . ' (Backup from '
                    . $ts . ') '
                    . $tmpl->type );
            $tmpl->type('backup');
            $tmpl->identifier(undef);
            $tmpl->rebuild_me(0);
            $tmpl->linked_file(undef);
            $tmpl->outfile('');
            $tmpl->save;
        } else {
            $tmpl->remove;
        }
    }

    if ($blog_id) {
        # Create the default templates and mappings for the selected
        # set here, instead of below.
        if ( ref $set ) {
            $set->{envelope} = $theme->path;
        }

        local MT->app->{component} = $theme->id;
        $blog->create_default_templates( $set );
    }

    return 1;
}

sub info {
    my ( $element, $theme, $blog ) = @_;
    my $set = $element->{data};
    if ( !ref $set ) {
        $set = MT->registry('template_sets' => $set );
    }
    return unless 'HASH' eq ref $set;
    my $templates = $set->{templates} eq '*' ? MT->registry("default_templates")
                  :                            $set->{templates}
                  ;
    return unless 'HASH' eq ref $templates;
    my %counts = ( widget => 0, widgetset => 0, template => 0 );
    for my $group_key ( keys %$templates ) {
        next if 'HASH' ne ref $templates->{$group_key};
        my $group = $group_key eq 'widget'    ? 'widget'
                  : $group_key eq 'widgetset' ? 'widgetset'
                  :                             'template'
                  ;
        $counts{$group} += scalar keys %{ $templates->{$group_key} };
    }
    return sub {
        MT->translate(
            'A template set containing [_1] templates, [_2] widgets, and [_3] widget sets.',
            $counts{template},
            $counts{widget},
            $counts{widgetset},
        );
    };
}

sub condition {
    my ( $blog ) = @_;
    # Export if at least one template;
    my $tmpl = MT->model('template')->load({ blog_id => $blog->id }, { limit => 1 });
    return defined $tmpl ? 1 : 0;
}

sub export_template {
    my $app = shift;
    my ( $blog, $saved ) = @_;
    require MT::CMS::Template;
    my %checked;
    if ( $saved ) {
        %checked = map { $_ => 1 } @{$saved->{template_set_export_ids}};
    }
    my @templates = MT->model('template')->load({
        blog_id => $blog->id,
        type    => { 'not' => 'backup'},
    });

    my %types = (
        'index' => {
            label => $app->translate("Index Templates"),
            order => 100,
        },
        'archive' => {
            label => $app->translate("Archive Templates"),
            order => 200,
        },
        'module' => {
            label => $app->translate("Template Modules"),
            order => 300,
        },
        'system' => {
            label => $app->translate("System Templates"),
            order => 400,
        },
        'email' => {
            label => $app->translate("Email Templates"),
            order => 500,
        },
        'widget' => {
            label => $app->translate("Widget Templates"),
            order => 600,
        },
        'widgetset' => {
            label => $app->translate("Widget Sets"),
            order => 700,
        },
    );
    my %tmpl_groups;
    my %id_map = map {
        $_->name => $_->id,
    } @templates;
    for my $tmpl ( @templates ) {
        my $type = $tmpl->type;
        my $template_type;
        if ( $type =~ m/^(individual|page|category|archive)$/ ) {
            $template_type = 'archive';
        }
        elsif ( $type eq 'widget' ) {
            $template_type = 'widget';
        }
        elsif ( $type eq 'widgetset' ) {
            $template_type = 'widgetset';
        }
        elsif ( $type eq 'index' ) {
            $template_type = 'index';
        }
        elsif ( $type eq 'custom' ) {
            $template_type = 'module';
        }
        elsif ( $type eq 'email' ) {
            $template_type = 'email';
        }
        else {
            $template_type = 'system';
        }

        my $group = $tmpl_groups{$template_type} ||= {
            order               => $types{$template_type}{order},
            template_type       => $template_type,
            template_type_label => $types{$template_type}{label},
            object_loop         => [],
        };
        my $obj = {
            tmpl_label    => $tmpl->name,
            tmpl_id       => $tmpl->id,
            checked       => $saved ? $checked{$tmpl->id} : 1,
        };
        if ( $template_type =~ /index/ ) {
            $obj->{outfile} = $tmpl->outfile;
        }
        if ( $template_type eq 'archive' ) {
            $obj->{archive_types} = MT::CMS::Template::_populate_archive_loop( $app, $blog, $tmpl );
        }

        my $tokens = $tmpl->compile;
        my @includes = (
            @{ $tokens->getElementsByTagName('include')      || [] },
            @{ $tokens->getElementsByTagName('includeblock') || [] },
        );
        for my $include ( @includes ) {
            my $module = $include->getAttribute('module')
                      || $include->getAttribute('widget');
            my $module_id = $id_map{$module};
            push @{$obj->{includes} ||= []}, $module_id if $module_id;
        }
        push @{ $group->{templates} }, $obj;
    }

    for my $group ( values %tmpl_groups ) {
        $group->{templates} = [
            sort {
                $a->{tmpl_label} cmp $b->{tmpl_label}
            }
            @{ $group->{templates} }
        ];
    }
    my %param = (
        tmpl_groups => [
            sort { $a->{order} <=> $b->{order} }
            values %tmpl_groups
        ],
    );
    return $app->load_tmpl('include/theme_exporters/templateset.tmpl', \%param);
}

{
    my %known_section = (
        index      => 1,
        archive    => 1,
        module     => 1,
        widget     => 1,
        widgetset  => 1,
    );

    sub _type {
        my $tmpl = shift;
        my $type = $tmpl->type;
        return $known_section{$type} ? $type
             : $type eq 'custom'     ? 'module'
             : $type eq 'individual' ? ( $tmpl->identifier eq 'page' ? 'page' : 'individual' )
             : $type eq 'page'       ? 'page'
             :                         'system'
             ;
    }
}

sub export {
    my $app = shift;
    my ( $blog, $setting ) = @_;
    my @templates;
    if ($setting) {
        @templates = MT->model('template')->load({
            id => $setting->{template_set_export_ids}
        });
    }
    else {
        @templates = MT->model('template')->load({
            blog_id => $blog->id,
            type    => { not => 'backup' }
        });
    }
    return unless scalar @templates;

    my $data = {};
    for my $t (@templates) {
        my $type = _type( $t );
        $data->{$type} ||= {};
        my $this = {
            label => $t->name,
        };
        if ( $type eq 'index' ) {
            $this->{outfile}    = $t->outfile;
            $this->{rebuild_me} = $t->rebuild_me;
        };
        if ( $type eq 'archive' || $type eq 'individual' || $type eq 'page' ) {
            my @maps = MT->model('templatemap')->load({ template_id => $t->id });
            $this->{mappings} = {} if scalar @maps;
            my %type_count;
            for my $map ( @maps ) {
                my $map_data = {
                    archive_type => $map->archive_type,
                    preferred    => $map->is_preferred,
                };
                $map_data->{file_template} = $map->file_template
                    if $map->file_template;
                my $type_count = $type_count{$map->archive_type}++;
                my $map_id = dirify( $map->archive_type );
                $map_id .= "_$type_count" if $type_count > 0;
                $this->{mappings}->{$map_id} = $map_data;
            }
        }
        if ( $type eq 'widgetset' ) {
            ## FIXME: this regex.
            my @widgets = $t->text =~ /<mt:include widget="([^\"]+)">/g;
            $this->{widgets} = \@widgets;
            $this->{order} = 1000;
        }
        $data->{$type}->{ $t->identifier || 'template_' . $t->id } = $this;
    }
    return {
        label     => 'exported_template set',
        base_path => 'templates',
        templates => $data,
        objs       => \@templates,

    };
}

sub finalize {
    my $app = shift;
    my ( $blog, $theme_hash, $tmpdir, $setting ) = @_;
    my $ts_hash = $theme_hash->{elements}{template_set}
        or return 1;
    my $templates = $ts_hash->{data}{objs};
    delete $ts_hash->{data}{objs};

    require MT::FileMgr;
    require File::Spec;
    my $fmgr = MT::FileMgr->new('Local');
    my $outdir = File::Spec->catdir( $tmpdir, 'templates' );
    $fmgr->mkpath( $outdir )
        or return $app->error(
            $app->translate(
                'Failed to make templates directory: [_1]',
                $fmgr->errstr,
        ));
    for my $t (@$templates) {
        my $tmpl_id = $t->identifier || 'template_' . $t->id;
        my $path = File::Spec->catfile( $outdir, $tmpl_id . '.mtml' );
        defined $fmgr->put_data( $t->text, $path )
            or return $app->error(
                $app->translate(
                    'Failed to publish template file: [_1]',
                    $fmgr->errstr,
            ));
    }
    return 1;
}

1;
