// implementation file: main.cpp in project Coding. Console application.
// description:         Root for testing coding abstractions and concretions.
//                      IO part: input from command line arguments, output to console.
// companion documents: http://informatik.karlheinz-hug.de/Lehrmaterial/Informatik_3/Praktikum/Projekte/
//                      PUe32-Projektuebungsblatt-Codierung.pdf
//                      PUe32-Beispielentwurf-Codierung.pdf
// variant:             2 with flexible composition
// author:              Karlheinz Hug
// creation date:       2012-07-04
// last update:         2012-10-03

#include <cstdlib>
#include <iostream>
#include <string>

#include "Coder.hpp"
#include "CoderFactory.hpp"
#include "FileCoder.hpp"

	std::string command_name;

	void show_usage_exit (std::string bad_message)
	{
		std::cout << bad_message << std::endl <<
			"Please call " << command_name << " with these nonempty arguments:" << std::endl <<
			command_name << " source_filename target_filename codings" << std::endl <<
			"codings:   'copy' | ( 'encode' | 'decode' ) ( compress | crypt ) { crypt }" << std::endl <<
			"compress:  'compress1' | 'compress2'" << std::endl <<
			"crypt:     'caesar' caesarkey | 'vernam' vernamkey" << std::endl <<
			"caesarkey: integer" << std::endl <<
			"vernamkey: string" << std::endl;
		exit(1);
	}

	void check (bool good, std::string bad_message)
	{
		if (!good) show_usage_exit(bad_message);
	}

	CoderFactory::CoderType coder_type (std::string coding)
	{
		if (coding.compare("caesar") == 0)
		{
			return CoderFactory::caesar;
		}
		else if (coding.compare("vernam") == 0)
		{
			return CoderFactory::vernam;
		}
		else
		{
			show_usage_exit("Wrong argument: crypt method unknown!");
			exit(2); // Unreachable, but avoids compiler warning.
		}
	}

	int main (int argc, char * argv[])
	{
		// Input from command line:
		command_name = argv[0];
		check(argc >= 4, "Missing arguments!");
		std::string source_filename = argv[1];
		std::string target_filename = argv[2];
		std::string function        = argv[3];
		check(source_filename.size() > 0, "Wrong argument 1: source_filename must be nonempty!");
		check(target_filename.size() > 0, "Wrong argument 2: target_filename must be nonempty!");
		check(function.size() > 0,        "Wrong argument 3: function must be nonempty!");
		check(source_filename.compare(target_filename) != 0, "Wrong arguments 1, 2: filenames must be different!");

		Coder * coder = 0;
		if (function.compare("copy") == 0)
		{
			check(argc == 4, "Too many arguments!");
			coder = CoderFactory::new_coder(0, CoderFactory::copier, "");
		}
		else if (function.compare("encode") == 0 || function.compare("decode") == 0)
		{
			check(argc >= 5, "Missing coding arguments!");
			std::string coding = argv[4];
			int i = 5;
			check(coding.size() > 0, "Wrong argument 4: coding must be nonempty!");
			std::string key;
			if (std::string(coding).compare("compress1") == 0 || std::string(coding).compare("compress2") == 0)
			{
				coder = CoderFactory::new_coder(0, CoderFactory::expander, "");
			}
			else
			{
				check(i < argc, "Missing key argument!");
				key = argv[i];
				++i;
				check(key.size() > 0, "Wrong argument 5: key must be nonempty!");
				coder = CoderFactory::new_coder(0, coder_type(coding), key);
			}
			while (i < argc)
			{
				coding = argv[i];
				++i;
				check(coding.size() > 0, "Wrong argument: coding must be nonempty!");
				check(i < argc, "Missing key argument!");
				key = argv[i];
				++i;
				check(key.size() > 0, "Wrong argument: key must be nonempty!");
				coder = CoderFactory::new_coder(coder, coder_type(coding), key);
			}
		}
		else
		{
			show_usage_exit("Wrong argument 3: function must be 'copy', 'encode' or 'decode'!");
		}

		// Calls to functional part:
		FileCoder file_coder(coder);
		if (function.compare("encode") == 0)
		{
			file_coder.encode(source_filename, target_filename);
		}
		else
		{
			file_coder.decode(source_filename, target_filename);
		}

		// Output to console:
		std::cout << file_coder.coder_name() << " ";
		switch (file_coder.result())
		{
			case FileCoder::done :
				std::cout << "done";
				break;
			case FileCoder::failed_open :
				std::cout << "failed open";
				break;
			case FileCoder::failed_write :
				std::cout << "failed write";
				break;
			case FileCoder::failed_decode :
				std::cout << "failed decode";
				break;
			default :
				std::cout << "fatal error!";
				break;
		}
		std::cout << std::endl;
		return 0;
	}
