package WebGUI::Account;

use strict;

use Class::InsideOut qw{ :std };
use WebGUI::Exception;
use Carp qw(croak);
use WebGUI::International;
use WebGUI::Pluggable;
use WebGUI::Utility;

=head1 NAME

Package WebGUI::Account::Profile

=head1 DESCRIPTION

This is the class which is used to display a users's profile information

=head1 SYNOPSIS

    use base 'WebGUI::Account';

    currentState("edit");

=head1 METHODS

These subroutines are available from this package:

=cut

#-------------------------------------------------------------------

=head2 session ()

Returns a reference to the current WebGUI::Session object.

=cut

readonly session => my %session;

#-------------------------------------------------------------------

=head2 module ()

Returns the string representation of the name of the last Account module called.

=cut

readonly module  => my %module;

#-------------------------------------------------------------------

=head2 method ()

Returns the string representation of the name of the last method called on the module().

=cut

public   method  => my %method;

#-------------------------------------------------------------------

=head2 uid ( [ userId ] )

Returns the userId of the WebGUI::User who's account is being interacted with.

=head3 userId

Optionally set the userId. Normally this is never needed, but is provided for completeness.

=cut

public   uid     => my %uid;

#-------------------------------------------------------------------

=head2 bare ( [ flag ] )

Returns whether or not the Account system should return a method's content
without the layout and style templates.  This would normally be used for
returning JSON or XML data out of the account system.

=head3 flag

Optionally set bare to be true, or false.

=cut

#-------------------------------------------------------------------

=head2 store ( [ hashRef ] )

Returns a hash reference attached to this account object that contains arbitrary data.

=head2 hashRef

A hash reference of data to store.

=cut

public   store   => my %store;  #This is an all purpose hash to store stuff in: $self->store->{something} = "something"
public   bare    => my %bare;   #This flag indicates that neither the layout nor style template should be applied
                                #to the output of the method.  Think JSON/XML, etc.
                                
#-------------------------------------------------------------------

=head2 appendCommonVars ( var )

Appends common template variables that all most templates will use

=head3 var

The hash reference to append template variables to

=cut

sub appendCommonVars {
    my $self    = shift;
    my $var     = shift;
    my $session = $self->session;
    my $user    = $self->getUser;

    $var->{'profile_user_id'  } = $user->userId;
    $var->{'user_full_name'   } = $user->getWholeName;
    $var->{'user_member_since'} = $user->dateCreated;
    $var->{'view_profile_url' } = $user->getProfileUrl;
    $var->{'root_url'         } = $session->url->page("op=account");
    $var->{'back_url'         } = $session->url->getBackToSiteURL;
}


#-------------------------------------------------------------------

=head2 callMethod ( [ method, args, uid ] ) 

Calls the method passed in

=head3 method

Method to call.  If no method is passed in, the view method is called

=head3 args

array reference of arguments to pass to the method being called.

=head3 uid

uid to set in the object.

=cut

sub callMethod {
    my $self     = shift;
    my $method   = shift || "view";
    my $args     = shift;
    my $uid      = shift;

    my $module   = $self->module;

    #Set the method in the object
    $self->method($method);
    #Set the uid in the object
    $self->uid($uid);

    $method = "www_".$method;

    unless ($self->can($method)) {
        WebGUI::Error::MethodNotFound->throw(
            error  => qq{Could not locate method $method in the $module module of the account system},
            method => $method
        );
        return undef;
    }

    unless ($self->canView) {
        my $session = $self->session;
        $session->output->print($session->privilege->insufficient);
        return undef;
    }
   
    #Try to call the method
    my $output = eval { $self->$method(@{$args}) };

    #Croak on error
    if($@) {
        croak "Unable to run $method on $module: $@";
        return undef;
    }

    #Return the output from the method call
    return $output;
}

#-------------------------------------------------------------------

=head2 displayContent ( content[, withoutStyle] ) 

Wraps the content in the style and layout of the account pluggin

=head3 content

Content to wrap in the pluggin style and layout

=head3 withoutStyle

