#!/usr/bin/perl

#         FILE:  main.pl
#
#        USAGE:  ./main.pl [options]
#
#  DESCRIPTION:  The entry point for perlLog, an application that generates a
#                semi-database of time related data. This could be used to log,
#                over time, ones activities, complete with provision for entry
#                of photos and keywords and description. This program is
#                therefore somewhat geared to those that are keeping some sort
#                of log.
#
#                At the moment the program is a console program using perls
#                input and output routines. This could be augmented using
#                `readline' support at some stage.
#
# REQUIREMENTS:  A newish (>=5.8) version of perl. The following perl modules:
#                Getopt::Long, Pod::Usage, Tie::File. These are normally
#                bundled with perl.
#
#      OPTIONS:  --interactive or -i: Make the program prompt for input as
#                opposed to taking the default action of entering one log
#                entry.
#
#                --file or -f: Enter a data file name after this option.
#
#                --help or -h: Give some help.
#
#         BUGS:  Too many to list! No, but seriously...
#
#        NOTES:  See POD at the end of the file.
#
#       AUTHOR:  Gus Beekman (Copyright under GNU GPL),
#
#      COMPANY:  Beekman And Associates
#
#      VERSION:  0.5
#
#      CREATED:  2010/02/08 11:30:32 AM
#
#     REVISION:  2010/02/09 11:54:23 AM - Added some comments.
#                2010/02/10 12:14:09 PM - Fleshed out add data field function
#                (enterOneRecord). Added variable comments.
#                2010/02/12 10:55:39 AM - More work on enterOneRecord.
#                2010/02/15 08:46:39 AM - More work on enterOneRecord. Also
#                worked on the menu structure and added items to view records.
#                2010/02/17 12:07:36 PM - Changed and added comments in various
#                places. Changed layout of POD so that is formats better when
#                using the pod2... tools. Also reworked enterOneRecord.
#                2010/02/22 10:17:42 AM - Reworked some comments.
#                2010/03/01 08:56:23 AM - Added newlines in various places to
#                allow for better looking menus.
#                2010/03/03 09:00:58 AM - Tidied up the imported subs that are
#                supposed to help with sorting; I don't really need them in
#                their current form. Added perl regex lookup facility.
#
#         TODO:  Priority rated from 1 (high priority) to 10 (low priority).
#
#                (5) Decide whether to use the tie function in main or in the
#                two sub main functions.
#
#                (8) Perhaps integrate some kind of `readline' support. This
#                may make data entry easier in the sense of having some keys to
#                edit the entry with. On the other hand it adds another perl
#                module dependency.
#
#                (2) May be better to use a module like Term::TUI which
#                provides a Unix like syntax for navigating through levels and
#                commands.  This must be decided upon soon or else a complete
#                rewrite will become necessary.
#
#                (2) Add some kind of operating system check to the call to
#                system in the function mainInteractive in the section dealing
#                with shelling out, the system menu.

# Perl Log Program
# Copyright (C) <2011>  <Gus Beekman>

# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

use strict;
use warnings;
use English;

# Use statements related to external modules
use Getopt::Long;
use Pod::Usage;
use Tie::File;

# Global variables. These exist outside all subroutines and are accessible by
# all subroutines.
# The filename to which the data is saved by default.
my $fileName = "data.csv";
# A variable to store user input in.
my $inputLine;
# A hash to store all the command line options. See Getopt::Long's POD for
# more details.
my %hashOfOptions = ();
# An array to store each line of the file in. ie: Each field in the line goes
# into one part of the array. Try to keep this ordered ($lineOfDataFile[0] is
# the date for example) because various parts of the program might cross
# reference this variable.
my @lineOfDataFile;
# An array to use for the Tie::File process. Each line in the data file is one
# entry in this array. Read Tie::File's docs for more information.
my @linesInDataFile;
# The number of lines in the data file. This counts all lines, including blank
# ones. It is normally just used with $numberLinesInDataFile =
# @linesInDataFile.
my $numberLinesInDataFile;
# A general purpose array with global scope.
my @array;

# Run Getopt to process command line arguments. These arguments are detailed
# in the POD at the end of the file in the section OPTIONS.
Getopt::Long::Configure("bundling");
GetOptions(\%hashOfOptions, 'help|?', 'interactive|i', 'file|f=s') or pod2usage(2);

# Begin definition of subroutines used in the program

