//******************************************************************
// Election program
// This program reads votes represented by precinct number and
// ballot position from a data file, calculates the sums per
// precinct and per candidate, and writes all totals to an
// output file
//******************************************************************
#include <iostream>
#include <iomanip>    // For setw()
#include <fstream>    // For file I/O
#include <string>     // For string class

using namespace std;

const int NUM_PRECINCTS = 4;
const int NUM_CANDIDATES = 4;

typedef int VoteArray[NUM_PRECINCTS][NUM_CANDIDATES];
                                 // 2-dimensional array type
                                 //   for votes
void GetNames( string[] );
void OpenForInput( ifstream& );
void OpenForOutput( ofstream& );
void WritePerCandidate( const VoteArray, const string[],
                        ofstream& );
void WritePerPrecinct( const VoteArray, ofstream& );
void WriteReport( const VoteArray, const string[], ofstream& );
void ZeroVotes( VoteArray );

int main()
{
    string    name[NUM_CANDIDATES];  // Array of candidate names
    VoteArray votes;         // Totals for precincts vs. candidates
    int       candidate;     // Candidate number input from voteFile
    int       precinct;      // Precinct number input from voteFile
    ifstream  voteFile;      // Input file of precincts, candidates
    ofstream  reportFile;    // Output file receiving summaries

    OpenForInput(voteFile);
    if ( !voteFile )
        return 1;
    OpenForOutput(reportFile);
    if ( !reportFile )
        return 1;

    GetNames(name);
    ZeroVotes(votes);

    // Read and tally votes

    voteFile >> precinct >> candidate;
    while (voteFile)
    {
        votes[precinct-1][candidate-1]++;
        voteFile >> precinct >> candidate;
    }

    // Write results to report file

    WriteReport(votes, name, reportFile);
    WritePerCandidate(votes, name, reportFile);
    WritePerPrecinct(votes, reportFile);

    return 0;
}

//******************************************************************

void OpenForInput( /* inout */ ifstream& someFile )    // File to be
                                                       // opened
// Prompts the user for the name of an input file
// and attempts to open the file

// Postcondition:
//     The user has been prompted for a file name
//  && IF the file could not be opened
//         An error message has been printed
// Note:
//     Upon return from this function, the caller must test
//     the stream state to see if the file was successfully opened

{
    string fileName;    // User-specified file name

    cout << "Input file name: ";
    cin >> fileName;

    someFile.open(fileName.c_str());
    if ( !someFile )
        cout << "** Can't open " << fileName << " **" << endl;
}

//******************************************************************

void OpenForOutput( /* inout */ ofstream& someFile )   // File to be
                                                       // opened
// Prompts the user for the name of an output file
// and attempts to open the file

// Postcondition:
//     The user has been prompted for a file name
//  && IF the file could not be opened
//         An error message has been printed
// Note:
//     Upon return from this function, the caller must test
//     the stream state to see if the file was successfully opened

{
    string fileName;    // User-specified file name

    cout << "Output file name: ";
    cin >> fileName;

    someFile.open(fileName.c_str());
    if ( !someFile )
        cout << "** Can't open " << fileName << " **" << endl;
}

//******************************************************************

void GetNames( /* out */ string name[] )    // Array of candidate
                                            //   names
// Reads the candidate names from standard input

// Postcondition:
//     The user has been prompted to enter the candidate names
//  && name[0..NUM_CANDIDATES-1] contain the input names,
//     truncated to 10 characters each

{
    string inputStr;      // An input string
    int    candidate;     // Loop counter

    cout << "Enter the names of the candidates, one per line,"
         << endl << "in the order they appear on the ballot."
         << endl;

    for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
    {
        cin >> inputStr;
        name[candidate] = inputStr.substr(0, 10);
    }
}

//******************************************************************

void ZeroVotes( /* out */ VoteArray votes )  // Array of vote totals

// Zeroes out the votes array

// Postcondition:
//     All votes[0..NUM_PRECINCTS-1][0..NUM_CANDIDATES-1] == 0

{
    int precinct;       // Loop counter
    int candidate;      // Loop counter

    for (precinct = 0; precinct < NUM_PRECINCTS; precinct++)
        for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
            votes[precinct][candidate] = 0;
}

//******************************************************************

void WriteReport(
       /* in */    const VoteArray votes,         // Total votes
       /* in */    const string    name[],        // Candidate names
       /* inout */       ofstream& reportFile )   // Output file

// Writes the vote totals in tabular form to the report file

// Precondition:
//     votes[0..NUM_PRECINCTS-1][0..NUM_CANDIDATES] are assigned
//  && name[0..NUM_CANDIDATES-1] are assigned
// Postcondition:
//     The name array has been output across one line, followed by
//     the votes array, one row per line

{
    int precinct;       // Loop counter
    int candidate;      // Loop counter

    // Set up headings

    reportFile << "            ";
    for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
        reportFile << setw(12) << name[candidate];
    reportFile << endl;

    // Print array by row

    for (precinct = 0; precinct < NUM_PRECINCTS; precinct++)
    {
        reportFile << "Precinct" << setw(4) << precinct + 1;
        for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
            reportFile << setw(12) << votes[precinct][candidate];
        reportFile << endl;
    }
    reportFile << endl;
}

//******************************************************************

void WritePerCandidate(
       /* in */    const VoteArray votes,         // Total votes
       /* in */    const string    name[],        // Candidate names
       /* inout */       ofstream& reportFile )   // Output file

// Sums the votes per person and writes the totals to the
// report file

// Precondition:
//     votes[0..NUM_PRECINCTS-1][0..NUM_CANDIDATES] are assigned
//  && name[0..NUM_CANDIDATES-1] are assigned
// Postcondition:
//     For each person i, name[i] has been output,
//     followed by the sum
//     votes[0][i] + votes[1][i] + ... + votes[NUM_PRECINCTS-1][i]

{
    int precinct;       // Loop counter
    int candidate;      // Loop counter
    int total;          // Total votes for a candidate

    for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
    {
        total = 0;

        // Compute column sum

        for (precinct = 0; precinct < NUM_PRECINCTS; precinct++)
            total = total + votes[precinct][candidate];

        reportFile << "Total votes for"
                   << setw(10) << name[candidate] << ":"
                   << setw(3) << total << endl;
    }
    reportFile << endl;
}

//******************************************************************

void WritePerPrecinct(
           /* in */    const VoteArray votes,         // Total votes
           /* inout */       ofstream& reportFile )   // Output file

// Sums the votes per precinct and writes the totals to the
// report file

// Precondition:
//     votes[0..NUM_PRECINCTS-1][0..NUM_CANDIDATES] are assigned
// Postcondition:
//     For each precinct i, the value i+1 has been output,
//     followed by the sum
//     votes[i][0] + votes[i][1] + ... + votes[i][NUM_CANDIDATES-1]

{
    int precinct;       // Loop counter
    int candidate;      // Loop counter
    int total;          // Total votes for a precinct

    for (precinct = 0; precinct < NUM_PRECINCTS; precinct++)
    {
        total = 0;

        // Compute row sum

        for (candidate = 0; candidate < NUM_CANDIDATES; candidate++)
            total = total + votes[precinct][candidate];

        reportFile << "Total votes for precinct"
                   << setw(3) << precinct + 1 << ':'
                   << setw(3) << total << endl;
    }
}
