#!/bin/bash
#
# syllabus -- Generates tex and html course syllabus tables from
#   formatted text files. 
#
# Usage: syllabus <format> <syllabus file> <output file>
#
#   The script expects to find the following files (with mandatory
#   format as given) in the current working directory:
#
#   holidays.dat: 
#     %% Optional comment line(s) may appear anywhere in file
#     %% Comments may NOT start in the middle of a line; no blank lines
#     Year 2002
#     Jan 14   Add/Drop
#     Mar 4    Spring Break
#     %% etc., listing all holidays for semester in this format
#     %% File must end with a newline
#
#   <syllabus file>, e.g. syllabus.dat:
#     %% Math 244, Spring 2002, Professor Hwang
#     %% First line is set of meeting days; must start with "M" or "T"
#     M W F
#     1.1 Systems of Linear Equations
#     1.2 Row Reduction and Echelon Forms
#     %% etc., SERIALLY listing section and lecture topics for each day
#     %% The following "keyworded" lines are also recognized:
#     Group <Topic>
#     Lab
#     Review
#     Midterm 1
#     Study Period
#     Final Exams
#     %% The syllabus.dat file should contain exactly enough entries to
#     %% fill the lecture schedule up to the Friday of the last lecture
#     %% (including "Study Period" and "Final Exams" lines)
#
# Available Output Formats:
#   tex -- LaTeX tabular environment, one lecture per line
#   textable, table -- LaTeX tabular environment, one week per line
#   html, htm -- HTML table, one lecture per line
#
# Please send bug reports, improvements, questions to the author.
# Andrew D. Hwang   <ahwang@mathcs.holycross.ed>  
# Version:
#   1.0 -- January, 2002
#   1.1 -- August 09, 2003: Integer comparison bug in seq(), misc tweaks
#   1.2 -- August 21, 2003: Lab and Group Work entries

PROG="$0"
FORMAT="$1"
INPUT_FILE="$2"
OUTPUT_FILE="$3"

export HOLIDAY_FILE="holidays.dat"
export HOLIDAY_DATA=".holidays.dat"
export SCHEDULE_DATA="schedule.dat"

declare -i YEAR
declare -i DAY_ONE # first day of term, e.g. 14 for Jan 14
declare -i TODAY

declare -a Class_Days

#### seq ####

seq() {
    declare -i i
    let i="$1"
    while [ $i -le $2 ] ; do echo -n "$i "; let i=i+1; done
}

#### Month Conversion routines ####

declare -a month_name[13]
month_name=(Err Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)

# Jan -> 1, Feb -> 2, etc.
month() {
    let i=0
    while [ "$1" != "${month_name[i]}" ]
	do
	    let i=i+1
        done
    echo $i;
}

declare -ai daytable[26]
daytable=(0 0 31 31 28 29 31 31 30 30 31 31 30 30 31 31 31 31 30 30 31 31 30 30 31 31) # Days per month, ordinary and leap years

# Given day of year, prints month and date
month_and_day() {
    let count=1
    DATE=$1
    while [ ${daytable[$((2*$count + $IS_LEAP))]} -lt $DATE ]
    do
	let DATE-=${daytable[$((2*$count + $IS_LEAP))]}
	let count=count+1
    done

    echo -n "${month_name[$count]} $DATE "
}


is_leap() {
    if [ $(($1%4)) == 0 ] # Not for use in year 1900, 2100, etc... :)
	then echo 1;
    else echo 0;
    fi
}

day_of_year() {
    MONTH_NAME=$1
    MONTH_NUMBER=$( month $MONTH_NAME )
    DAY_NUMBER=$2

    for i in $(seq 0 $(($MONTH_NUMBER-1)) )
    do
	let DAY_NUMBER+=${daytable[$((2*$i + $IS_LEAP))]}
    done
    echo $DAY_NUMBER;
}

