#!/usr/bin/perl -w

#-----------------------------------------------------------
#
# SIMPLESITE
# 
# Copyright (C) 1999, 2000 by Arjan Scherpenisse <acscherp@wins.uva.nl>
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#-----------------------------------------------------------

use strict;

# - globals
my $verbose = 0;
my $locale;
my $page;
my $theme;
my ($version, $help);
my $VERSION = "0.60";

#-- read_key_values ----------------------------------------
# this sub reads a file (first and only parameter) and returns an assiociative
# array with key-value pairs.
# key value pairs can be multi-lined if they are enclosed in <--  --> tags. 
# lines started with # are ignored.
sub read_key_values {

  # declare the local variables
  my($complexline, %dict, $file, $chunk, $ckey);

  # get first parameter
  ($file) = $_[0];

  # initialization
  $complexline = 0;
  $chunk="";
  $ckey = "";

  # open file
  open(FILE, $file) || die "No such file: $file";

 Loop:
  while (<FILE>)  {
    # read a line from the file

    next Loop if (/^[\s]*\#/) ;       # comment encountered. skip it.
    next Loop if (!$complexline && /^[\s]*$/); # empty line. skip it.

    if (/<--/) {		# multiline start
      $complexline = 1;
    } 

    if (!$complexline) {
      # if not, check the integrity of the line format
      if ($_ !~ /.*:[ ]*/) 
	{ $verbose && warn "Wrong line format in $file";
	} else {

	  # this statement puts the key-value pair of a simple formatted item into the dict.
	  # check this routine out .... god i love perl :)
	  (/([\w\/]+)\s*:\s*(.*)/);
	  $dict{$1}=$2;
	}
    } else {
      # handle a complex line
      if ( /(.*)-->/ ) {
	# end of complex line reached.
	# ...add last part to line
	$chunk .= $1;
	# ...clean line of garbage
	$chunk =~ s/[\r\t\n ]+/ /g;
	# remove trailing whitespace
	$chunk =~ s/^[\s]//;
	# reset the flag
	$complexline = 0;
	# ...add it to the dict
	$dict{$ckey} = $chunk;

      } elsif ( /(.+):.*<--[\s]*(.*)/ ) {
	$complexline = 1;
	$chunk = $2 . " ";
	$ckey = $1;
	
      } else {
	# in a multiline, just add it
	$chunk .= $_;
      }
	
     } 
  }

  &i18n_dict(\%dict);

  ($complexline) ? die "Unclosed multiline encountered in $file" : return %dict;
}

#-- i18n_dict(\%dict) --------------------------------------------
sub i18n_dict {
  # this subroutine takes a dict of key value pairs and tries to change
  # the original "foo"=>"bar" key to the "foo[$locale]"=>"blaat" value,
  # to support internationalization. No key/value pairs get deleted,
  # if no replacing for foo is found, the value stays the default.

  my($dict) = $_[0];
  my($origkey, $key, $nextkey);

  if ($locale ne "C") {
    foreach $origkey (keys %$dict) {
      if ( $origkey =~ /(.*)\/$locale/ ) {
	$key = $1;
	foreach $nextkey (keys %$dict) {
	  # $$dict{$nextkey. "[C]"} = $$dict{$nextkey};
	  $$dict{$nextkey} = $$dict{$origkey} if ($key eq $nextkey);
	}
      }
    }
  }
}

#-- replace_keys_in_line ---------------------------------------------
sub replace_keys_in_line {
  # this subroutine takes a dict of key values and a line and tries to replace all occurences of
  # <!var $KEY> in VALUE. It returns a string in which everything is replaced.

  my($line, $dict, $key);
  
  ($dict, $line) = @_;

  foreach $key (keys %$dict) {
    $line =~ s/\<\!var[\s]+$key[\s]*\>/$$dict{$key}/gi;
  }

  return $line;
}

#-- shameless_banner ---------------------------------------------
# inserts a banner with the compile date and stuff
sub shameless_banner {
  my ($sec,$min,$h,$mday, $mon, $year, $wday, $ydat, $isdst) = gmtime();
  
  print "<!-- SimpleSite $VERSION (c) Arjan Scherpenisse <acscherp\@wins.uva.nl>\n";
  print "     Website compiled on $mon-$mday-$year $h:$min:$sec \n";
  print "     on " . $ENV{"HOSTNAME"} . ", by user " . $ENV{"USER"} . " -->\n\n";
}

  

#-- replace_keys --------------------------------------------------
# this subroutine takes as arguments a filename and a dict of key/values.
# It replaces in the file all instances of <!--*KEY*--> in VALUE.
# It returns the edited file as an array of lines.
sub replace_keys {
  my($fname, @lines, $dict, $i);
  $i=0;
  # second parameter is the filename, first parameter is the dict of key/values.
  ($dict, $fname) = @_;
  
  open(FILE, "< " . $fname) || die "No such file: $fname";

  while (<FILE>) {
    $lines[$i] = &replace_keys_in_line(\%$dict, $_);
    $i++;
  }

  return @lines;
}

#-- merge_dicts --------------------------------------------------
# this subroutine takes two dictionaries as arguments and returns the 'merge'
# of these two dicts as one dictionary.  If duplicate keys are encountered, the value from the
# first dict is taken as new value.
sub merge_dicts {
  my ($dict1, $dict2, %newdict, $key);

  ($dict1, $dict2) = @_;

  foreach $key (keys %$dict2) {
    $newdict{$key} = $$dict2{$key};
  }

  foreach $key (keys %$dict1) {
    $newdict{$key} = $$dict1{$key};
  }

  &i18n_dict(\%newdict);

  return %newdict;
}

#-- print_dict(\%dict) ------------------------
# this subroutine prints a dict with all its values to the screen. Usefull for debugging.
sub print_dict {
  my($dict) = $_[0];
  my $key;
  foreach $key(keys %$dict) {
    print "[$key] -> $$dict{$key}\n"
  }
}

#-- override_filename($theme, $page, $filename) ------------------------
# this subroutine checks if for a certain theme $theme and a page $page a file exists
# that is overridden by the theme. If it does, it returns that overridden file. Else it returns
# the normal $filename for that theme, if that file exists. Otherwise, an error is returned.
sub override_filename {
  my ($theme, $page, $filename) = @_;

  if (-f "themes/$theme/$page/$filename") {
    # it exists, return it.
    return &i18n_filename("themes/$theme/$page/$filename");
  } elsif 
    (-f "themes/$theme/$filename") {
      # no override found, return the default, BUT i18n'd 
      return &i18n_filename("themes/$theme/$filename");
    }
  else {
    $verbose && warn "Not found: themes/$theme/$filename";
    return 0;
  }
}

#-- i18n_filename($filename) ----------------------------------------------
# looks at the current locale and tries if a file like $filename.$locale exists.
# if so, returns it. Otherwise it just returns $filename.
sub i18n_filename {
  my ($fn) = @_;
  return ( ($locale ne "C") && (-f "$fn.$locale")) ?
    "$fn.$locale" : $fn;
}

#-- build_page --------------------------------------------------
# this subroutine builds the page as specified in the SimpleSite SPECS. It prints
# everything to the standard output, so saving of the generated file should be handled
# somewhere else, in the Makefile for instance.
# this routine should be run from the _root_ of the SimpleSite directory. The two arguments to
# this routine are the theme and the name of the page without the extension.
sub build_page {
  my (%page_defaults, %temp, @lines, $name, %sections, %section_defaults, @files, 
      $itemfile, $section, %item_defaults, @sorted_by_name);
  my $filename = $page;

  # do some sanity checks
  -d "content/$filename" || die "Content directory <$filename> doesnt exist";
  -d "themes/$theme" || die "Theme <$theme> doesnt exist";

  # set up some plain defaults
  %page_defaults = ("theme_id", $theme, "page_id", $filename, "site_title", "Unnamed SimpleSite site");

  # read in the site defaults and merge them with %page_defaults
  if (-f "content/defaults") {
    %temp = read_key_values("content/defaults");
    %page_defaults = merge_dicts(\%temp, \%page_defaults);
  } else {
    $verbose && warn "No site defaults specified for this site";
  }

  # read in the page defaults and merge them with %page_defaults
  if (-f "content/$filename/defaults") {
    %temp = read_key_values("content/$filename/defaults");
    %page_defaults = merge_dicts(\%temp, \%page_defaults);
  } else {
    $verbose && warn "No page defaults specified for $filename";
  }

  # inserts banner
  &shameless_banner;

  # format the default header
  $name = override_filename($theme, $filename, "standard_header");
  if ($name) {
    @lines = &replace_keys(\%page_defaults, $name);
    print @lines;
  }

  # format the page header
  $name = &i18n_filename("content/$filename/header");
  if (-f $name) {
    @lines = &replace_keys(\%page_defaults, $name);
    print @lines;
  } else {
    $verbose && warn "No page header found";
  }

  # if there are sections
  if (-f "content/$filename/sections") {
    %sections = read_key_values("content/$filename/sections");
    # ... for each section
    foreach $section (sort keys %sections) {
      # ... ... if section dir exists, and it's no translation
      if ((-d "content/$filename/$section") && ($section !~ /.+\/.+/)) {
	
	# ... ... read in section defaults
	
	if (-f "content/$filename/$section/defaults") {
	  %section_defaults = read_key_values("content/$filename/$section/defaults");
	} else {
	  $verbose && warn "No section defaults for [$section]";
	}
	$section_defaults{'section_title'} = $sections{$section};
	$section_defaults{'section_id'} = $section;
	
	# ... ... merge the variabele scopes
	%section_defaults = merge_dicts(\%section_defaults, \%page_defaults);
	
	# ... ... format section header
	$name = override_filename($theme, $filename, "section_header");
	if ($name) {
	  @lines = &replace_keys(\%section_defaults, $name);
	  print @lines;
	}
	# ... ... insert the section header contents 
	$name = &i18n_filename("content/$filename/$section/header");
	if (-f $name) {
	  @lines = &replace_keys(\%section_defaults, $name);
	  print @lines;
	} else {
	  $verbose && warn "No section header found";
	}

	# ... ... get all files in the dir
	opendir(DIR, "content/$filename/$section/");
	@files = readdir(DIR);
	# ... ... if there are  files
	if (@files) {
	  # ... ... ... for each item
	  # ... ... ... sorted alphabeticaly, _reversed_
	  @sorted_by_name = reverse sort @files;
	  foreach $itemfile (@sorted_by_name) {
	    if ($itemfile =~ /\.item$/) {
	      # it's an item file!
	      $itemfile =~ s/[\s]+//g;
	      # ... ... ... ... read in item properties
	      %temp = read_key_values("content/$filename/$section/$itemfile");
	      %item_defaults = merge_dicts(\%temp, \%section_defaults);
	      # ... ... ... ... format the item_template
	      $name = override_filename($theme, $filename, "item_template");
	      if ($name) {
		@lines = &replace_keys(\%item_defaults, $name);
		print @lines;
	      }
	    }
	  }
	}
	# ... ... insert the section footer contents 
	$name = &i18n_filename("content/$filename/$section/footer");
	if (-f $name) {
	  @lines = &replace_keys(\%section_defaults, $name);
	  print @lines;
	} else {
	  $verbose && warn "No section footer found";
	}

	# ... ... format section footer
	$name = override_filename($theme, $filename, "section_footer");
	if ($name) {
	  @lines = &replace_keys(\%section_defaults, $name);
	  print @lines;
	}
      } else {
	# the section dir isnt found, print warning
	$verbose && warn "Section $section ($sections{$section}) not found.";
      }
    }  # end foreach section
  } else {
    # else warn: no sections
    $verbose && warn "No sections";
  }
  # format the page footer
  $name = &i18n_filename("content/$filename/footer");
  if (-f $name) {
    @lines = &replace_keys(\%page_defaults, $name);
    print @lines;
  } else {
    $verbose && warn "No page footer found";
  }

  # format the default footer
  $name = override_filename($theme, $filename, "standard_footer");
  if ($name) {
    @lines = &replace_keys(\%page_defaults, $name);
    print @lines;
  }

  return 1;
}
# end of build_page


# says enough
sub version {
  print "Package: SimpleSite\nby Arjan Scherpenisse <acscherp\@wins.uva.nl>\n";
  print "$0 version 0.60\n";
}

# says enough
sub usage {
  print "usage: $0 \n";
  print "\t--theme <theme>\n";
  print "\t--page <page>\n";
  print "\t--locale <locale>\n";
  print "\t--debug\n";
  print "\t--version\n";
  print "\nOptions can be abreviated. Page and theme are required.\n"
}

# main routine
# handles command line switches to specify how the script must act.
sub main {
  use Getopt::Long;

  &GetOptions("version" => \$version, 
	      "theme=s"=>\$theme, 
	      "page=s"=>\$page, 
	      "help"=>\$help, 
	      "debug"=>\$verbose,
	      "locale=s"=>\$locale);

  if ($version) { 
    &version; 
    exit 0;
  } 
  if ($help) {
    &usage;
    exit 0;
  }
  
  if ($page) {
    if (! $theme) {
      $theme = "default";
      warn "No theme on command line, trying default theme.";
    }
    if (! $locale) {
      $locale = "C";
      $verbose && print STDERR "Default locale selected\n";
    }

    &build_page;
  } else {
    &usage;
  }
}



&main;
