package Bigtop::Backend::Model::GantryDBIxClass;
use strict; use warnings;

use Bigtop::Backend::Model;
use File::Spec;
use Inline;
use Bigtop;

#-----------------------------------------------------------------
#   The Default Template
#-----------------------------------------------------------------

our $template_is_setup = 0;
our $default_template_text = <<'EO_TT_blocks';
[% BLOCK stub_base_model %]
package [% app_name %]::Model;
use strict; use warnings;

use base 'DBIx::Class::Schema';

use Gantry::Utils::ModelHelper qw( db_Main );

__PACKAGE__->load_classes( qw/
[% FOREACH table_model IN table_models %]
    [% table_model %]

[% END %]
/ );

# DBIx::Class wants a code reference which returns the dbh.  Give
# the people what they want.
sub gen_db_Main {
    my $class = shift;
    return sub {
        return $class->db_Main();
    };
}

sub get_db_options {
    return { AutoCommit => 1 };
}

1;

=head1 NAME

[% app_name %]::Model - schema class for [% app_name %]


=head1 SYNOPSIS

In your base module:

    use [% app_name %]::Model;
    sub schema_base_class { return '[% app_name %]::Model'; }
    use Gantry::Plugins::DBIxClassConn qw( get_schema );

[%- FOREACH table_model IN table_models -%]
    use [% app_name %]::Model::[% table_model %] qw( $[% table_model | upper %] );

[%- END -%]

=head1 DESCRIPTION

This module was generated by Bigtop.  But, feel free to edit it.  You
might even want to update these docs.

=over 4

=item gen_db_Main

=item get_db_options

The generated version sets AutoCommit to 1, this assumes that you will
do all transaction work via the DBIx::Class API.

=back

=head1 DEPENDENCIES

    Gantry::Utils::DBIxClass

=head1 AUTHOR

[% FOREACH author IN authors %]
[% author.0 %][% IF author.1 %], E<lt>[% author.1 %]E<gt>[% END %]

[% END %]

=head1 COPYRIGHT AND LICENSE

Copyright (C) [% year %] [% copyright_holder %]


[% IF license_text %]
[% ELSE %]
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
[% END %]

=cut
[% END %]

[% BLOCK stub_table_module %]
package [% package_name %];
use strict; use warnings;

use base '[% gen_package_name %]', 'Exporter';

our $[% package_alias %] = '[% package_name %]';

our @EXPORT_OK = ( '$[% package_alias %]' );

1;

=head1 NAME

[% package_name %] - model for [% table_name %] table (stub part)

=head1 DESCRIPTION

This model inherits from its generated helper, which inherits from
[% base_class || base_class_default %].  It was generated by Bigtop, but is
NOT subject to regeneration.

=cut
[% END %]

[% BLOCK gen_table_module %]
# NEVER EDIT this file.  It was generated and will be overwritten without
# notice upon regeneration of this application.  You have been warned.
package [% gen_package_name %];
use strict; use warnings;

use base '[% base_class || base_class_default %]';

__PACKAGE__->load_components( qw/ Core / );
__PACKAGE__->table( '[% table_name %]' );
__PACKAGE__->add_columns( qw/
[% FOREACH column IN all_columns %]
    [% column +%]
[% END %]
/ );
__PACKAGE__->set_primary_key( '[% primary_key +%]' );
[% FOREACH has_a IN has_a_list %]
[% package_name %]->has_a( [% has_a.column %] => '[% base_package_name %]::[% has_a.table %]' );
[% END +%]
sub get_foreign_display_fields {
    return [ qw( [% foreign_display_columns %] ) ];
}

sub get_foreign_tables {
    return qw(
[% FOREACH foreign_table IN foreign_tables %]
        [% base_package_name %]::[% foreign_table +%]
[% END %]
    );
}

sub foreign_display {
    my $self = shift;

[% foreign_display_body %]
}

sub table_name {
    return '[% table_name %]';
}

1;

=head1 NAME

[% gen_package_name %] - model for [% table_name %] table (generated part)

=head1 DESCRIPTION

This model inherits from [% base_class || base_class_default %].
It was generated by Bigtop, and IS subject to regeneration.

=head1 METHODS

You may use all normal [% base_class || base_class_default %] methods and the
ones listed here:

=over 4

=item get_foreign_display_fields

=item get_foreign_tables

=item foreign_display

=item table_name

=back

=cut
[% END %]
EO_TT_blocks

#-----------------------------------------------------------------
#   Methods in the Bigtop::Model::GantryDBIxClass package
#-----------------------------------------------------------------

