/***********************************************************************
FILE            OLA7.CC
COURSE          CSCI-1170-006
AUTHOR          Patrick Riley Connor
INSTRUCTOR      Jungson Yoo
DUE DATE        1 December 2009
DESCRIPTION     This program organizes player information in a chart, 
                while being given the player's batting record. The 
                information in either file does not have to be organized
                in any way, as long as each player's records match with
                their record numbers.
INPUT           Two input files, formatted according to the project
                specification. See OLA7.PDF for details.
OUTPUT          Chart with player names, numbers, batting averages, and
                walk counts. Also shows players with most extreme 
                batting averages.
OTHER NOTES     This program was written in an IDE which helped me
                manage the code much cleaner. Indentation should be
                consistent through the whole code, regardless of the
                text editor reading it.
***********************************************************************/

using namespace std;
#include <iostream>     // Needed for cout and cin.
#include <string>       // String-based operations, such as at().
#include <iomanip>      // setw() and fixed.
#include <fstream>      // File manipulation.

// Documentation for each subroutine is located in the heading and
// inline in each. Subroutines are listed in order from top to bottom.
// Subroutine definitions start on line 86.
void ReadPlayerInfo(ifstream&, string [], int [], int&);
void ReadProcessBatting(ifstream&, const int [], float [], int [], int);
void ProcessRecord(string, float&, int&);
int SearchPosition(const int [], int, int);
void PrintTable(string [], int [], float [], int [], int);
int FindHigh(float [], int);
int FindLow(float [], int);
void OpenFilePrompt(ifstream&);

int main()
{
        const int maxPlayers = 20;      // Max number of players to use.
        string playerName[maxPlayers];  // Player names.
        int playerNumber[maxPlayers],   // Jersey numbers.
            walkCount[maxPlayers],      // Times each player walked.
            highBatter,                 // Index of highest batting avg.
            lowBatter,                  // Index of lowest batting avg.
            numPlayers = 0;             // Number of players on team.
        float batAverage[maxPlayers];   // Player's batting average.
        ifstream inputFile;             // Generic input file handle.
        
        // Ask user for player records file, process it, and close it.
        cout << "Please input path to the Player Records ";
        OpenFilePrompt(inputFile);
        ReadPlayerInfo(inputFile, playerName, playerNumber, numPlayers);
        inputFile.close();
        
        // Ask user for batting records file, process it, and close it.
        cout << "Please input path to the Batting Records ";
        OpenFilePrompt(inputFile);
        ReadProcessBatting(inputFile, playerNumber, batAverage,
                           walkCount, numPlayers);
        inputFile.close();
        
        // Print our table with our player's names, their numbers, the
        // batting averages, and the number of times each player walked.
        PrintTable(playerName, playerNumber, batAverage, walkCount, 
                   numPlayers);
        
        // Find the highest and lowest batting averages.
        highBatter = FindHigh(batAverage, numPlayers);
        lowBatter = FindLow(batAverage, numPlayers);
        
        // Report extreme batting averages.
        cout << fixed << setprecision(3)
             << "Highest batting average: " << batAverage[highBatter]
             << " by " << playerName[highBatter] << endl
             << "Lowest batting average: " << batAverage[lowBatter]
             << " by " << playerName[lowBatter] << endl << endl;
        
        return 0;       // Ta-da!
}


/***********************************************************************
 * 
 * 
 *                    Start subprogram definitions
 * 
 * 
 **********************************************************************/

void ReadPlayerInfo(ifstream& fileRead, string nameArray[], 
                    int idArray[], int& arraySize)
{
        // This function reads each player name and player number and
        // stores them in the proper array.
        
        // Read our first set of information. Used as priming read.
        fileRead >> nameArray[arraySize] >> idArray[arraySize];
        while (fileRead)
        {
                arraySize++;    // Increment the size of our array.
                fileRead >> nameArray[arraySize] >> idArray[arraySize];
                // Attempt to read in more information.
        }
}

