package SmokeOrNot::Controller::Bars;

use strict;
use warnings;
use parent 'Catalyst::Controller::HTML::FormFu';

use XML::Feed;
use DateTime::Format::Pg;
use Date::Parse;

=head1 NAME

SmokeOrNot::Controller::Bars - Catalyst Controller

=head1 DESCRIPTION

Catalyst Controller.

=head1 METHODS

=cut

=head2 index 

=cut

#sub index :Path :Args(0) {
sub index : PathPart('bars') Chained('/language') Args(0) {

    my ( $self, $c ) = @_;

# $c->response->body($c->loc('Matched SmokeOrNot::Controller::Bars in Bars.'));
# redirect to search page

    $c->response->redirect( $c->uri_for('search') );
    $c->detach();

}

=head2 list

Fetch all bar objects and pass to barss/list.tt2 in stash to be displayed

=cut

#sub list :Local {
#sub list : PathPart('bars/list') Chained('/language') Args(0) {
sub list : PathPart('bars/list') Chained('/language') {

    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
    # 'Context' that's used to 'glue together' the various components
    # that make up the application
    my ( $self, $c,  @args ) = @_;
    my ( $page, $rows ) = @args;
    $page = 1 unless ($page && $page =~ /^\d+/);
    $rows = 10 unless ($rows && $rows =~ /^\d+/);

    # Clear the flash
    $c->clear_flash();

    # Retrieve all of the bar records as bar model objects and store in the
    # stash where they can be accessed by the TT template
    # $c->stash->{bars} = [$c->model('DB::Bar')->all()];
    my $rs
        = $c->model('DB::Bar')
        ->search( undef,
        { order_by => 'name', page => $page, rows => $rows } );
    my $bars = [ $rs->all() ];
    foreach ( @{$bars} ) {
        $_->{last_updated_epoch} = str2time($_->last_updated);
    }
    $c->stash->{bars}  = $bars;
    $c->stash->{pager} = $rs->pager();
    $c->stash->{rows}  = $rows;

    # Set the TT template to use.  You will almost always want to do this
    # in your action methods (action methods respond to user input in
    # your controllers).
    $c->stash->{template} = 'bars/list.tt2';
}

=head2 create_edit

Use HTML::FormFu to create or update a bar

=cut

#sub create_edit :Local :FormConfig {
sub create_edit : PathPart('bars/create_edit') Chained('/language')
    Args : FormConfig {
    my ( $self, $c, $id ) = @_;
    my $bar;

    # TURN OFF
    $c->flash->{error_msg} = $c->loc("Adding/editing turned off");
#    $c->response->redirect( $c->uri_for('list') );
    $c->stash->{template} = 'bars/list.tt2';
    $c->detach();

    # button new_city
    if ( $c->req->param('submit_new_city') ) {
        $c->session->{new_bar} = $c->req->body_parameters;
        $c->response->redirect( $c->uri_for('../cities/create') );
        $c->detach();
    }

    # We are in edit mode
    if ($id) {

        # Get the specified bar
        $bar = $c->model('DB::Bar')->find($id);

        # Make sure we were able to get a bar
        unless ($bar) {
            $c->flash->{error_msg} = $c->loc("Invalid bar -- Cannot edit");
            $c->response->redirect( $c->uri_for('list') );
            $c->detach();
        }
    }

    # Get the form that the :FormConfig attribute saved in the stash
    my $form = $c->stash->{form};

    # Pass the bar too (last_updated)
    $bar->{last_updated_epoch} = str2time($bar->last_updated);
    $c->stash->{bar} = $bar;

    # Clear the flash
    $c->clear_flash();

    # Check if the form has been submitted (vs. displaying the initial
    # form) and if the data passed validation.  "submitted_and_valid"
    # is shorthand for "$form->submitted && !$form->has_errors"
    if ( $form->submitted_and_valid ) {

        # We are in create mode
        unless ($bar) {
            $bar = $c->model('DB::Bar')->new_result( {} );
        }

        # Set last_updated
        my $now = localtime;
        $form->add_valid( 'last_updated', $now );

        # Save the form data for the bar
        $form->model->update($bar);

        # Set a status message for the user
        $c->flash->{status_msg}
            = $c->loc('Bar ')
            . $bar->name
            . ( $id ? $c->loc(' edited') : $c->loc(' created') );

        # Return to the bars list
        $c->response->redirect( $c->uri_for('list') );
        $c->detach();
    } else {

        # Get the cities from the DB
        my @citiesObjs = $c->model("DB::City")->all();

        # Create an array of arrayrefs where each arrayref is a city
        my @cities;
        foreach ( sort { $a->name cmp $b->name } @citiesObjs ) {
            my $zip = ($_->zip ? $_->zip . ", " : "");
            push(
                @cities,
                [   $_->id,
                    $_->name . " (" . $zip . $_->country->name . ")"
                ]
            );
        }

        # Get the select added by the config file
        my $select_city = $form->get_all_element( { name => 'city' } );

        # Add the cities to it
        $select_city->options( \@cities );

        # Fill form with values from session
        if ( $c->session->{new_bar} ) {
            my $values = {};
            foreach my $key ( keys %{ $c->session->{new_bar} } ) {
                $values->{$key} = $c->session->{new_bar}->{$key}
                    if $key ne 'submit_new_city';
            }
            delete $c->session->{new_bar};
            my $session_bar = $c->model('DB::Bar')->new_result($values);
            $form->model->default_values($session_bar);
        }

        # We are in edit mode
        if ($bar) {

            # Populate the form with existing values from DB
            $form->model->default_values($bar);
        }
    }

    # Set the template
    $c->stash->{template} = 'bars/create_edit.tt2';
}