# Compare dates in the format dd/mm/yyyy. This might be necessary at some
# point if the user enters data in this format. It is more likely that on
# needs to change the data apon entry to the form yyyymmdd. This form allows
# easier sorting. In any case the idea of using regular expressions to massage
# the data is a good one.
sub compareDatesDDMMYYYY
{
  # use Time::Local;
  # @dates = ('10/07/2004', '12/07/2004', '12/07/2004');
  # print "@dates\n";
  # @ordered = sort { &compare } @dates;
  # sub compare {
  # $a =~ /(\d{2})\/(\d{2})\/(\d{4})/;
  # $amonth = $1;
  # $aday = $2;
  # $ayear = $3;
  # 
  # $b =~ /(\d{2})\/(\d{2})\/(\d{4})/;
  # $bmonth = $1;
  # $bday = $2;
  # $byear = $3;
  # 
  # $c = timelocal(0,0,0,$aday,$amonth,$ayear-1900);
  # $d = timelocal(0,0,0,$bday,$bmonth,$byear-1900);
  # 
  # $c <=> $d;
  # }
  # print "@ordered\n";

  # And another way to do the same thing.
  #!/usr/bin/perl
  #
  #print "Content-type: text/html\n\n";
  #
  #@dates = ('10/02/2004', '02/01/2004', '12/02/2004', '01/06/2004', '01/02/2005', '01/12/2004');
  #
  #print "Original List: @dates <br><br>\n";
  #
  #@ordered = sort { &compare } @dates;
  #
  #sub compare {
  #$a =~ /(\d{2})\/(\d{2})\/(\d{4})/;
  #$c = $3 . $1 . $2;
  #
  #$b =~ /(\d{2})\/(\d{2})\/(\d{4})/;
  #$d = $3 . $1 . $2;
  #
  #$c <=> $d;
  #}
  #
  #print "Ordered List: @ordered\n";
}

# Enter a new record into the data file. This does rudimentary error checking
# at the moment. It does not sort the data file.
sub enterOneRecord
{
  # Variables: @record is for storing one line of data. $correctFlag: should
  # be 1 if the data has been entered in an acceptable format and 0 otherwise.
  # my @record;
  my $correctFlag = 1;

  # Allow for the user to terminate input at this stage
  print "If you input a blank date it will be assumed that you wish to terminate input.\n";

  # Get and verify the format for the date.
  print "Please enter the date, remember this should be in the format YYYYMMDD:\n";
  $inputLine = <STDIN>;
  chomp($inputLine);

  if ($inputLine =~ /^$/)
  {
    $correctFlag = 0;
  }
  if ($correctFlag == 1)
  {
    if ($inputLine =~ /\d\d\d\d\d\d\d\d/)
    {
      if ($inputLine !~ /[12]\d\d\d\d\d\d\d/)
      {
        print "Year should begin with 1 or 2.\n";
        $correctFlag = 0;
      }
      if ($inputLine !~ /\d\d\d\d[01]\d\d\d/)
      {
        print "Month should begin with 0 or 1.\n";
        $correctFlag = 0;
      }
      if ($inputLine !~ /\d\d\d\d\d\d[0123]\d/)
      {
        print "Day field should begin with 0 or 1 or 2 or 3.\n";
        $correctFlag = 0;
      }
    }
    else
    {
      print "Entry of date was in incorrect format. It should be YYYYMMDD.\n";
      $correctFlag = 0;
    }
  }

  if ($correctFlag == 1)
  {
    $lineOfDataFile[0] = $inputLine;
  }

  # Enter the keywords for the line in the data file. Keywords indicate
  # some sort of grouping.
  if ($correctFlag == 1)
  {
    print "Please enter the keywords for this entry. Multiple entries should\nbe separated by a space:\n";
    $inputLine = <STDIN>;
    chomp($inputLine);
    $lineOfDataFile[1] = $inputLine;
  }

  # Enter the description for the line in the file.
  if ($correctFlag == 1)
  {
    print "Please enter the description for this entry:\n";
    $inputLine = <STDIN>;
    chomp($inputLine);
    $lineOfDataFile[2] = $inputLine;
  }

  # Enter the relative location of one picture.
  if ($correctFlag == 1)
  {
    print "Please enter the relative location of the picture you wish to use for this\nentry. Note that the path seperator should be a forward slash on all operating\nsystems.:\n";
    $inputLine = <STDIN>;
    chomp($inputLine);
    $lineOfDataFile[3] = $inputLine;
  }

  if ($correctFlag == 1)
  {
    push @linesInDataFile, "$lineOfDataFile[0],$lineOfDataFile[1],$lineOfDataFile[2],$lineOfDataFile[3]";
  }

  # Note here that we do no sorting withing the enter record function.
}

