package CMS::Simple ;

use warnings ;
use strict ;

use Carp ;
use Data::Dumper ;

use CMS::Simple::Parse ;
use Template::Simple ;
use File::Slurp ;


our $VERSION = '0.01' ;


my %defaults = (

###
# use File::Path and other modules to make paths clean and portable
###

	working_dir	=> '.',
	content_paths	=> ['content'],
	templates_dir	=> 'templates',
	output_dir	=> 'output',

# this is for timestamps

	published_dir	=> 'published',

	template_args	=> {},
) ;

my %parsers = (

	cont	=> \&CMS::Simple::Parse::parse_content,
	csv	=> \&CMS::Simple::Parse::parse_csv,
	pl	=> \&_parse_perl,
#	yaml	=> \&parse_yaml,
) ;

sub new {

	my( $class, $args ) = @_ ;

	my $self = bless { %defaults, %{$args} }, $class ;

	$self->_invert_filter_tags() ;

	$self->_make_dirs() ;

# rename/clean dirs??

	$self->_load_content() ;

####
# add template path arg

	$self->{tmpl_obj} = Template::Simple->new( %{$self->{template_args}} ) ;

	return $self ;
}


sub _load_content {

	my( $self ) = @_ ;

	my $contents = $self->{contents} ;

#print Dumper $contents ;

	while( my( $name, $file ) = each %{$contents} ) {

######
# ADD BETTER DIRECTORY STUFF
######

		my $file_path = "$self->{content_paths}[0]/$file" ;

		my $content_text = read_file( $file_path ) ;

		my ($suffix) = $file_path =~ /\.(\w+)$/ ;

		my $parser = $parsers{ $suffix } ;

		$parser or die "unknown suffix '$suffix'" ;

		my $parsed = $parser->( $content_text ) ;

		$contents->{$name} = {

			file	=>	$file,
			text	=>	$content_text,
			parsed	=>	$parsed,
		} ;
	}
#print Dumper $contents ;

}

sub build_all_pages {

	my( $self ) = @_ ;

	foreach my $page_name ( keys %{$self->{pages}} ) {

		$self->build_page( $page_name ) ;
	}
}


sub build_page {

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

#print "BUILD $page_name\n" ;

	my $page = $self->{pages}{$page_name} ;

	return if $page->{skip} ;
	
	$page->{name} = $page_name ;

	$self->_get_page_content( $page ) ;

#print Dumper $page ;

	$self->_filter_page_content( $page ) ;


	$self->_render_page( $page ) ;
#print ${$page->{rendered}} ;

	$self->_output_page( $page ) ;
}

sub _get_page_content {

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

#######
# FIX so a page contents can override values and not just whole maps
# ADD contents maps to have multilevel keys
#######

	my $all_contents = $self->{contents} ;

	my $page_contents = $page->{contents} || {} ;

# loop over the default (common) and page specific content maps

	foreach my $contents_map (
		$self->{default_contents_map},
		$page->{contents_map} ) {

#print "MAP ", Dumper $contents_map ;

		while( my( $name, $location ) = each %{$contents_map} ) {

# get the contents for this content name

			my $contents = $all_contents->{$name}{parsed} ;

			$self->_add_page_contents(
				$page_contents,
				$location,
				$contents
			) ;
		}
	}

	print Dumper $page_contents if $page->{dump} ;

	$page->{contents} = $page_contents ;
}

sub _add_page_contents {

	my( $self, $page_contents, $location, $contents ) = @_ ;

#########
# this needs to handle multilevel content location
#########

# if we have a location, just store the contents there

	if ( $location ) {

		my @loc_keys = split /:/, $location ;
#print "LOC @loc_keys\n" ;

		my $loc_ref = \$page_contents->{ shift @loc_keys } ;

#print "LOC $loc_ref\n" ;

# descend into the page contents based on the location keys

		$loc_ref = \${$loc_ref}->{$_} for @loc_keys ;

		${$loc_ref} = deep_copy( $contents ) ;

		return ;
	}

# no location so store all the top level contents in the top level of
# the page

	@{$page_contents}{keys %{$contents}} = values %{$contents} ;
}

sub _filter_page_content {

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

# NOTE content must be a hash at the top

	$self->_filter_content_hash( $page->{contents} ) ;

#print Dumper $page->{contents} ;
}

sub _filter_content_hash {

	my( $self, $href, $path ) = @_ ;

	while( my( $tag, $val ) = each %{$href} ) {

		my @new_val =
			$self->_filter_content_tag( $tag, $val, $path ) ;

		next unless @new_val ;
		$href->{$tag} = $new_val[0] ;
	}
}

