#!/usr/bin/perl
############################################################################
#
#   Copyright 1999-2000 Phone.com, Inc.  All rights reserved.
#
#   Subject to the terms and conditions of the SDK License Agreement, 
#   Phone.com, Inc. hereby grants you the right to use the UP.SDK 
#   software and its related documentation.
#
#   The Phone.com name and logo and the family of terms carrying the "UP."
#   prefix are trademarks of Phone.com, Inc. All other brands and product 
#   names may be trademarks of the respective companies with which they 
#   are associated.
#
############################################################################

######################################################################
#
# Employee Picker Context
#
# This application (usually called as an context from another app) allows 
# users to search for a particular employee. Information about the selected
# employee is returned to the calling context.
# 
# Key concepts illustrated include
#    - Returning from context with variables
#    - Simple list management and paging
#
# Application Flow:
#
# A user's navigation through the application is represented as state
# machine transitions.  Each URL encodes the state to transition to in
# the "NEXT" (Next State) argument.  The main routine of the application
# examines the NEXT argument and either returns the appropriate static
# WML deck, or invokes the appropriate routine to compute the generated
# WML deck.
#
# The NEXT state could be one of the following:
#     HOME or blank - To provide the form to specify search criteria
#     LIST    - Display a list of employees that match the search criteria
#     EMPINFO - Retrieve information about the selected employee  
#               and return from context
#
######################################################################

######################################################################
#
# SDK SAMPLE APPLICATION CONFIGURATION
#
# To configure this application to run on your system:
#
# 1. On Unix, change the first line of the file to use the 
#    path for the Perl interpreter on your system
#
# 2. On NT, verify that the .cgi file extension is mapped to
#    the Perl interpreter on your system or rename the file
#    extension to the default extension that is mapped to the
#    Perl interpreter on your system (e.g. .pl)
#
######################################################################

######################################################################
# AppUtils Loading and Configuration
######################################################################
# Include the apputils directory which contains all the SDK utilities
BEGIN { push (@INC, "../apputils");	} # Path to SDK apputils

# Load required application utilities
require 'FileUtils.pl';

require Digest;


######################################################################
# Constants
######################################################################
# Employees record data file
$EMPDATA_FILE = './employees.data';

# The WML Decks

# This deck presents the user with an entry form to specify
# the search criteria for listing employees. It uses two separate entry
# context to accept the user's search pattern for the first and/or last
# names. The choice list shows the current values of all the form entries.

$EMPSEARCH  =
'<wml>
   <card title="Employee">

      <p>
         <do type="options" label="Find">
            <go method="post" href="?NEXT=LIST">
	           <postfield name="NEXT" value="LIST"/>
               <postfield name="L" value="$(last:noesc)"/>
               <postfield name="F" value="$(first:noesc)"/>
            </go>
         </do>
            Employee:
      </p>

      <p mode="nowrap">
         <select>

            <option>
               <onevent type="onpick">
                  <spawn href="#EditString">
                     <setvar name="string" value="$(last)"/>
                     <setvar name="title" value="Last Name"/>
                     <receive name="last"/>
                     <catch/>
                  </spawn>
               </onevent>
                  Last:[$(last:noesc)]
            </option>

            <option>
               <onevent type="onpick">
                  <spawn href="#EditString">
                     <setvar name="string" value="$(first)"/>
                     <setvar name="title" value="First Name"/>
                     <receive name="first"/>
                     <catch/>
                  </spawn>
               </onevent>
                  First:[$(first:noesc)]
            </option>

         </select>
      </p>
   </card>

   <!-- context provides prompted input & editing of a single text value -->

   <card id="EditString">
      <p mode="wrap">
         <do type="accept">
            <exit>
              <send value="$(string)"/>
            </exit>
         </do>
            $(title): <input type="text" value="$(string)" name="string"/>
      </p>
   </card>

</wml>';	

# EMPLOYEELIST Deck
# Used to display employee list.
$EMPLOYEELIST = 
'<wml>
   <card>
      <do type="accept">
         <go method="get" href="?">
	    <postfield name="NEXT" value="EMPINFO"/> 
            <postfield name="NAME" value="$name"/> 
	 </go>
      </do>
      <p>
         Employees: [%s..%s]
         <select name="name">
            %s	
         </select>
      </p>
   </card>
</wml>';

# Option elements
# Templates strings for employee list control in function listEmployees 
$LIST_ENTRY = '<option value="%s">%s</option>'. "\n" ;
$MORE_ENTRY = '<option onpick="?NEXT=LIST&amp;L=%s&amp;F=%s&amp;POS=%s">More...</option>'. "\n";

# DISPINFO Deck
# Generic display card to indicate errors to the user
$DISPINFO = 
'<wml>
   <card>
      <p>
         %s
      </p>
   </card>
</wml>';


# EMPINFO Deck
# This deck returns with selected employee information to calling context.
# The valudes are returned in the following order:  
#             lastname, firstname, phone, email and fax
$EMPINFO = 
'<wml>
   <card>
      <onevent type="onenterforward">
         <exit>
            <send value="%s"/>
            <send value="%s"/>
            <send value="%s"/>
            <send value="%s"/>
            <send value="%s"/>
	 </exit>
      </onevent>
   </card>
</wml>';


######################################################################
# State definitions
#
# main uses these definitions to determine the next state
######################################################################
# Static states {state, deck}
%StaticStateArray = ( "HOME", $EMPSEARCH
                    );

# Dynamic states {state, routine}
%DynamicStateArray = ( "LIST", "listEmployees",
                       "EMPINFO", "employeeInfo"
                     );