# This is the entry point for interactive use of the program.
sub mainInteractive
{
  # print a nice menu screen...
  print "Perl Log --- Interactive.\n";
  print "-------------------------\n\n";
  print "Using \"$fileName\" as the data file name.\n\n";

  # tie the data file.
  tie @linesInDataFile, 'Tie::File', $fileName, recsep => "\n" or die "Could not open the data file: $!\n";

  # Do an infinite loop with the option to break out nicely by merely setting
  # $continueLoop to something other than 1.
  my $continueLoop = 1;
  while ($continueLoop == 1)
  {
    # The main menu...
    print "Menu.\n-----\n\n(1) Enter a new record.\n(2) View records sub menu.\n(3) System commands sub menu.\n(q) Quit the program.\n\n";

    $inputLine = <STDIN>;
    chomp($inputLine);

    if ($inputLine =~ /^1$/)
    {
      enterOneRecord();
      @array = sort @linesInDataFile;
      @linesInDataFile = @array;
    }
    if ($inputLine =~ /^2$/)
    {
      # Do an infinite loop with the option to break out at any time.
      my $continueRecordsMenuLoop = 1;
      while ($continueRecordsMenuLoop == 1)
      {
        # The view records sub menu...
        print "View Records Sub-Menu\n---------------------\n\n(1) View all records.\n(2) View the records from a specific date.\n(3) Perl regex lookup.\n(u) Up to main menu.\n\n";

        $inputLine = <STDIN>;
        chomp($inputLine);

        # view all records
        if ($inputLine =~ /^1$/)
        {
          print "Data file \"$fileName\" contains:\n";
          foreach (@linesInDataFile)
          {
            print "$_\n";
          }
        }
        # view records from a specific date
        if ($inputLine =~ /^2$/)
        {
          # Infinite loop with option to break out...
          my $continueDateEntryForLookup = 1;
          while ($continueDateEntryForLookup == 1)
          {
            print "Which date do you want data from? A blank entry takes you back to the menu.\n";
            $inputLine = <STDIN>;
            chomp($inputLine);
            if ($inputLine !~ /\d\d\d\d\d\d\d\d/ and $inputLine !~ /[12]\d\d\d\d\d\d\d/ and $inputLine !~ /\d\d\d\d[01]\d\d\d/ and $inputLine !~ /\d\d\d\d\d\d[0123]\d/ and $inputLine !~ /^$/)
            {
              print "This does not appear to be a valid date.\n";
            }
            elsif ($inputLine =~ /^$/)
            {
              $continueDateEntryForLookup = 0;
            }
            else
            {
              foreach (@linesInDataFile)
              {
                if (/^$inputLine,.*$/)
                {
                  print "$_\n";
                }
              }
            }
          }
        }
        if ($inputLine =~ /^3$/)
        {
          # Infinite loop with option to break out...
          my $continueRegexForLookup = 1;
          while ($continueRegexForLookup == 1)
          {
            print "Please enter a perl regular expression to look up. A blank line terminates\nthis lookup.\n";
            $inputLine = <STDIN>;
            chomp($inputLine);
            if ($inputLine =~ /^$/)
            {
              $continueRegexForLookup = 0;
            }
            else
            {
              foreach (@linesInDataFile)
              {
                if (/$inputLine/)
                {
                  print "$_\n";
                }
              }
            }
          }
        }
        # break out of the loop by setting the variable to a value
        # other then 1
        if ($inputLine =~ /^[uU]$/)
        {
          $continueRecordsMenuLoop = 0
        }
      }
    }
    # This is the system call menu loop.
    if ($inputLine =~ /^3$/)
    {
      my $systemMenuLoop = 1;
      while ($systemMenuLoop == 1)
      {
        print "Systems Commands Sub-Menu.\n--------------------------\n\n(1) Shell out.\n(u) Up a level.\n\n";

        $inputLine = <STDIN>;
        chomp($inputLine);

        # Shell out using system call. TODO here... We only accomodate linux
        # and windows 2000 or later.
        if ($inputLine =~ /^1$/)
        {
          if ($OSNAME eq "MSWin32")
          {
            print "You seem to be running a variant of Windows.\nPlease type \"exit\" to return when you are done.\n";
            system("cmd");
          }
          if ($OSNAME eq "linux")
          {
            print "You seem to be running a running a version of linux.\nPlease type \"exit\" or \"logout\" or \"\" to finish.\n";
            system("bash");
          }
        }
        if ($inputLine =~ /^[uU]$/)
        {
          $systemMenuLoop = 0;
        }
      }
    }
    # break out of the loop by setting the variable to a value other then 1
    if ($inputLine =~ /^[qQ]$/)
    {
      $continueLoop = 0;
    }
  }

  # untieing the file results in all data being written out to the file.
  untie @linesInDataFile;
}

