package Template::Parser;
use strict;
use Exporter;
use Futils qw( :all );
$Template::Parser::VERSION       = 4.03_6;         # 9/19/02, 8:33 pm
@Template::Parser::ISA           = qw( Futils Exporter );
@Template::Parser::EXPORT        = @Futils::EXPORT;
@Template::Parser::EXPORT_OK     = @Futils::EXPORT_OK;
%Template::Parser::EXPORT_TAGS   = %Futils::EXPORT_TAGS;


=pod

   AUTHOR
      Tommy Butler <tommy @ atrixnet.com>
      phone: (817)-468-7716
      6711 Forest Park Dr
      Arlington, TX
           76001-8403

   COPYRIGHT   Tommy Butler. All rights reserved
   LISCENCE    This software is free, use/distribute under the GNU GPL.
   BUGS TO     Tommy Butler <perlmod @ atrixnet.com>

   HISTORY
      4.03_6
         9/19/02, 8:33 pm
         method 'delete_token' now takes a list, and returns a list of the
         values for each token deleted, instead of simply returning a value of
         1 as it has in previous versions.

=cut


# --------------------------------------------------------
# Constructor
# --------------------------------------------------------
sub new {

   # get this namespace, arguments
   my($this) = {};

   # bless object ref into the class' namespace
   bless($this, shift(@_));

   # set up class attributes
   my($in)           = $this->coerce_array(@_);
   $this->{'name'}   = __PACKAGE__;
   $this->{'config'} = $in->{'config'}||{};

   # verify object attributes
   $this->verify_attributes();

   # initialize the object
   $this->init();

   # return object reference
   return($this);
}


# --------------------------------------------------------
# Template::Parser::verify_attributes()
# --------------------------------------------------------
sub verify_attributes {

   my($this) = shift(@_);

=pod

   this is here in case there comes a
   time that Template::Parser has any
   required arguments for its new()
   object method.

=cut

   return(1);
}