=head2 autocomplete

Private autocomplete sub for the searchterm field

=cut

#sub autocomplete : PathPart('bars/autocomplete') Chained('/language') Args(0) {
sub autocomplete : Local {
    my ( $self, $c ) = @_;
    my @suggestions;

    # Grab the search term
    my $req = $c->req->param('searchterm');

    if ($req) {

        # Get data
        my @countryObjs = $c->model("DB::Country")
            ->search( { 'name' => { '-ilike', "$req%" } } );
        foreach (@countryObjs) {
            push @suggestions, $_->name;
        }
        my @barObjs = $c->model("DB::Bar")
            ->search( { 'name' => { '-ilike', "$req%" } } );
        foreach (@barObjs) {
            push @suggestions, $_->name;
        }
        my @cityObjs = $c->model("DB::City")->search(
            [   'name' => { '-ilike', "$req%" },
                'zip'  => { '-ilike', "$req%" }
            ]
        );
        foreach (@cityObjs) {
            push @suggestions, $_->zip if $_->zip;
            push @suggestions, $_->name if $_->name;
        }

    }

    # Send results
    $c->res->output( $c->prototype->auto_complete_result( \@suggestions ) );
}

=head2 search_form

Use HTML::FormFu to search for a bar

=cut

#sub search_form :Local :FormConfig {
sub search_form : PathPart('bars/search_form') Chained('/language') Args(0)
    : FormConfig {

    my ( $self, $c ) = @_;

    # Get the form that the :FormConfig attribute saved in the stash
    my $form = $c->stash->{form};

    # Clear the flash
    $c->clear_flash();

    # Check if the form has been submitted (vs. displaying the initial
    # form) and if the data passed validation.  "submitted_and_valid"
    # is shorthand for "$form->submitted && !$form->has_errors"
    if ( $form->submitted_and_valid ) {

        # now handle params
        # my $params = $c->request->parameters;
        # we want thre processed (a.k.a. filtered) parameters
        my $params = $form->_processed_params;

        # remove 'submit'
        delete $params->{'submit'};

        # remove empty values
        foreach my $key ( keys %{$params} ) {
            delete $params->{$key} if $params->{$key} eq '';
        }

        # Search string for output
        my @search;
        push @search, $c->loc('Name') . ": " . $params->{'name'}
            if $params->{'name'};
        push @search, $c->loc('Zip') . ": " . $params->{'zip'}
            if $params->{'zip'};
        push @search,
            $c->loc('City') . ": "
            . $c->model("DB::City")->find( $params->{'city'} )->name
            if $params->{'city'};
        if ( $params->{'status'} ) {
            my $status = (
                ref $params->{'status'} eq 'ARRAY'
                ? join( '/', @{ $params->{'status'} } )
                : $params->{'status'}
            );
            push @search, $c->loc('Status') . ": " . $status;
        }

        # .name is both in me (=bar) and in city
        if ( $params->{'name'} ) {
            $params->{'me.name'} = $params->{'name'};
            delete $params->{'name'};
        }

        # Get bars
        $c->stash->{bars} = [ $c->model('DB::Bar')
                ->search( $params, { order_by => 'name', join => 'city' } ) ];

        # Set a status/error message for the user
        if ( @{ $c->stash->{bars} } ) {
            $c->flash->{status_msg} = $c->loc('Search results for ') . '"'
                . join( ', ', @search ) . '"';
        } else {
            $c->flash->{error_msg} = $c->loc('No search results for ') . '"'
                . join( ', ', @search ) . '"';
        }

        # Show results
        $c->stash->{template} = 'bars/list.tt2';

        # don't detach
        # $c->detach();
    } else {

        # Get the cities from the DB
        my @citiesObjs = $c->model("DB::City")->all();

        # Create an array of arrayrefs where each arrayref is a city
        my @cities;
        foreach ( sort { $a->name cmp $b->name } @citiesObjs ) {
            push( @cities,
                [ $_->id, $_->name . " (" . $_->country->name . ")" ] );
        }

        # Get the select added by the config file
        my $select_city = $form->get_element( { name => 'city' } );

        # Add the cities to it
        $select_city->options( \@cities );

# Set the template
# don't set it. 'bars/search.tt2' is set anyway, and we want to set a different one
# from site/index
# $c->stash->{template} = 'bars/search_form.tt2';

    }

}

=head2 search

Use HTML::FormFu to search for a bar

=cut

