package Toader::Templates;

use warnings;
use strict;
use Toader::isaToaderDir;
use Cwd 'abs_path';
use base 'Error::Helper';
use Text::Template;
use Toader::Templates::Defaults;

=head1 NAME

Toader::Templates - This handles fetching Toader templates.

=head1 VERSION

Version 0.1.0

=cut

our $VERSION = '0.1.0';

=head1 SYNOPSIS

For information on the storage and rendering of entries,
please see 'Documentation/Templates.pod'.

=head1 METHODS

=head2 new

=head3 args hash ref

=head4 dir

This is the directory to intiate in.

    my $foo = Toader::Templates->new();

=cut

sub new{
	my %args;
	if(defined($_[1])){
		%args= %{$_[1]};
	};

	my $self={
			  error=>undef,
			  errorString=>'',
			  perror=>undef,
			  isatd=>Toader::isaToaderDir->new(),
			  dir=>undef,
			  defaults=>Toader::Templates::Defaults->new,
			  };
	bless $self;

	if ( defined( $args{dir} ) ){
		if ( ! $self->{isatd}->isaToaderDir( $args{dir} ) ){
			$self->{perror}=1;
			$self->{error}=1,
			$self->{errorString}='The specified directory is not a Toader directory';
			$self->warn;
			return $self;
		}
		$self->{dir}=$args{dir};
	}

	return $self;
}

=head2 dirGet

This gets L<Toader> directory this entry is associated with.

This will only error if a permanent error is set.

    my $dir=$foo->dirGet;
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub dirGet{
	my $self=$_[0];

	if (!$self->errorblank){
		return undef;
	}

	return $self->{dir};
}

=head2 dirSet

This sets L<Toader> directory this entry is associated with.

One argument is taken and it is the L<Toader> directory to set it to.

    $foo->dirSet($toaderDirectory);
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub dirSet{
	my $self=$_[0];
	my $dir=$_[1];

	if (!$self->errorblank){
		return undef;
	}

	#checks if the directory is Toader directory or not
    my $returned=$self->{isatd}->isaToaderDir($dir);
	if (! $returned ) {
		$self->{error}=1;
		$self->{errorString}='"'.$dir.'" is not a Toader directory';
		$self->warn;
		return undef;
	}

	$self->{dir}=$dir;

	return 1;
}

=head2 fill_in

This fills in a template that has been passed to it.

Two arguments are taken. The first is the template name.
The second is a hash reference. 

The returned string is the filled out template.

    my $rendered=$foo->fill_in( $templateName, \%hash );
    if ( $foo->error ){
        warn( 'Error:'.$foo->error.': '.$foo->errorString );
    }

=cut

sub fill_in{
	my $self=$_[0];
	my $name=$_[1];
	my $hash=$_[2];

	if( ! $self->errorblank ){
		return undef;
	}

	#make sure a template name is specified
	if ( ! defined( $name ) ){
		$self->{error}=9;
		$self->{errorString}='No template name specified';
		$self->warn;
		return undef;
	}

	#gets the template
	my $template=$self->getTemplate( $name );
	if ( $self->error ){
		return undef;
	}

	return $self->fill_in_string( $template, $hash );
}

=head2 fill_in_string

This fills in a template that has been passed to it.

Two arguments are required and the first is the template string to use and
second it is the hash to pass to it.

The returned string is the filled out template.

    my $rendered=$foo->fill_in_string( $templateString, \%hash );
    if ( $foo->error ){
        warn( 'Error:'.$foo->error.': '.$foo->errorString );
    }

=cut

sub fill_in_string{
	my $self=$_[0];
	my $string=$_[1];
	my $hash=$_[2];

	if( ! $self->errorblank ){
		return undef;
	}

	if ( ! defined( $string ) ){
		$self->{error}=8;
		$self->{errorString}='No template string specified';
		$self->warn;
		return undef;
	}

	my $template = Text::Template->new(
		TYPE => 'STRING',
		SOURCE => $string,
		DELIMITERS=>[ '[==', '==]' ],
		);

	my $rendered=$template->fill_in(
		HASH=>$hash,
		);

	if ( ! defined ( $rendered ) ){
		$self->{error}=7;
		$self->{errorString}='Error encountered filling in the template';
		$self->warn;
		return undef;
	}

	return $rendered;
}