Return the layout without the style wrapper

=cut

sub displayContent {
    my $self    = shift;
    my $content = shift;
    my $noStyle = shift;
    my $session = $self->session;

    ##Don't do any templating if we're sending back data like JSON or XML.
    return $content if $self->bare;

    ##Don't do any templating if we're sending back data like JSON or XML.
    return $content if $self->bare;

    #Wrap content into the layout
    my $var         = {};
    $var->{content} = $content;

    my $configs     = $session->config->get("account");
    my $method      = $self->method || "view";

    # Get fieldsets for avaiable account methods in the order they exist in the config file and append them to the template
    my @pluggins    = ();
    foreach my $account (@{$configs}) {
        #Instantiate the pluggin
        my $instance     = undef;
        my $identifier   = $account->{identifier};
        #Use the currently instantiated pluggin if we are checking this pluggin
        if($account->{identifier} eq $self->module) {
            $instance = $self;
        }
        else {
            #Eval it as we don't want to fail if there's a problem with another method in the config file
            $instance = eval { WebGUI::Content::Account->createInstance($session,$identifier) };
            if (my $e = WebGUI::Error->caught) {
                $session->log->warn("Couldn't instantiate Account Pluggin ".$account->{className}." ... skipping");
                next;
            }
            elsif(!$instance->isa('WebGUI::Account')) {
                $session->log->warn((ref $instance)." is not a subclass of WebGUI::Account ... skipping");
                next;
            }
            #Set the current uid state in the instance
            $instance->uid($self->uid);
        }
        
        #Skip this module if the user can't view it
        next unless ($instance->canView);
            
        #Push the tab variables onto the template
        my %hash                     = %{$account};
       
        $hash{'is_'.$identifier    } = "true";
        $hash{'is_method_'.$self->method } = "true";
        $hash{'url'                } = $instance->getUrl("module=$identifier",1);
        $hash{'isActive'           } = "true" if($identifier eq $self->module);
        WebGUI::Macro::process($session,\$hash{'title'});
        push(@pluggins,\%hash);       
    }
    $var->{'account_loop'} = \@pluggins;

    #Append common display variables to the layout template
    $self->appendCommonVars($var); 

    #Process the layout template
    my $output      = $self->processTemplate($var,$self->getLayoutTemplateId);
    
    return $output if($noStyle);
    #Wrap the layout in the user style
    $session->http->setCacheControl("none");
    return $session->style->process($output,$self->getStyleTemplateId);    
}


#-------------------------------------------------------------------

=head2 canView ( )

Override this method to create permission levels for your Account Pluggin

=cut

sub canView {
    my $self = shift;
    return 1;
}

#-------------------------------------------------------------------

=head2 editSettingsForm ( )

Override this method to create settings for your Account Pluggin

=cut

sub editSettingsForm {
    my $self = shift;
    return "";
}

#-------------------------------------------------------------------

=head2 editSettingsFormSave ( )

Override this method to create settings for your Account Pluggin

=cut

sub editSettingsFormSave {
    my $self = shift;
    return "";
}

#-------------------------------------------------------------------

=head2 getLayoutTemplateId ( )

Override this method to return the template Id for the account layout.  The default
account layout draws a tabbed interface to the different account plugins, and displays
the content from a particular screen from the account plugin.

=cut

sub getLayoutTemplateId {
    my $self = shift;
    return "N716tpSna0iIQTKxS4gTWA";
}

#-------------------------------------------------------------------

=head2 getStyleTemplateId ( )

Override this method to return the template for the main style.  The style would
be for the page that the account layout template is embedded in.

=cut

sub getStyleTemplateId {
    my $self = shift;
    return $self->session->setting->get("userFunctionStyleId");
}


#-------------------------------------------------------------------

=head2 getUrl ( [pairs,appendUID] )

Builds the url for the current page.

=head3 pairs

name value pairs to append to the page url.  If pairs is not passed in
the current module and do values will be used.

=head3 appendUID

If this flag is set and uid is passed as a URL param, that uid will be
appended to the end of the url.