#sub search :Local :FormConfig {
sub search : PathPart('bars/search') Chained('/language') Args(0)
    : FormConfig {

    my ( $self, $c ) = @_;

    # Get the form that the :FormConfig attribute saved in the stash
    my $form = $c->stash->{form};

    # Clear the flash
    $c->clear_flash();

    # Check if the form has been submitted (vs. displaying the initial
    # form) and if the data passed validation.  "submitted_and_valid"
    # is shorthand for "$form->submitted && !$form->has_errors"
    if ( $form->submitted_and_valid ) {

        # now handle params
        # my $params = $c->request->parameters;
        # we want thre processed (a.k.a. filtered) parameters
        my $params = $form->_processed_params;

        # remove 'submit'
        delete $params->{'submit'};

        # remove empty values
        foreach my $key ( keys %{$params} ) {
            delete $params->{$key} if $params->{$key} eq '';
        }

        # Search string for output
        my @search;
        push @search, $c->loc('Search terms') . ": " . $params->{'searchterm'}
            if $params->{'searchterm'};
        if ( $params->{'status'} ) {
            my $status = (
                ref $params->{'status'} eq 'ARRAY'
                ? join( '/', @{ $params->{'status'} } )
                : $params->{'status'}
            );
            push @search, $c->loc('Status') . ": " . $status;
        }

        # Create WHERE clause
        my $where = {};    # man SQL::Abstract, DBIx::Class::ResultSet
        if ( $params->{'status'} ) {
            $where->{'me.status'} = $params->{'status'};
        }
        if ( $params->{'searchterm'} ) {
            my @searchterms = split( /[\s,]/, $params->{'searchterm'} );
            map ( s/$_/%$_%/, @searchterms );
            foreach my $term (@searchterms) {
                push @{ $where->{'-and'} },
                    [
                    'me.name'      => { '-ilike', $term },
                    'city.zip'     => { '-ilike', $term },
                    'city.name'    => { '-ilike', $term },
                    'country.name' => { '-ilike', $term },
                    ]
                    if $term ne '';
            }
        }

        # Get bars
        $c->stash->{bars} = [
            $c->model('DB::Bar')->search(
                $where,
                { order_by => 'name', join => { 'city' => 'country' } }
            )
        ];

        # Set a status/error message for the user
        if ( @{ $c->stash->{bars} } ) {
            $c->flash->{status_msg} = $c->loc('Search results for ') . '"'
                . join( ', ', @search ) . '"';
        } else {
            $c->flash->{error_msg} = $c->loc('No search results for ') . '"'
                . join( ', ', @search ) . '"';
        }

        # Show results
        $c->stash->{template} = 'bars/list.tt2';

        # don't detach
        # $c->detach();
    } else {

# display the form ...
# Set the template
# don't set it. 'bars/search.tt2' is set anyway, and we want to set a different one
# from site/index
# $c->stash->{template} = 'bars/search.tt2';

    }

}

=head2 rss

Create an RSS feed for the bars

=cut

sub rss : Local {
    my ( $self, $c ) = @_;
    $c->forward('list/1/65536');    # get the bars, 1 page, all bars

    my $feed = XML::Feed->new('RSS');
    $feed->title( $c->config->{name} . ' - find non-/smoking bars' );
    $feed->link( $c->req->base );
    $feed->self_link( $c->req->uri );
    $feed->description( $c->config->{name}
            . ' is a database that lists the "smoking status" of bars and allows '
            . 'you to select the kind of bar you prefer.' );
    $feed->author('gregor+smokeornot@comodo.priv.at (gregor herrmann)');
    $feed->language('en');

    # Process the bars
    my @modificationdates;
    foreach my $bar ( @{ $c->stash->{bars} } ) {
        my $feed_entry = XML::Feed::Entry->new('RSS');
        $feed_entry->title( $bar->name );
        $feed_entry->link(
            $c->uri_for(
                '/' . $c->language, 'bars', 'create_edit', $bar->id
            )
        );
        $feed_entry->id( $bar->id );
        my $modificationdate
            = DateTime::Format::Pg->parse_datetime( $bar->last_updated );
        push @modificationdates, $modificationdate;
        $feed_entry->modified($modificationdate);
        $feed_entry->content(
                  'Location: '
                . $bar->city->name . ', '
                . $bar->city->country->name
                . '<br />'
                . 'Status: '
                . $bar->status
                . (
                $bar->url
                ? '<br />Website: <a href="'
                    . $bar->url . '">'
                    . $bar->url . '</a>'
                : ''
                )
                . ( $bar->comment ? '<br />Comment: ' . $bar->comment : '' )
        );
        $feed->add_entry($feed_entry);
    }

    @modificationdates = reverse sort @modificationdates;
    $feed->modified( $modificationdates[0] );

    $c->res->body( $feed->as_xml );
    $c->res->content_type('application/rss+xml');
}

=head1 COPYRIGHT

Copyright 2009-2010, gregor herrmann <gregor.herrmann@comodo.priv.at>

=head1 LICENSE

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

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, see <http://www.gnu.org/licenses/>.

=cut

1;