sub what_do_you_make {
    return [
        [ 'lib/AppName/Model/*.pm'     =>
            'DBIx::Class style model stubs [safe to change]'                 ],
        [ 'lib/AppName/Model/GEN/*.pm' =>
            'DBIx::Class style model specifications [please, do not change]' ],
        [ 'note' =>
            'This backend requires "For use with DBIx::Class" to checked '
            .   'for the Control Gantry backend.' ],
        [ 'note' =>
            'This backend is incompatible with other Model backends.' ],
    ];
}

sub backend_block_keywords {
    return [
        { keyword => 'no_gen',
          label   => 'No Gen',
          descr   => 'Skip everything for this backend',
          type    => 'boolean' },
        { keyword => 'model_base_class',
          label   => 'Models Inherit From',
          descr   => 'Defaults to Gantry::Utils::DBIxClass',
          type    => 'text' },
    ];
}

sub setup_template {
    my $class         = shift;
    my $template_text = shift || $default_template_text;

    return if ( $template_is_setup );

    Inline->bind(
        TT                  => $template_text,
        POST_CHOMP          => 1,
        TRIM_LEADING_SPACE  => 0,
        TRIM_TRAILING_SPACE => 0,
    );

    $template_is_setup = 1;
}

sub gen_Model {
    my $class       = shift;
    my $build_dir   = shift;
    my $bigtop_tree = shift;

    # make sure the directories are ready for us
    my $app_base_name = $bigtop_tree->get_appname();
    my $model_name    = $app_base_name . '::Model';

    my ( $module_dir, @sub_dirs )
                      = Bigtop::make_module_path( $build_dir, $model_name );

    my $gen_dir       = File::Spec->catdir( $module_dir, 'GEN' );

    mkdir $gen_dir;

    # see if there is an alternate default base module
    my $config_block  = $bigtop_tree->get_config()->{Model};

    # build the individual model packages
    my $child_models = $bigtop_tree->walk_postorder(
        'output_dbix_model',
        {
            module_dir       => $module_dir,
            model_name       => $model_name,
            lookup           => $bigtop_tree->{application}{lookup},
            model_base_class => $config_block->{model_base_class}
                                    || 'Gantry::Utils::DBIxClass',
        },
    );

    my $year = ( localtime )[5] + 1900;

    my $base_model = Bigtop::Backend::Model::GantryDBIxClass::stub_base_model(
        {
            app_name     => $app_base_name,
            table_models => $child_models,
            authors      => $bigtop_tree->get_authors(),

            year             => $year,
            copyright_holder => $bigtop_tree->get_copyright_holder(),
            license_text     => $bigtop_tree->get_license_text(),
        }
    );

    my ( $base_dir ) = Bigtop::make_module_path( $build_dir, $app_base_name );
    my $base_file    = File::Spec->catfile( $base_dir, 'Model.pm' );

    Bigtop::write_file( $base_file, $base_model, 'no overwrite' );
}

#-----------------------------------------------------------------
#   Packages named in the grammar
#-----------------------------------------------------------------

package # sql_block
    sql_block;
use strict; use warnings;