=cut

sub getUrl {
    my $self      = shift;
    my $pairs     = shift;
    my $appendUID = shift;

    my $session   = $self->session;
    
    my $uid = $self->uid;
    if($pairs) {
        #Append op=account to the url if it doesn't already exist
        unless ($pairs =~ m/op=account/){
            $pairs = "op=account;".$pairs;
        }
    }
    else {
        $pairs = q{op=account;module=}.$self->module.q{;do=}.$self->method;
    }

    $pairs .= ";uid=".$uid if($appendUID && $uid);

    return $session->url->page($pairs);
}

#-------------------------------------------------------------------

=head2 getUser

Gets the user, either specified by the uid URL parameter, or the
session user.

=cut

sub getUser {
    my $self      = shift;
    if ($self->uid) {
        return WebGUI::User->new($self->session, $self->uid);
    }
    else {
        return $self->session->user;
    }
}

#-------------------------------------------------------------------

=head2 new ( session, module [,method ,uid] )

Constructor.

=head3 $session

A WebGUI::Session object.

=head3 module

The module being called

=cut

sub new {
    my $class         = shift;
    my $session       = shift;
    my $module        = shift;

    unless (ref $session eq 'WebGUI::Session') {
        WebGUI::Error::InvalidObject->throw(
            expected =>"WebGUI::Session",
            got      =>(ref $session),
            error    => q{Must provide a session variable}
        );
    }

    my $self              = register $class;
    my $id                = id $self;

    $session { $id      } = $session;
    $module  { $id      } = $module;
    $store   { $id      } = {};
    $method  { $id      } = "view";
    $uid     { $id      } = undef;
    $bare    { $id      } = 0;

    return $self;
}

#-------------------------------------------------------------------

=head2 processTemplate ( vars, templateId, template )

Returns the content generated from this template.  It adds the Asset control
bar to the template variables, as well as all Asset properties and metadata.

=head3 vars

A hash reference containing variables and loops to pass to the template engine.

=head3 templateId

An id referring to a particular template in the templates table.

=head3 template

Instead of passing in a templateId, you may pass in a template object.

=cut

sub processTemplate {
    my $self       = shift;
    my $session    = $self->session;

    my $var        = shift;
    my $templateId = shift;
    my $template   = shift;
    my $className  = ref $self;

    # Sanity checks
    if (ref $var ne "HASH") {
        $session->log->error("First argument to processTemplate() should be a hash reference.");
        my $i18n = WebGUI::International->new($session, 'Account');
        return sprintf($i18n->get('Error: Cannot instantiate template'),$templateId,$className);
    }

    $template = WebGUI::Asset->new($session, $templateId,"WebGUI::Asset::Template") unless (defined $template);

    unless (defined $template) {
        $session->log->error("Can't instantiate template $templateId for class ".$className);
        my $i18n = WebGUI::International->new($session, 'Account');
        return sprintf($i18n->get('Error: Cannot instantiate template'),$templateId,$className);
    }

    return $template->process($var);
}


#-------------------------------------------------------------------

=head2 showError ( vars )

Returns a general error screen with the message passed in.

=head3 vars

Variable hash ref to append errors to

=head3 error

Error message to display

=head3 url

URL to display to the user to go back to a safe place

=head3 templateId

temlateId to use to display error

=cut

sub showError {
    my $self  = shift;

    my $var                 = shift || {};
    $var->{'error_message'} = shift;
    $var->{'back_url'     } = shift;
    
    my $templateId          = shift;
    
    return $self->processTemplate($var,$templateId)
}

#-------------------------------------------------------------------

=head2 store ( )

This method returns an internal hash where you can store things for your Account Plugin.
The store is private to your plugin, to each user's copy of the plugin, and only lasts as
long as the session does.

=cut

#-------------------------------------------------------------------

=head2 store ( )

This method returns an internal hash where you can store things for your Account Plugin.
The store is private to your plugin, to each user's copy of the plugin, and only lasts as
long as the session does.

=cut


1;