void ReadProcessBatting (ifstream& fileRead, const int idArray [],
                         float battingArray [], int walkArray[], 
                         int arraySize)
{
        // Processes the batting record information and pair it with 
        // each player the record goes to.
        int location,                   // Index of player in array.
            playerNum;                  // The player's jersey number.
        string batRecord;               // Batting record read.
        for (int i = 0; i < arraySize; i++)
        {
                fileRead >> playerNum >> batRecord;     // Read in info.
                
                // Find which player the information goes to.
                location = SearchPosition(idArray, playerNum,
                                          arraySize);
                
                // Process the information and match it up with player.
                ProcessRecord(batRecord, battingArray[location],
                              walkArray[location]);
        }
}

void ProcessRecord(string battingRecord, float& battingAverage,
                   int& walkCount)
{       
        // This function determines a batting average and counts walks
        // from a record in a string in the format of "WOH", where W is
        //  a walk, O is an out, and H is a hit.
        
        int hitCount = 0, batCount = 0; // Make some counter variables.
        walkCount = 0;                  // Initialize our walk counter.
        for(int i = 0; i < battingRecord.length(); i++) // Scan string.
        {
                switch (battingRecord.at(i))    // Read our record.
                {
                        case 'W':
                                walkCount++;    // Walk doesn't count as
                                break;          // a time at the bat.
                        case 'H':               // A hit counts as a hit
                                hitCount++;     // and a time at bat.
                        case 'O':               // An out only qualifies
                                batCount++;     // as a time at bat.
                }
        }
        battingAverage = 1.0 * hitCount / batCount;
                // Calculate batting average. Use 1.0 in there to allow
                // for float division.
}

int SearchPosition(const int searchArray[], int find, int arraySize)
{
        // Searches for value find inside of searchArray[]. Returns
        // index of found value, else returns -1 if value not found.
        for(int i = 0; i < arraySize; i++)      // Scan our array.
                if (searchArray[i] == find)     // If value was found...
                        return i;               // return its index.
        return -1;                              // Otherwise, error.
}

void PrintTable(string nameArray[], int idArray[], float battingArray[],
              int walkArray[], int arraySize)
{
        // Print our table with all the information listed in each of
        // our parallel arrays.
        cout << "\n Player Name | Number | Batting Average | Walks\n"
             << "-------------+--------+-----------------+-------\n";
        for(int i = 0; i < arraySize; i++)
        {
                cout << ' ' << setw(11) << left << nameArray[i] << right
                     << " | " << setw(4) << idArray[i] << "   |      "
                     << fixed << setprecision(3) << battingArray[i]
                     << "      |   " << setprecision(0) << walkArray[i]
                     << endl;
        }
}

int FindHigh(float searchArray[], int arraySize)
{
        // Finds the highest value in searchArray and returns the index.
        
        int largeIndex = 0;
                // Variable for storing our largest value's index.
                // initialize it with the first index.

        for(int i = 0; i < arraySize; i++)      // Scan for larger.
                if (searchArray[i] > searchArray[largeIndex])
                        largeIndex = i;
                // If larger value is found, store the new index.

        return largeIndex;      // Return index of largest value.
}

int FindLow(float searchArray[], int arraySize)
{
        // Finds the lowset value in searchArray and returns the index.
        int smallIndex = 0;
                // Variable for storing our smallest value's index.
                // Initialize it with the first index.

        for(int i = 0; i < arraySize; i++)      // Scan for smaller.
                if (searchArray[i] < searchArray[smallIndex])
                        smallIndex = i;
                // If smaller value is found, store the new index.

        return smallIndex;      // Return index of smallest value.
}

void OpenFilePrompt(ifstream& fileStream)
{
        // Prompts user for a file name and attempts to open it. If it
        // fails, ask user for new filename and try again.
        string fileName;        // Store our file name.
        do
        {
                cout << "File: ";       // Ask for the file.
                cin >> fileName;
                fileStream.open(fileName.c_str());      // Attempt open.
                if (!fileStream)        // If fail, prompt for new file.
                        {
                                cout    << "There was an error opening "
                                        << "the file specified. Try a "
                                        << "different file name.\n\n";
                        }
        } while (!fileStream);  // Keep going until open file success.
}