# --------------------------------------------------------
# Template::Parser::init()
# --------------------------------------------------------
sub init {

   my($this) = shift(@_);

   # tag syntax used to deliniate embedded
   # tokens within strings or files passed
   # to Template::Parser::parse()
   my($open_embed_tag) = $this->
                            {'config'}
                               {'open embed tag'}     || q[<?=];

   my($close_embed_tag) = $this->
                             {'config'}
                                {'close embed tag'}   || q[?>];

   my($open_token_tag) = $this->
                            {'config'}
                               {'open token tag'}     || q[%%%];

   my($close_token_tag) = $this->
                             {'config'}
                                {'close token tag'}   || q[%%%];


   # regular expression used to parse embedded
   # tokens within user-defined template files
   $this->{'tokenRE'} = qr/(?sx)
                           \Q$open_token_tag\E
                           (.*?)
                           \Q$close_token_tag\E/;

   # regular expression used to parse embedded
   # tokens within user-defined template files
   $this->{'embedRE'} = qr/(?sx)
                           \Q$open_embed_tag\E
                           (.*?)
                           \Q$close_embed_tag\E/;

   $this->{'embedEV'} = qr/(?<!my\()(?:\${1,2})([^ ]*?)(?:->|\{)/;

   # create a refernced hash table to store
   # tokenized data objects
   $this->{'objects'} = {};

   $this->set_token( 'Parser' => $this );

   return(1);
}



# --------------------------------------------------------
# Template::Parser::parse()
# --------------------------------------------------------
sub parse {

   my($this)   = shift(@_);
   my($string) = shift || undef;

   return('') if (not defined($string));

   {
      local($^W)=0;

      $string =~ s/$this->{'tokenRE'}/$this->_token_eval($1)/ge;

      $string =~ s/$this->{'embedRE'}/$this->_embed_eval($1)/ge;
   }


   return($string);
}


# --------------------------------------------------------
# Template::Parser::parse_file()
# --------------------------------------------------------
sub parse_file {

   my($this)   = shift(@_);

   return('') if (!defined($_[0]));

   return( $this->parse($this->load_file($_[0])) );
}


# --------------------------------------------------------
# Template::Parser::_embed_eval()
# --------------------------------------------------------
sub _embed_eval {

   my($this) = shift(@_); my($cmd) = $_[0];

   $cmd =~ s/$this->{'embedEV'}/$this->_wrap_embed($1)/ge;

   $cmd =~ s/\!Parser/\$this/g; &{ sub { CORE::eval($cmd); } }();
}


# --------------------------------------------------------
# Template::Parser::_embed_eval()
# --------------------------------------------------------
sub _token_eval {

   my($this) = shift(@_);

   my($tmp) = $_[0]; $tmp =~ s/^(.*?)$/$this->_wrap_token($1)/e;

   undef($@); $tmp = CORE::eval( $tmp ); $@ || $tmp;
}


# --------------------------------------------------------
# Template::Parser::_wrap_embed()
# --------------------------------------------------------
sub _wrap_embed { q[$this->{'objects'}].q[{'].$_[1].q['}]; }


# --------------------------------------------------------
# Template::Parser::_wrap_token()
# --------------------------------------------------------
sub _wrap_token { q[$this->{'tokens'}{'].$_[1].q['}]; }


# --------------------------------------------------------
# Template::Parser::set_object()
# --------------------------------------------------------
sub set_object {

   my($this)   = shift(@_);
   my($in)     = $this->coerce_array(@_);
   my($name)   = $in->{'name'};

   return(0)
      unless
         (
            defined($in->{'name'})
               and
            defined($in->{'value'})
         );

   $in->{'value'} ||= '';
   $in->{'name'}  ||= '';

   if (ref($in->{'value'}) eq 'HASH') {

      $this->
         {'objects'}->
            { $in->{'name'} } = $in->{'value'};

      if ($in->{'cc tokens'}) {

         foreach (keys(%{ $in->{'value'} })) {

            $this->
               set_token
                  (
                     'name'   => $in->{'name'}.q[->].$_,
                     'value'  => $in->{'value'}->{$_},
                  );
         }
      }

      return($this->{'objects'}->{ $in->{'name'} });
   }
   elsif (defined($in->{'param'})) {

      $this->
         {'objects'}->
            { $in->{'name'} }->
               { $in->{'param'} } = $in->{'value'};

      if ($in->{'cc tokens'}) {

         $this->
            set_token
               (
                  'name'   => $in->{'name'}.q[->].$in->{'param'},
                  'value'  => $in->{'value'},
               );
      }

      return($this->{'objects'}->{ $in->{'name'} }{ $in->{'param'} });
   }

   return(0);
}


# --------------------------------------------------------
# Template::Parser::make_token()
# --------------------------------------------------------
sub set_token {

   my($this)   = shift(@_);

   my($name)   = $_[0]  || '';
   my($value)  = $_[1]  || '';

   my($in)     = $this->coerce_array(@_);

   return(0) if (scalar(@_) < 2);

   $in->{'value'} ||= '';
   $in->{'name'}  ||= '';

   if ($in->{'name'} and $in->{'value'}) {

      $this->
         {'tokens'}->
            { $in->{'name'} } = $in->{'value'};

      return($this->{'tokens'}{ $in->{'name'} });
   }
   else {

      $this->{'tokens'}{ $name } = $value;

      return($this->{'tokens'}{ $name });
   }

   return(0);
}


# --------------------------------------------------------
# Template::Parser::get_object()
# --------------------------------------------------------
sub get_object {

   my($this)   = shift(@_);
   my($in)     = $this->coerce_array(@_);

   $in->{'value'} ||= '';
   $in->{'name'}  ||= '';

   return(0)
      if
         (
            not defined($_[0])
         );

   return
      (
         $this->{'objects'}{ $in->{'name'} }{ $in->{'param'} }
      )
         if
            (
               defined
                  (
                     $this->{'objects'}{ $in->{'name'} }{ $in->{'param'} }
                  )
            );

   return
      (
         $this->{'objects'}{ $in->{'name'} }
      )
         if
            (
               defined
                  (
                     $this->{'objects'}{ $in->{'name'} }
                  )
            );

   return('');
}


# --------------------------------------------------------
# Template::Parser::get_token()
# --------------------------------------------------------
sub get_token {

   my($this)   = shift(@_);
   my($in)     = $this->coerce_array(@_);

   return(0)
      if
         (
            not defined($_[0])
         );

   return
      (
         $this->{'tokens'}{ $in->{'name'} }
      )
         if
            (
               defined
                  (
                     $this->{'tokens'}{ $in->{'name'} }
                  )
            );

   return('');
}


# --------------------------------------------------------
# Template::Parser::delete_object()
# --------------------------------------------------------
sub delete_object {

   my($this)   = shift(@_);
   my($in)     = $this->coerce_array(@_);

   $in->{'name'}  ||= return(0);
   $in->{'param'} ||= '';

   return(delete($this->{'objects'}{$in->{'name'}}{$in->{'param'}}))
      if $in->{'param'};

   return(delete($this->{'objects'}{ $in->{'name'} }));
}


# --------------------------------------------------------
# Template::Parser::delete_token()
# --------------------------------------------------------
sub delete_token {

   my($this)   = shift(@_);
   my(@delete) = ();

   foreach (@_) { push(@delete, delete($this->{'tokens'}{ $_ })); }

   return(@delete);
}


# --------------------------------------------------------
# Template::Parser::load_object()
# --------------------------------------------------------
sub load_object {

   my($this)      = shift(@_);
   my($dir)       = shift(@_)||return({});
   my($dir_hash)  = {};
   my(@files)     = ();

   # get list of files from Futils::list_dir()
   @files =
      $this->list_dir
         (
            $dir, '--files-only', '--no-fsdots'
         );

   # map the content of each file into
   # a hash key-value element where the
   # key name for each file is the name
   # of the file
   foreach (@files) {

      my($keyname) = $_; $keyname =~ s/^(.*)\..*/$1/;

      $dir_hash->{ $keyname } = $this->load_file( $dir . '/' . $_ );
   }

   # return a reference to the hash containing
   # all the filenames and their contents
   return($dir_hash);
}


# --------------------------------------------------------
# Template::Parser::flush_objects()
# --------------------------------------------------------
sub flush_objects { delete($$_[0]{'objects'}); 1 }


# --------------------------------------------------------
# Template::Parser::flush_tokens()
# --------------------------------------------------------
sub flush_tokens { delete($$_[0]{'tokens'}); 1 }


# --------------------------------------------------------
# Template::Parser::flush_all()
# --------------------------------------------------------
sub flush_all { delete($$_[0]{'objects'}); delete($$_[0]{'tokens'}); 1 }


# --------------------------------------------------------
# Template::Parser::DESTROY()
# --------------------------------------------------------
sub DESTROY { }


# --------------------------------------------------------
# End Template::Parser class, return true on import
# --------------------------------------------------------

1;