day_of_week() {
case "$2" in
    name)
	if [ "$1" == "0" ]
	    then echo "Monday"; return

	elif [ "$1" == "1" ]
	    then echo "Tuseday"; return

	elif [ "$1" == "2" ]
	    then echo "Wednesday"; return

	elif [ "$1" == "3" ]
	    then echo "Thursday"; return

	elif [ "$1" == "4" ]
	    then echo "Friday"; return

	else
	    echo "Weekend"; return
	fi
	;;

    abbr)
	if [ "$1" == "0" ]
	    then echo "M"; return

	elif [ "$1" == "1" ]
	    then echo "T"; return

	elif [ "$1" == "2" ]
	    then echo "W"; return

	elif [ "$1" == "3" ]
	    then echo "R"; return

	elif [ "$1" == "4" ]
	    then echo "F"; return

	else
	    echo "?"; return
	fi
	;;

    *) # Should never happen (script calls day_of_week() with proper option)

	echo "Warning: Failed to convert \"$1\" to day of the week"; return
	;;
	
esac
}

#### Write $HOLIDAY_DATA ####
holiday_data() {

if [ ! -f $HOLIDAY_DATA ] || [ $HOLIDAY_DATA -ot $HOLIDAY_FILE ]
then
    mv -f $HOLIDAY_DATA $HOLIDAY_DATA~ &> /dev/null
    echo "%% Created on $(date)" >> $HOLIDAY_DATA
    echo "%% Automatically generated file; do not edit." >> $HOLIDAY_DATA

    cat $HOLIDAY_FILE | 
    while read MONTH DAY REASON
    do
	case "$MONTH" in
	
	%|%%)
	    # Discard comments
	    ;;

	YEAR|Year|year)
	    echo -e "YEAR\t$DAY" >> $HOLIDAY_DATA
	    export IS_LEAP=$(is_leap $DAY)
	    continue
	    ;;

	*)
	    echo -en "$(day_of_year $MONTH $DAY $YEAR)\t" >> $HOLIDAY_DATA
	    echo " $REASON" >> $HOLIDAY_DATA
	    ;;
	esac
    done
fi
}

is_holiday() {
    grep -qw "^$1" "$HOLIDAY_DATA"
    echo $((($?+1)%2)) # "Not"
}

# If reg expr can be used in sed match (e.g., GNU sed), instead do
# echo "$(grep -qw "^$TODAY" $HOLIDAY_DATA | sed 's/$TODAY//g')"
get_holiday_reason() {
    cat $HOLIDAY_DATA | 
    while read DAY_NUMBER REASON
    do
	if [ "$DAY_NUMBER" == "YEAR" ] || [ "$DAY_NUMBER" == "%%" ]
	    then
	    continue
	elif [ "$DAY_NUMBER" == "$1" ]
	    then echo "$REASON" >> $SCHEDULE_DATA
	    return
	fi
    done;
}

increment() { # Get indices of next day
    WKDAY=$(( ($WKDAY+1)%7 ))
    TODAY=TODAY+1
}

#### Initialization ####
get_year() {
    cat $HOLIDAY_DATA | 
    while read DATA1 DATA2
    do
	case "$DATA1" in
	
	%%)
	    # Discard comments
	    ;;

	YEAR)
	    echo "$DATA2"; break
	    ;;

	# If we haven't returned yet, the YEAR line is missing
	*)
	    echo "No year found in $HOLIDAY_DATA"
	    break
	    ;;
	esac
    done
}

get_day_one() {
    cat $HOLIDAY_DATA | 
    while read DATA1 DATA2
    do
	case "$DATA1" in
	
	%%|YEAR)
	    # Discard comments, YEAR line
	    ;;

	*) 
	    echo "$DATA1"; return
	    ;;
	esac
    done
}