sub _filter_content_array {

	my( $self, $tag, $aref, $path ) = @_ ;

#print "ARRAY: ", Dumper \$tag, $aref ;

	my @new_vals ;

	foreach my $val ( @{$aref} ) {

		push @new_vals,
			$self->_filter_content_tag( $tag, $val, $path ) ;
	}

	@{$aref} = @new_vals ;

#print Dumper $aref ;

}

sub _filter_content_tag {

	my( $self, $tag, $val, $path ) = @_ ;

	my $ref_type = ref $val ;

	if ( $ref_type eq 'HASH' ) {

		$self->_filter_content_hash( $val, $path ) ;
		return $val ;
	}

	if ( $ref_type eq 'ARRAY' ) {

		$self->_filter_content_array( $tag, $val, $path ) ;
		return $val ;
	}

	my @new_val = $self->_filter_content_value( $tag, $val, $path ) ;

	return unless @new_val ;

	$val = $new_val[0] ;

	$self->_filter_content_tag( $tag, $val, $path ) if ref $val ;

	return $val ;
}

sub _filter_content_value {

	my( $self, $tag, $val, $path ) = @_ ;

	my $filters = $self->{tag_to_filters}{$tag} ;

	return unless $filters ;

	my @new_val ;

	foreach my $filter ( @{$filters} ) {

#print "FILTER $filter->{name}\n" ;

#print "TAG $tag [$val]\n" unless defined $val;

$val = '' unless defined $val || $tag ne 'text' ;

		@new_val = $filter->{code}->( $tag, $val, $path ) ;

		next unless @new_val ;

		$val = $new_val[0] ;
	}

#print "TAG: $tag: ", Dumper \$val ;

# return if nothing was changed

	return unless @new_val ;

	return $val ;
}

sub _render_page {

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

	my $tmpl_obj = $self->{tmpl_obj} ;

# NOTE: using internal method. will fix template::simple to expose it

	my $tmpl_name = $page->{template} || $self->{default_template} ;

	my $template = $tmpl_obj->_get_template( $tmpl_name ) ;

#print Dumper $page->{contents} ;

	my $rendered = $tmpl_obj->render( $template, $page->{contents} ) ;

	$page->{rendered} = $rendered ;
}



sub _output_page {

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

##########
# use file::path stuff to make this portable
##########

	my $output_path = 
	"$self->{'output_dir'}/$page->{name}$self->{'output_suffix'}" ;

	$page->{'output_path'} = $output_path ;

	write_file( $output_path, $page->{rendered} ) ;
}

sub publish_output {

	my( $self ) = @_ ;

	while ( my($name, $page) = each %{$self->{'pages'}} ) {

		my $output_file         = $self->{'pages'}{$name}{'output_file'} ;

		my $remote_host         = $self->{'remote_host'} ;
		my $remote_user         = $self->{'remote_user'} ;
		my $remote_directory    = $self->{'remote_directory'} ;

		# Strip trailing slash if there is one, then replace it...
		#   so that dir always ends in slash whether or not one is passed:
		# (Note:  not portable outside Linux/unix!)

		$remote_directory =~ s/^(.*)\/$/$1/ ;

		my $scp = Net::SCP->new() ;

		die "Unable to construct remote destination" unless
		  ( $remote_host && $remote_user && $remote_directory ) ;

		# Construct remote destination from class attributes:
		my $destination = "${remote_user}\@${remote_host}:${remote_directory}/" ;

		# Use 'iscp' for interactive scp:
		$scp->iscp( $output_file, $destination ) or die $scp->{errstr};

	}
}

sub _parse_perl {

	my( $text ) = @_ ;

	return eval $text ;
}


# change 
sub deep_copy {

	my( $val ) = @_ ;

	return $val unless ref $val ;

	return [ map deep_copy( $_ ), @{$val} ] if ref $val eq 'ARRAY' ;

	return { map { $_, deep_copy( $val->{$_} ) } keys %{$val} }
		if ref $val eq 'HASH' ;

	die "$val is not a scalar, ARRAY or HASH" ;
}


sub _make_dirs {

	my( $self ) = @_ ;

############
# use File::Path to make deep dirs
###########

	mkdir( $self->{output_dir} ) ;
	mkdir( $self->{published_dir} ) ;
}

sub _invert_filter_tags {

	my( $self) = @_ ;

	my %tag_to_filters ;

	foreach my $filter ( @{$self->{filters}} ) {

		push @{$tag_to_filters{$_}}, $filter for @{$filter->{tags}} ;
	}

#print Dumper \%tag_to_filters ;

	$self->{tag_to_filters} = \%tag_to_filters ;
}

1 ;
