//
// Program name:  better_paste
// Author:  B. Massingill 
//
// Purpose:  simplified version of Unix "paste" command (but less
//	simplified than the simplified_paste program)
//
// Input:  
//	how many input files, their names, and name of an output file,
//	from standard input
// Output:  
//	none to standard output
//	output file has been created, with each line being the 
//		concatenation of the corresponding lines of the 
//		input files, separated by a tab character.
//		if the input files don't have the same numbers of lines,
//		"missing" lines are filled in with empty lines.
//
#include <iostream.h>
#include <fstream.h>		// has ifstream, ofstream
#include <iomanip.h>		// has setw()

const int MaxFileNameLength = 1000;	// maximum length of filename

// **** function prototypes ****

//
// function to prompt for an input file name and open an input stream
//
// precondition:
//	descrip is a description of the file (e.g., "input file")
// postcondition:
//	user has been prompted to enter a filename
//	input stream instream has been opened for the specified filename
//	(if the filename entered by the user cannot be opened, prompts 
//		again, and continues to prompt until the user enters
//		a filename that can be opened)
//
void obtainInputFile(const char descrip[], ifstream& instream);

//
// function to prompt for an output file name and open an output stream
//
// precondition:
//	descrip is a description of the file (e.g., "output file")
// postcondition:
//	user has been prompted to enter a filename
//	output stream outstream has been opened for the specified filename
//	(if the filename entered by the user cannot be opened, prompts 
//		again, and continues to prompt until the user enters
//		a filename that can be opened)
//
void obtainOutputFile(const char descrip[], ofstream& outstream);

//
// function to prompt for a filename
//
// precondition:
//	descrip is a description of the file whose name is to be
//		entered
//	MaxLength is the length of array fName
// postcondition:
//	user has been prompted with prompt string, and fName contains
//		the string entered in response
//
void obtainFileName(const char descrip[], char fName[], const int MaxLength);

//
// function to see if there is anything more to read in an input stream
//
// precondition:
//	instream is an input stream, successfully opened
// postcondition:
//	return value is true if there is at least one character left to
//		read in instream, false if not
//
bool moreToRead(ifstream& instream);

// function to apply moreToRead to multiple input streams
//
// precondition:
// postcondition:

bool moreInSomeFile(ifstream instream[], const int numFiles);

//
// function to copy a line, up to but not including end-of-line
//
// precondition:
//	instream is an input stream, successfully opened
//	outstream is an output stream, successfully opened
// postcondition:
//	characters have been copied from instream to outstream, up to
//		but not including the next '\n' (end of line) character.
//		the '\n' character is read but discarded.
//
// note:  this function can still be called even if instream has reached
//	end of file; no characters will be copied in that case.
//	
void copyLine(ifstream& instream, ofstream& outstream);

//
// **** main program ****
//
int main()
{
	const int MaxFiles=10;
	int numFiles;
	ifstream infiles[MaxFiles];
	ofstream out;

	cout << "How many input files? (maximum of " << MaxFiles <<")\n";
	cin >> numFiles;
	if (numFiles > MaxFiles)
	{
		cout << "Too many!\n";
		exit(1);
	}

	for (int i=0; i<numFiles; i+=1)
		obtainInputFile("next input file", infiles[i]);

	obtainOutputFile("output file", out);

	while (moreInSomeFile(infiles, numFiles))
	{
		for (int i=0; i<numFiles; i+=1)
		{
			if (i > 0)
				cout << '\t';
			copyLine(infiles[i], out);
		}
		out << endl;
	}

	for (int i=0; i<numFiles; i+=1)
		infiles[i].close();
	out.close();

	return 0 ;
}

//
// **** function definitions ****
// (see prototypes for comments)
//

void obtainInputFile(const char descrip[], ifstream& instream) 
{
	char fName[MaxFileNameLength];
	bool gotFile = false;
	while (!gotFile)
	{
		obtainFileName(descrip, fName, MaxFileNameLength);
		instream.open(fName);
		if (instream.fail())
			cerr << "Unable to open file " << fName << endl;
		else
			gotFile = true;
	}
	return;
}


void obtainOutputFile(const char descrip[], ofstream& outstream) 
{
	char fName[MaxFileNameLength];
	bool gotFile = false;
	while (!gotFile)
	{
		obtainFileName(descrip, fName, MaxFileNameLength);
		outstream.open(fName);
		if (outstream.fail())
			cerr << "Unable to open file " << fName << endl;
		else
			gotFile = true;
	}
	return;
}


void obtainFileName(const char descrip[], char fName[], const int MaxLength)
{
	cerr << "Please enter the name of the " << descrip << 
		" (maximum of " << MaxLength << " characters):  ";
	cin >> setw(MaxLength) >> fName;
}


bool moreToRead(ifstream& instream)
{
	char c;

	if (instream.eof())
		return false;
	else 
	{
		if (instream.get(c))
		{
			instream.putback(c);
			return true;
		}
		else
			return false;
	}
}


void copyLine(ifstream& instream, ofstream& outstream)
{
	char c;

	while (instream.get(c) && c != '\n')
	{
		outstream.put(c);
	}
}

bool moreInSomeFile(ifstream instreams[], const int numFiles)
{
	bool foundSome = false;
	for (int i=0; i<numFiles; i+=1)
		if (moreToRead(instreams[i]))
			foundSome = true;
	return foundSome;
}