get_class_days() {

    cat $INPUT_FILE |
    while read DATA1 DATA2
    do
	case "$DATA1" in
	
	%|%%)
	    # Discard comments
	    ;;

	# All courses meet either Monday or Tuesday
	M|T)
	    set $(echo "$DATA1 $DATA2")
	    while [ "$1" != "" ]
	    do 
		case "$1" in

		M)
		    Class_Days[0]=1
		    shift
		    ;;	    

		T)
		    Class_Days[1]=1
		    shift
		    ;;	    

		W)
		    Class_Days[2]=1
		    shift
		    ;;	    

		R)
		    Class_Days[3]=1
		    shift
		    ;;	    

		F)
		    Class_Days[4]=1
		    shift
		    ;;	    

		*)
		    return
		    ;;
		esac

	    done
	    # Set unset entries to 0; no classes on weekends
	    for i in $(seq 0 6)
		do echo -n "${Class_Days[$i]:-0} "; done
	    return
	    ;;

	*) 
	    return
	    ;;
	esac
    done
}

#### Write $SCHEDULE_DATA ####
schedule_data() {
# Start at first day of term
TODAY=$DAY_ONE
WKDAY=0 # Monday

if  [ ! -f $SCHEDULE_DATA ] || [ $SCHEDULE_DATA -ot $INPUT_FILE ]
then
    mv $SCHEDULE_DATA $SCHEDULE_DATA~ &> /dev/null
    echo "%% Created on $(date)" >> $SCHEDULE_DATA
    echo "%% Automatically generated file; edit with care." >> $SCHEDULE_DATA

    # Look for printable lines
    cat $INPUT_FILE | while read SECTION TOPIC 
    do
	case "$SECTION" in
	
	%|%%)
	    # Discard comments
	    ;;

	M|T|W|R|F)
	    # Discard "class meeting days" line
	    ;;

	*)  # Line of form <section> <topic>
	    export PRINTED=0

	    while [ $PRINTED = 0 ]
	    do
		while [ ${Class_Days[$WKDAY]} = 0 ] # Find next class day
		do
		    increment
		done
	    	    
		echo -n "$(day_of_week $WKDAY abbr) " >> $SCHEDULE_DATA
		echo -en "$(month_and_day $TODAY)\t" >> $SCHEDULE_DATA

		if [ $(is_holiday $TODAY) = 1 ] 
		then
#		    echo "$(month_and_day $TODAY) is a holiday" # debug
		    echo -en "Hol\t" >> $SCHEDULE_DATA 
		    get_holiday_reason $TODAY

		# Not a holiday
		else
		    PRINTED=1

		    if [ "$SECTION" == "Midterm" ]
			then echo -e "Mid\t$SECTION $TOPIC" >> $SCHEDULE_DATA

		    elif [ "$SECTION" == "Lab" ]
			then echo -e "Lab\t$SECTION $TOPIC" >> $SCHEDULE_DATA

		    elif [ "$SECTION" == "Group" ]
			then echo -e "Grp\t$SECTION $TOPIC" >> $SCHEDULE_DATA

		    elif [ "$SECTION" == "Review" ]
			then echo -e "Rev\tReview" >> $SCHEDULE_DATA

		    elif [ "$SECTION" == "Study" ]
			then echo -e "Std\tStudy Period" >> $SCHEDULE_DATA

		    elif [ "$SECTION" == "Exam" ] || [ "$SECTION" == "Final" ]
			then echo -e "Exm\tExams Begin" >> $SCHEDULE_DATA

		    else 
			echo -e "$SECTION\t$TOPIC" >> $SCHEDULE_DATA
		    fi
		fi
	    increment
	    done #PRINTED
	    ;;
	esac
    done
fi
}