sub output_dbix_model {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;

    # Skip sequences, etc.
    return unless ( $self->{__TYPE__} eq 'tables' );

    my $table_lookup = $data->{lookup}{tables}{ $self->{__NAME__} };

    if ( $table_lookup->{not_for} ) {
        foreach my $skipped_type ( @{ $table_lookup->{not_for}{__ARGS__} } ) {
            return if ( $skipped_type eq 'Model' );
        }
    }

    # get columns sets
    my $lookup       = $table_lookup->{fields};

    my $all        = $self->walk_postorder(
            'output_all_fields_dbix', $lookup
    );
    my $essentials = $self->walk_postorder(
            'output_essential_fields_dbix', $lookup
    );

    # deal with foreign keys
    my $foreign_tables = $self->walk_postorder(
            'output_foreign_tables_dbix',       $lookup
    );

    my @foreign_table_names;
    my @has_a_list;

    foreach my $entry ( @{ $foreign_tables } ) {
        my $entry_hash = { @{ $entry } };
        push @foreign_table_names, $entry_hash->{table};
        push @has_a_list,          $entry_hash;
    }

    # Gone Fishing.
    my $table           = $self->{__NAME__};
    my $module_name     = $data->{model_name} . '::' . $table;
    my $gen_pack_name   = $data->{model_name} . '::GEN::' . $table;
    my $alias           = uc $table;
    my $sequence        = $table_lookup->{sequence};
    my $foreign_display = $table_lookup->{foreign_display};

    my $sequence_name;

    if ( $sequence ) {
        $sequence_name = $sequence->{__ARGS__}[0];
    }

    my $primary_key = _find_primary_key( $table_lookup->{fields} );

    my $foreign_display_columns;
    my $foreign_display_body;

    if ( $foreign_display ) {
        my $foreign_display_cols = $foreign_display->{__ARGS__}[0];

        my @field_names          = ( $foreign_display_cols =~ /%([\w\d_]*)/g );
        $foreign_display_columns = "@field_names";

        $foreign_display_body  = _build_foreign_display_body(
            $foreign_display_cols, @field_names
        );
    }

    my $base_class;
    
    if ( defined $table_lookup->{model_base_class} ) {
        $base_class = $table_lookup->{model_base_class}{__ARGS__}[0];
    }

    # generate output
    my $stub_content =
        Bigtop::Backend::Model::GantryDBIxClass::stub_table_module(
        {
            base_class              => $base_class,
            base_class_default      => $data->{model_base_class},
            base_package_name       => $data->{model_name},
            gen_package_name        => $gen_pack_name,
            package_name            => $module_name,
            package_alias           => $alias,
            table_name              => $table,
        }
    );

    my $gen_content =
        Bigtop::Backend::Model::GantryDBIxClass::gen_table_module(
        {
            base_class_default      => $data->{model_base_class},
            base_package_name       => $data->{model_name},
            package_name            => $module_name,
            gen_package_name        => $gen_pack_name,
            package_alias           => $alias,
            table_name              => $table,
            sequence_name           => $sequence_name,
            primary_key             => $primary_key,
            foreign_display_columns => $foreign_display_columns,
            foreign_display_body    => $foreign_display_body,
            all_columns             => $all,
            essential_columns       => $essentials,
            has_a_list              => \@has_a_list,
            foreign_tables          => \@foreign_table_names,
        }
    );

    # store it
    my $module_file = File::Spec->catfile( $data->{module_dir}, "$table.pm" );
    my $gen_dir     = File::Spec->catdir ( $data->{module_dir}, 'GEN' );
    my $gen_file    = File::Spec->catfile( $gen_dir, "$table.pm" );

    eval {
        no warnings qw( Bigtop );
        Bigtop::write_file( $module_file, $stub_content, 'no overwrite' );
    };
    warn $@ if $@;

    eval {
        Bigtop::write_file( $gen_file, $gen_content );
    };
    warn $@ if $@;

    return [ $table ];
}

package # table_element_block
    table_element_block;
use strict; use warnings;

sub output_all_fields_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    return if ( _not_for_model( $field ) );

    return [ $self->{__NAME__} ];
}

sub output_essential_fields_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    if ( $field->{non_essential} ) {
        my $non_essential_value = $field->{non_essential}{args}[0];

        return if ( $non_essential_value );
    }

    return if ( _not_for_model( $field ) );

    return [ $self->{__NAME__} ];
}

sub output_foreign_tables_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    if ( $field->{refers_to} ) {
        my $foreign_table_name = $field->{refers_to}{args}[0];

        return [
            [ column => $self->{__NAME__}, table => $foreign_table_name ]
        ];
    }
    return;
}

1;

__END__

=head1 NAME

Bigtop::Backend::Model::GantryDBIxClass - Bigtop backend generating DBIx::Class models

=head1 SYNOPSIS

If your bigtop file looks like this:

    config {
        base_dir `/home/user`;
        ...
        Model GantryDBIxClass {}
    }
    app Name {...}

and there are tables in the app block, when you type:

    bigtop your.bigtop Model

or
    bigtop your.bigtop all

this module will make model modules which are subclasses of
Gantry::Utils::DBIxClass (which inherits from DBIx::Class).

All modules will live in the lib subdirectory of the app's build directory.
See Bigtop::Init::Std for an explanation of how base_dir and the
build directory are related.

=head1 DESCRIPTION

This is a Bigtop backend which generates data model modules which are
subclasses of Gantry::Utils::DBIxClass.

=head1 KEYWORDS

This module does not register any keywords.  See Bigtop::Model for
a list of keywords models understand.

The default for the model_base_class keyword is Gantry::Utils::DBIxClass.

=head1 AUTHOR

Phil Crow <philcrow2000@yahoo.com>

=head1 COPYRIGHT and LICENSE

Copyright (C) 2005 by Phil Crow

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.

=cut