=head2 findTemplate

This finds a specified template.

One arguement is taken and it is the name of the template.

A return of undef can mean either a error or it was not found.
If there was an error, the method error will return true.

    my $templateFile=$foo->findTemplate($templateName);
    if( !defined( $templateFile ) ){
        if($foo->error){
            warn('Error:'.$foo->error.': '.$foo->errorString);
        }else{
            print("Not found\n");
        }
    }else{
        print $templateFile."\n";
    }

=cut

sub findTemplate{
	my $self=$_[0];
	my $name=$_[1];

	if (!$self->errorblank){
		return undef;
	}

	#make sure a directory has been set
	if (!defined( $self->{dir} )) {
		$self->{error}=2;
		$self->{errorString}='No directory has been set yet';
		$self->warn;
		return undef;		
	}

	#checks if the name is valid
	my $returned=$self->templateNameCheck($name);
	if (! $returned ) {
		$self->{error}=4;
		$self->{errorString}='"'.$name.'" is not a valid template name';
		$self->warn;
		return undef;
	}

	#checks if the directory is Toader directory or not
    $returned=$self->{isatd}->isaToaderDir( $self->{dir} );
	if (! $returned ) {
		$self->{error}=3;
		$self->{errorString}='"'.$self->{dir}.'" is no longer a Toader directory';
		$self->warn;
		return undef;
	}

	#initial stuff to check
	my $dir=$self->{dir};
	my $template=$dir.'/.toader/templates/'.$name;

	#checks if the template exists
	if (-f $template) {
		return $template;
	}

	#recurse down trying to find the last one
	$dir=abs_path($dir.'/..');
	#we will always find something below so it is just set to 1
	while (1) {
		#we hit the FS root...
		#if he hit this, something is definitely wrong
		if ($dir eq '/') {
			return undef;
		}

		#make sure
		$returned=$self->{isatd}->isaToaderDir($dir);
		if (!$returned) {
			return undef;
		}

		#check if it exists
		$template=$dir.'/.toader/templates/'.$name;
		if (-f $template) {
			return $template;
		}

		#check the next directory
		$dir=abs_path($dir.'/..');
	}
}

=head2 getTemplate

This finds a template and then returns it.

The method findTemplate will be used and if that fails the default
template will be returned.

One arguement is required and it is the template name.

    my $template=$foo->getTemplate($templateName);
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub getTemplate{
	my $self=$_[0];
	my $name=$_[1];

	if (!$self->errorblank){
		return undef;
	}

	#make sure a template name is specified
    if ( ! defined( $name ) ){
        $self->{error}=9;
        $self->{errorString}='No template name specified';
        $self->warn;
        return undef;
    }

	#try to find it as a file
	#also allow this to do the error checking as it is the same
	my $file=$self->findTemplate($name);
	if ($self->error) {
		$self->warnString('findTemplate errored');
		return undef;
	}

	#the contents of the template to be returned
	my $template;

	#if we found a file, read it and return it
	if (defined($file)) {
		my $fh;
		if ( ! open( $fh, '<', $file ) ) {
			$self->{error}=5;
			$self->{errorString}="Unable to open '".$file."'";
			$self->warn;
			return undef;
		}
		$template=join('',<$fh>);
		close $fh;
		return $template;
	}

	#tries to fetch the default template
	$template=$self->{defaults}->getTemplate($name);
	if ( ! defined( $template ) ) {
			$self->{error}=6;
			$self->{errorString}='No default template';
			$self->warn;
			return undef;
	}

	return $template;
}

=head2 getTemplateDefault

This finds a default template and then returns it.