print_table() {
case "$FORMAT" in

############
tex)
    mv -f $OUTPUT_FILE $OUTPUT_FILE~ 2>/dev/null
    echo "%% Created on $(date)" >> $OUTPUT_FILE
    echo "%% Automatically generated file." >> $OUTPUT_FILE
    echo -en "\\\\begin{tabular}{|c|l||l|l|}\n\\hline\n" >> $OUTPUT_FILE

    cat $SCHEDULE_DATA | while read DAY MONTH DATE SECTION TOPIC 
    do
	case "$DAY" in
	
	%%)
	    # Discard comments
	    ;;

	*)
	    echo -n "$DAY & $MONTH~$DATE & " >> $OUTPUT_FILE
	    case "$SECTION" in
	    Hol|Holiday)
		echo -en "\t& \\qquad $TOPIC" >> $OUTPUT_FILE
		;;

	    Rev|Review)
		echo -en "\t& \\qquad Review" >> $OUTPUT_FILE
		;;

	    Std|Study)
		echo -en "\t& \\qquad Study Period" >> $OUTPUT_FILE
		;;

	    Exm|Final)
		echo -en "Final Exams\t&\t\t" >> $OUTPUT_FILE
		;;

	    Mid|Midterm)
		echo -en "\t&\qquad\\\\textbf{$TOPIC}" >> $OUTPUT_FILE
		;;

	    Lab)
		echo -en "\t&$TOPIC\t\t" >> $OUTPUT_FILE
		;;

	    Grp|Group)
		echo -en "&\t$TOPIC\t\t" >> $OUTPUT_FILE
		;;

	    *)
		echo -en "Section~$SECTION & $TOPIC " >> $OUTPUT_FILE
		;;
	    esac # $SECTION
	    echo -en " \\\\\\ \n\\hline\n" >> $OUTPUT_FILE
	    ;;
	esac # $DAY
    done

    echo -en "\\\\end{tabular}\n\n" >> $OUTPUT_FILE
    ;; # $FORMAT="tex"

############
textable|table)
    mv -f $OUTPUT_FILE $OUTPUT_FILE~ 2>/dev/null
    Class_Days=($(get_class_days))

    echo "%% Created on $(date)" >> $OUTPUT_FILE
    echo "%% Automatically generated file." >> $OUTPUT_FILE

    # Table header
    echo -en "\\\\begin{tabular}{|" >> $OUTPUT_FILE
    for i in $(seq 0 6) # Make table columns
    do 
	if [ ${Class_Days[$i]} = 1 ]
	    then echo -n "c|" >> $OUTPUT_FILE
	fi
    done
    echo -en "}\n\\hline\n\\\\vp " >> $OUTPUT_FILE
    # End of header

    # First row
    for i in $(seq 0 6)
    do
	if [ ${Class_Days[$i]} = 1 ]
	    then echo -n "$(day_of_week $i name) " >> $OUTPUT_FILE
		if [ "$i" != "4" ]; then echo -n " & " >> $OUTPUT_FILE; fi
	fi
    done
    # End first row

    cat $SCHEDULE_DATA | while read DAY MONTH DATE SECTION TOPIC 
    do
	case "$DAY" in
	
	%%)
	    # Discard comments
	    ;;

	*) 
	    if [ "$DAY" == "M" ] # Assume all classes meet Monday
		then echo -en "\\\\\\ \n\\hline\n\\\\vp " >> $OUTPUT_FILE
	    fi

	    echo -n "$MONTH~$DATE: \\hfill " >> $OUTPUT_FILE

	    case "$SECTION" in
	    Hol|Holiday)
		echo -en "$TOPIC " >> $OUTPUT_FILE
		;;

	    Mid)
		echo -en "\\\\textbf{$TOPIC} " >> $OUTPUT_FILE
		;;

	    Lab)
		echo -en "Computer Lab" >> $OUTPUT_FILE
		;;

	    Grp)
		echo -en "Group Work " >> $OUTPUT_FILE
		;;

	    Rev|Review)
		echo -en "Review " >> $OUTPUT_FILE
		;;

	    Std|Study)
		echo -en "Study Period " >> $OUTPUT_FILE
		;;

	    Exm|Final)
		echo -en "Final Exams " >> $OUTPUT_FILE
		;;

	    *)
		echo -en "Sect~$SECTION " >> $OUTPUT_FILE
		;;

	    esac # $SECTION

	    if [ "$DAY" != "F" ]; then echo -n " & " >> $OUTPUT_FILE; fi
	    ;;
	esac # $DAY
    done

    echo -en "\\\\\\ \n\\hline\n" >> $OUTPUT_FILE
    echo -en "\\\\end{tabular}\n\n" >> $OUTPUT_FILE
    ;; # $FORMAT="textable"