# This is the entry point for non interactive use of the program.
sub mainNonInteractive
{
  print "Perl Log --- Non-Interactive.\n";
  print "-----------------------------\n\n";
  print "Using \"$fileName\" as the data file name.\n";
  print "In this mode data is merely appended to the data file. The data\nfile is then sorted by date and saved.\n\n";
}

# Main subroutine. An idea cribbed from the C language. This mainly processed
# command line options and then calls the appropriate routine (either
# interactive or not).  It also sets the data file name in the case where it
# is specified on the command line.
sub main
{
  # Process command line options. The first here is for a help message.
  if (defined($hashOfOptions{"help"}) or defined($hashOfOptions{"?"}))
  {
    pod2usage(1);
  }

  # Then check to see if we need to change the default data file name.
  if (defined($hashOfOptions{"file"}) or defined($hashOfOptions{"f"}))
  {
    my $localFileName;
    if (defined($hashOfOptions{"file"}))
    {
      $localFileName = $hashOfOptions{"file"};
    }
    if (defined($hashOfOptions{"f"}))
    {
      $localFileName = $hashOfOptions{"f"};
    }
    if ( -e $localFileName )
    {
      if ( -T $localFileName )
      {
        $fileName = $localFileName;
      }
      else
      {
        print "File given on the command line is not a text file.\n";
        pod2usage(1);
      }
    }
    else
    {
      print "File given on the command line does not exist.\n";
      print "Do you want to use \"$localFileName\" really? (y/n).\n";
      $inputLine = <STDIN>;
      chomp($inputLine);
      # Possible to do the following because pod2usage tends to exit the
      # program.
      unless ($inputLine =~ /^[yY]$/)
      {
        pod2usage(1);
      }
      $fileName = $localFileName;
    }
  }

  # This checks to see if we should go interactive or not.
  if (defined($hashOfOptions{"interactive"}) or defined($hashOfOptions{"i"}))
  {
    mainInteractive();
  }
  else
  {
    mainNonInteractive();
  }

  print "All finished.\n";
}

# Begin execution by calling the main subroutine. Again, this is an idea
# cribbed from C. It makes for an abstracted flow of program execution where
# everything is nicely split in terms of variable scope and so on.
main();

=head1 NAME

main.pl

=head1 SYNOPSIS

main.pl [options]. A program for logging activities over time. If you run this
program with either a -? or a --help as an argument you will get more
extensive help.

=head1 OPERATING SYSTEM SUPPORT

We support linux and windows 2000 or later. In other words the shell should be
bash for the unix like operating system and cmd for the windows like operating
system.

=head1 AUTHOR AND COPYRIGHT

main.pl. A program for logging activities over time.

Copyright (C) <2010>  <Gus Beekman>

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., 51 Franklin
Street, Fifth Floor, Boston, MA 02110-1301 USA.

=head1 OPTIONS

-? or --help: Show some help.

-i or --interactive: Run the program in interactive mode. This prompts for
input and is menu driven.

-f or --file: Give an alternative data file name. The default for this name
is "data.csv".

=head1 DATA FILE FORMAT

The data file is a comma separated file. There are no quotation marks used
because perl has very good scalar handling. It has the following format:

Field   Description
-----   -----------

1       Date. This should be in the form YYYYMMDD. That is, no dashes or
        slashes anywhere to be seen.

2       Keywords. A list of keywords that allow for lookup based on them.
        In other words, these keywords must contain some idea of what the
        entry is about. Each keyword should be separated from the others
        by a space.

3       Description. The long entry for the particular date.

4       Picture location. One and only one filename which is looked up
        relative to the data file in use. This is a restriction which is
        a bit arbitrary but on the other hand it simplifies the use of
        the CSV file apon which the data is organized. TODO: This restriction
        may be lifted if we include here instead of the file name a
        directory name, which contains only pictures that should all be
        included. Regardless of operating system please use a forward
        slash "/" here. Perl handles these forward slashes as path
        seperators well.

=cut

# vim: ts=2 sw=2 fdm=indent foldenable

