/******************************************************************************
 * This program allows the user to enter bytes as sequences of ones and zeros.
 * It converts each byte string to a char by calculating the integer 
 * represented by the string, using a char variable as a numeric variable.
 * The program writes each byte storing a character to a file using .write.
 * Finally, the program reads back the binary file byte by byte, displaying 
 * the corresponding string of ones and zeros to the screen.
 *
 * After execution, the directory containing the executable will also contain 
 * a file called myBits.txt, with the bytes described above. It will generally
 * look like nonsense, because most bytes do not represent recognizable 
 * characters. If you enter values in the 65-90 range, you should see 
 * uppercase letters.
 * **************************************************************************/

#include<iostream>
#include<cstdlib>
#include<fstream>
#include<iomanip>
#include<string>
using namespace std;

/* 'okByte' tests the parameter string for being a valid byte. If it is, the 
 * function returns 'true'. If the length is not 8, or if characters 
 * other than '1' and '0' occur, the function returns 'false'.
 */
bool okByte(string test)
{
	if(test.size()!=8)
		return false;

	for (int i=0; i!=8; i++)
		if(test[i]!='0' && test[i]!='1')
			return false;
	return true;
}

/* 'makeByte' verifies that a string contains eight 1's and 0's. If so, it
 * calculates the numerical value that results from interpreting the string
 * as a binary number. This calculation occurs in a 'char' variable. Both 
 * C and C++ support such use of char's for small integers. The function 
 * returns the char containing the result.
 */ 
char makeByte(string theByte)
{
	char theChar=0;
	if(!okByte(theByte))
		exit(1);

	for (int i=0; i!=8; i++)
		theChar=theChar*2+(theByte[i]=='1'?1:0);
	return theChar;
	
}
		
/* 'displayBytes' displays the binary representation of the 'char' parameter
 * to the screen.
 */
void displayBytes(char value) 
{
	int SHIFT=sizeof(char)*8-1;
	unsigned MASK=1<<SHIFT;
	for(int i=0; i!=SHIFT+1; i++)
	{
		cout<<(value & MASK?'1':'0');
		value<<=1;
	}
	cout<<" ";
}


int main()
{
	
	unsigned x,y;
	char aByte;
	string byteString;
	ofstream outFile("myBits.txt", ios::binary);
	if(!outFile)
	{
		cout<<"\nUnable to open myBits.txt.";
		exit(1);
	}

	cout<<"Enter your space-separated bytes, <CTRL>-D to end: ";
	
	/* While there is a valid byte string in the cin stream, the loop
	 * reads it into 'byteString', converts it to the corresponding char,
	 * and writes that char to the file.
	 */
	while(cin>>byteString && okByte(byteString))
	{
	aByte=makeByte(byteString);
	outFile.write(&aByte,sizeof(char));
	}
	
	outFile.close();

	ifstream inFile("myBits.txt", ios::binary);
	if(!inFile)
	{
		cout<<"\nUnable to open myBits.txt.";
		exit(1);
	}
	

	
/* Read the file back in binary, using 'char'-sized pieces. Display the
 * byte string corresponding to the char value on the screen. */
	inFile.read(reinterpret_cast<char *>(&aByte),sizeof(char));
	int counter=0;
	while(inFile && !inFile.eof())
	{
	displayBytes(aByte);
	counter++;
	if(counter%4==0)
		cout<<endl;
	inFile.read(&aByte,sizeof(char));
	}
	inFile.close();
	return 0;
}
