/*======================================================================
 *                            Employee List
 *======================================================================
 * Prints ordered lists of employee information and employee statistics.
 *----------------------------------------------------------------------
 * Author:      William Breathitt Gray
 * Date:        January 20, 2012
 * Version:     1.0
 * License:     Simplified BSD License
 *
 * Language:    C
 * Standard:    C99
 * Doc System:  Doxygen v1.7.6.1
 *----------------------------------------------------------------------
 * Copyright (c) 2012, William Breathitt Gray
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *======================================================================
 *======================================================================
 */
/**
 * @file
 * Prints ordered lists of employee information and employee statistics.
 *
 * @author      William Breathitt Gray
 * @date        January 20, 2012
 * @version     1.0
 * @copyright   Simplified BSD License
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct employee{
	int points;
	char name[50];
	struct employee *next;
} employee;

employee* addNode(employee* head, employee* node);
employee* getPresidentList(employee* head);
void freeList(employee* head);
void printList(employee* head, FILE* stream);

int main(void){
	// files for input/output
	const char fInName[] = "employee_list.in";
	const char fOutName[] = "employee_list.out";
	
	// open input file
	FILE* fp = fopen(fInName, "r");
	if(fp == NULL){
		fprintf(stderr, "ERROR: Can't open input file %s!\n", fInName);
		exit(EXIT_FAILURE);
	}

	// get number of employees
	char buffer[64];
	fgets(buffer, sizeof(buffer), fp);

	unsigned int numEmployees = strtoul(buffer, NULL, 10);
	// check if number of employees is out of range
	if(errno == ERANGE){
		fprintf(stderr, "ERROR: The number of employees is out of the range of representable values!\n");
		exit(EXIT_FAILURE);
	}

	// add all employees to a linked list
	employee* head = NULL;
	employee* node;
	unsigned int i, totalPoints = 0;
	for(i = 0; i < numEmployees; i++){
		//allocate memory for node
		node = malloc(sizeof(*node));

		// get employee info
		fgets(buffer, sizeof(buffer), fp);

		// set employee name
		strncat(node->name, strtok(buffer, " "), sizeof(node->name)-1);

		// set employee points
		node->points = strtol(strtok(NULL, ""), NULL, 10);
		// check if points is out of range
		if(errno == ERANGE){
			fprintf(stderr, "ERROR: The value of the points of the employee %s is out of the range of representable values!\n", node->name);
			exit(EXIT_FAILURE);
		}

		// sum total points
		totalPoints += node->points;

		// add node to linked list of employees in order of increasing points
		head = addNode(head, node);
	}

	// close input file, open output file
	fclose(fp);
	fp = fopen(fOutName, "w");
	if(fp == NULL){
		fprintf(stderr, "ERROR: Can't open output file %s!\n", fOutName);
		exit(EXIT_FAILURE);
	}

	// print results to output file
	fprintf(fp, "Vice President's List:\n");
	printList(head, fp);

	fprintf(fp, "\nPresident's List:\n");
	head = getPresidentList(head);
	printList(head, fp);

	float averagePoints = (float)totalPoints/numEmployees;
	fprintf(fp, "\nTotal Points Earned:\n%d\n"
				"\nAverage Points Earned:\n%.f\n", totalPoints, averagePoints);

	// clean up
	freeList(head);
	fclose(fp);

	return 0;
}

/**
 * Adds an employee to an employee list in order of increasing value of
 * points.
 *
 * @param head pointer to the start of the list
 * @param node pointer to the employee being added to the list
 *
 * @return pointer to the start of the list
 */
employee* addNode(employee* head, employee* node){
	// if list is empty
	if(head == NULL)
		return node;

	// insert node to front
	if(node->points > head->points){
		node->next = head;
		return node;
	}

	// find position to insert node
	employee* curr = head;
	while(curr->next != NULL){
		if(node->points > curr->next->points)
			break;
		curr = curr->next;
	}

	// insert node
	node->next = curr->next;
	curr->next = node;
	return head;
}

/**
 * Reverses the list and inserts the employee with the highest value of
 * points into the front of the list.
 *
 * @param curr pointer to the start of the list
 *
 * @pre
 *     The list passed in must be sorted in order of increasing value of
 *     points.
 *
 * @return pointer to the start of the list
 */
employee* getPresidentList(employee* curr){
	// if list is empty or contains only one node
	if(curr == NULL || curr->next == NULL)
		return curr;

	// employee with highest value of points is already at front of list
	employee* highest = curr;
	curr = curr->next;

	// reverse list (skipping first node)
	employee* hind = NULL;
	employee* fore = curr->next;
	do{
		// point to previous node
		curr->next = hind;

		// move to next positions
		hind = curr;
		curr = fore;
		fore = fore->next;

		// check if highest earner
		if(curr->points > highest->points)
			highest = curr;
	}while(fore != NULL);
	// take care of trailing node
	curr->next = hind;

	// put highest earner in front of list
	highest->next = curr;

	return highest;
}

/**
 * Frees an employee list from memory.
 */
void freeList(employee* head){
	// free memory for each node
	employee* temp;
	while(head != NULL){
		temp = head->next;
		free(head);
		head = temp;
	}
}

/**
 * Prints an employee list to an output stream.
 *
 * @param node pointer to the start of the list
 * @param stream pointer to file stream for output
 */
void printList(employee* node, FILE* stream){
	int i = 1;
	while(node != NULL){
		fprintf(stream, "%d) %s %d\n", i, node->name, node->points);
		node = node->next;
		i++;
	}
}