One arguement is required and it is the template name.

    my $template=$foo->getTemplate($templateName);
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub getTemplateDefault{
    my $self=$_[0];
    my $name=$_[1];

    if (!$self->errorblank){
        return undef;
    }

    #make sure a template name is specified
    if ( ! defined( $name ) ){
        $self->{error}=9;
        $self->{errorString}='No template name specified';
        $self->warn;
        return undef;
	}

    #tries to fetch the default template
    my $template=$self->{defaults}->getTemplate($name);
    if ( ! defined( $template ) ) {
		$self->{error}=6;
		$self->{errorString}='No default template';
		$self->warn;
		return undef;
    }
	
    return $template;
}

=head2 listTemplates

This lists the various templates in the directory.

    my @templates=$foo->listTemplates;
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub listTemplates{
	my $self=$_[0];

	if (!$self->errorblank){
		return undef;
	}

	#make sure a directory has been set
	if (!defined( $self->{dir} )) {
		$self->{error}=2;
		$self->{errorString}='No directory has been set yet';
		$self->warn;
		return undef;
	}

	#checks if the directory is Toader directory or not
    my $returned=$self->{isatd}->isaToaderDir( $self->{dir} );
	if (! $returned ) {
		$self->{error}=3;
		$self->{errorString}='"'.$self->{dir}.'" is no longer a Toader directory';
		$self->warn;
		return undef;
	}

	#the directory to list
	my $dir=$self->{dir}.'/.toader/templates/';

	#makes sure the template exists and if it does not, return only having the default
	if (! -d $dir) {
		return;
	}

	#lists each theme
	my $dh;
	if (opendir($dh, $dir )) {
		$self->{error}='4';
		$self->{errorString}='Failed to open the directory "'.$dir.'"';
		$self->warn;
		return undef;
	}
	my @templates=grep( { -f $dir.'/'.$_ } readdir($dh) );
	close($dh);
	
	return \@templates;
}

=head2 listDefaultTemplates

This lists the various templates in the directory.

    my @templates=$foo->listTemplates;
    if($foo->error){
        warn('Error:'.$foo->error.': '.$foo->errorString);
    }

=cut

sub listDefaultTemplates{
    my $self=$_[0];

    if (!$self->errorblank){
        return undef;
    }

	return $self->{defaults}->listTemplates;
}

=head2 templateNameCheck

This makes sure checks to make sure a template name is valid.

    my $returned=$foo->templateNameCheck($name);
    if ($returned){
        print "Valid\n";
    }

=cut

sub templateNameCheck{
	my $self=$_[0];
	my $name=$_[1];

	if (!$self->errorblank){
		return undef;
	}

	if (!defined($name)) {
		return 0;
	}
	if ($name =~ /^ /) {
		return 0;
	}
	if ($name =~ /\t/) {
		return 0;
	}
	if ($name =~ /\n/) {
		return 0;
	}
	if ($name =~ / $/) {
		return 0;
	}

	return 1;
}

=head1 ERROR CODES

=head2 1

The specified directory is not a L<Toader> directory.

=head2 2

No directory has been specified yet.

=head2 3

The directory in question is no longer a toader directory.

=head2 4

Not a valid template name.

=head2 5

Unable to open the template file.

=head2 6

Unable to fetch the default template. It does not exist.

=head2 7

Errored filling out the template string.

=head2 8

Nothing specified for the template string.

=head2 9

The Toader::Gallery object is not usable according
to the usable method.

=head1 AUTHOR

Zane C. Bowers-Hadley, C<< <vvelox at vvelox.net> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-toader at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Toader>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Toader::Templates


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Toader>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Toader>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Toader>

=item * Search CPAN

L<http://search.cpan.org/dist/Toader/>

=back


=head1 ACKNOWLEDGEMENTS


=head1 LICENSE AND COPYRIGHT

Copyright 2011 Zane C. Bowers-Hadley.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.


=cut

1; # End of Toader