############
html|htm)
    mv -f $OUTPUT_FILE $OUTPUT_FILE~
    echo -en "<!--\nCreated on $(date)\n" >> $OUTPUT_FILE
    echo -en "Automatically generated file.\n-->\n" >> $OUTPUT_FILE
    echo -e "<center>\n<table BORDER WIDTH=\"90%\" BGCOLOR=\"#ffeeff\">" >> $OUTPUT_FILE

    echo -en "\n<tr>\n" >> $OUTPUT_FILE
    echo -en "<td width=\"3%\">\n<b>Day</b>\n</td>\n\n" >> $OUTPUT_FILE
    echo -en "<td align=center width=\"10%\">\n<b>Date</b>\n</td>\n\n" >> $OUTPUT_FILE
    echo -en "<td align=center width=\"15%\">\n<b>Section</b>\n</td>\n\n" >> $OUTPUT_FILE
    echo -en "<td align=center>\n<b>Topics</b>\n</td>\n\n" >> $OUTPUT_FILE
    echo -en "<tr>\n" >> $OUTPUT_FILE

    cat $SCHEDULE_DATA | while read DAY MONTH DATE SECTION TOPIC 
    do
	case "$DAY" in
	
	%%)
	    # Discard comments
	    ;;

	*)
	    echo -en "\n<tr>\n" >> $OUTPUT_FILE
	    echo -en "<td align=center>\n$DAY  \n</td>\n\n" >> $OUTPUT_FILE
	    echo -en "<td>\n$MONTH  $DATE\n</td>\n\n" >> $OUTPUT_FILE

	    case "$SECTION" in
	    Hol|Holiday)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\n$TOPIC\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    Rev|Review)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td>\nReview\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    Std|Study)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\nStudy Period\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    Exm|Final)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\nFinal Exams\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    Mid|Midterm)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=center><font color=\"#aa00ff\">\n" >> $OUTPUT_FILE
		echo -en "<b>$TOPIC</b>\n</font></td>\n\n" >> $OUTPUT_FILE
		;;

	    Lab)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\n$TOPIC\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    Grp)
		echo -en "<td>\n&nbsp;\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\n$TOPIC\n</td>\n\n" >> $OUTPUT_FILE
		;;

	    *)
		echo -en "<td>\nSection  $SECTION\n</td>\n\n" >> $OUTPUT_FILE
		echo -en "<td align=left>\n$TOPIC\n</td>\n\n" >> $OUTPUT_FILE
		;;
	    esac # $SECTION
	    echo -en "</tr>\n" >> $OUTPUT_FILE
	    ;;
	esac # $DAY
    done

    echo -en "</table>\n\n</center>\n\n" >> $OUTPUT_FILE
    ;; # $FORMAT="html"

############
*)
    echo -e "Unknown output format \"$FORMAT\"\n"
    exit 1
    ;;

esac # FORMAT
}
#### End of print_table ####

test_fcn() {
    if [ $1 = 1 ]; then echo -n " is "; else echo -n " is not "; fi
}

#### Script proper starts here ####

holiday_data

YEAR=$(get_year)
IS_LEAP=$(is_leap $YEAR)
DAY_ONE=$(get_day_one)

Class_Days=($(get_class_days))

schedule_data

print_table $FORMAT

exit 0

#### End of script ####