#Call the main function to begin execution
&main;


######################################################################
#
# main Function
#
# When a URL request is received, the application checks the value of 
# NEXT to determine the next action. The next action may be to display 
# a static WML deck or to execute a function to generate dynamic WML.
# This function implements this logic.
#
# This function also reads the CGI variables into %cgiVars for the
# rest of the application to use.
#
###################################################################### 
sub main
{
    # Parse the HTTP CGI variables into the associative array %cgiVars
    %cgiVars = &AppUtils::ParseCGIVars();

    # Get the next state, in undefined, use HOME as the state
    $nextState = $cgiVars{"NEXT"};
    if( !defined($nextState) )	{
        $nextState = "HOME";
    }
 
    # Transition to the next state
    $deck = $StaticStateArray{$nextState};
    if( $deck ne "" ) {
        # Found a static deck to return for the NEXT state, just return it
	&AppUtils::OutputDeck($deck);
    }
    else {
        # Retrieve dynamic function state 
	$subRoutine = $DynamicStateArray{$nextState};

	# Check for valid function
        if ($subRoutine ne undef) {

	    # Function found to generate NEXT state dynamic WML
            &$subRoutine();
        }
        else {
            # Invalid NEXT state, return Error deck
	    &AppUtils::ErrorExit("Invalid Dynamic State", "");
        }
    }
}

######################################################################
#
# listEmployees
#
# Search through the employee data file to find matching employee 
# name(s) to display.
# 
# Creates a list of upto 9 employees starting from
# the current position indicated in the CGI variable POS
#
# POS indicates the position of the item in the list.
# Since each page displays 9 entries, the first page is accessed 
# with POS=0 (or no POS variable), the second with POS=9, the third 
# with POS=8 and so on. When this function determines that there are
# more than 9 matching entries starting from POS, it creates a "More" 
# choice entry at the end of the page with a new POS value of POS+9.
# So when the user clicks "More", a request is made from the browser 
# to this function with the same search criteria (L and F) and with the 
# new POS value to retrieve the next page.
#
######################################################################
sub listEmployees
{
    my($last) = $cgiVars{"L"};
    my($first) = $cgiVars{"F"};
    my($pos)  = $cgiVars{"POS"};

    my $PAGE_SIZE = 9;

    my($ln, $fn, $employees, $count, $more);

    $count = 0; 
    $more = 0;
	
    # Even though this application only reads the employee data file,
    # locking is still used to allow for the case where some
    # external source might update the file

    # Open data file, then position file pointer at begining of file.
    &AppUtils::OpenWithLock(*PHONE_DATA, "<$EMPDATA_FILE", 'READ');
    seek(PHONE_DATA, 0, 0);

    # Build a series of WML <option> elemenets for entries that 
    # match the first characters of the name entered.
    foreach(<PHONE_DATA>) {
        ($ln, $fn) = split(/\|/);

	# If the search pattern does not match, don't include that employee
	if (!($ln =~ m/^$last/i && $fn =~ m/^$first/i)) {
	    next;
	}

	# Skip till u reach the right page
	if ($count < $pos) { 
	   $count++; 
	   next;
	}

        # Check for full page, if so stop and exit loop
	if ($count == $pos + $PAGE_SIZE) { 
	    $more = 1; 
	    last;
	}

	# Add employee to deck option list
	$employees .= sprintf($LIST_ENTRY,
                            &AppUtils::HTTPEscapeString("$ln,$fn"),
                            &AppUtils::DeckEscapeString("$ln,$fn"));
	$count++;
    }

    # If there are more employee entries, 
    # add 'More' entry to fetch the next page
    if ($more == 1) {
        $employees .= sprintf($MORE_ENTRY,$last,$first,$count);
    }

    # Close employee data file 
    &AppUtils::CloseWithLock(*PHONE_DATA);

    # If employees were found, generate deck
    if ($employees ne '') {
	&AppUtils::OutputDeck(sprintf($EMPLOYEELIST, $pos+1, $count, $employees));
    }
    else {
    	# No matches found, return with status
        &AppUtils::OutputDeck(sprintf($DISPINFO, "No match found"));
    }
}


######################################################################
#
# employeeInfo Subroutine
#
# Retrieve employee info and return to the calling context
#
######################################################################
sub employeeInfo
{
    my($last, $first);

    ($last, $first) = split(/\,/, $cgiVars{"NAME"});

    my($ln, $fn, $ph, $fax, $email);

    # Open data file, then position file pointer at begining of file.
    &AppUtils::OpenWithLock(*PHONE_DATA, "<$EMPDATA_FILE", 'READ');
    seek(PHONE_DATA, 0, 0);
 
    # look through the datafile to find the employee using $first and $last
    foreach(<PHONE_DATA>) {
	# Remove trailing newline
	chomp;
	($ln,$fn,$ph,$fax,$email) = split(/\|/);
	# Check for matching employee Last and First name
	if ($ln eq $last && $fn eq $first) {

	    # Build & output return deck w/employee info 
	    $deck = sprintf($EMPINFO, 
	                    &AppUtils::DeckEscapeString($ln), 
                            &AppUtils::DeckEscapeString($fn),
                            &AppUtils::DeckEscapeString($ph), 
                            &AppUtils::DeckEscapeString($email),
                            &AppUtils::DeckEscapeString($fax));

                            &AppUtils::OutputDeck($deck);
            return;
	}
    }
    # No employees found
    &AppUtils::OutputDeck(sprintf($DISPINFO, "Employee info not found"));
}

