//-------------------------------------------------------------------------------------------------
// <copyright file="main.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
//   This is a filter program that takes a data stream on standard input and creates
//   a zip file containing a single file (datafile) for the content of the stream.
//   For example:
//
//		ls | aszip > foo.zip
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
#include <stdio.h>
#ifdef USING_ZIP_API
#include <zip.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include "LqrSMACTempFile.h"

using namespace std;
using namespace LiquidRobotics::SMACFramework;

// If the byte array (ba) is not empty, it is written
// to the temp file and the byte array size is zeroed.
inline void EmptyBuffer(TempFile& tf, ByteArray& ba)
{
	if (ba.size())
	{
		try
		{
			tf.Write(ba);
		}
		catch(exception& ex)
		{
			cerr << "Write: " << ex.what() << endl;
			throw runtime_error("Write error");
		}

		ba.resize(0);
	}
}

// Program entry point
int main(int argc, char* argv[])
{
	// don't allow any parameters
	if (argc != 1)
	{
		cerr << "Usage: input-stream-command | " << argv[0] << " > output.zip" << endl;
		return 1;
	}

	// create two temporary files (names)
	TempFile dataFile, zipFile;

	// fail if no data file
	if (!dataFile.IsOpen())
	{
		cerr << "Unable to open temporary file to hold data" << endl;
		return 1;
	}

	// fail if no zip file
	if (!zipFile.IsOpen())
	{
		cerr << "Unable to open temporary file to hold zip archive" << endl;
		return 1;
	}

	int exitStatus = 0;

	try
	{
		const string dataFileName = dataFile.GetName();			// save the data file name
		const string zipFileName = zipFile.GetName();			// save the zip fil ename
		FILE* fp = stdin;										// rereference the stdin as the file to read
		char c;													// single character read from input
		ByteArray buffer;										// byte array buffer

		buffer.reserve(2048);									// up to 2048 bytes will be read between writes

		// read byte by byte and store to temp file
		while(c = fgetc(fp), !feof(fp) && !ferror(fp))
		{
			buffer.push_back((ByteArray::byte) c);

			if (buffer.size() == buffer.capacity())
				EmptyBuffer(dataFile, buffer);
		}

		// commit whatever is left in the buffer to the file
		EmptyBuffer(dataFile, buffer);

		dataFile.Close();										// closes the data file but the file still exists
		zipFile.Close();										// closes the zip file but the file still exists

		string cmdStr;											// command string to execute

		// build command string for zipping up the data file and correcting the zip file name
		{
			stringstream cmd;
			cmd << "zip -jq " << zipFileName << ".zip " << dataFileName
				<< " && mv " << zipFileName << ".zip " << zipFileName;

			cmdStr = cmd.str();
		}

		// store results
		int systemStatus = system(cmdStr.c_str());

		exitStatus = WIFEXITED(systemStatus) ? WEXITSTATUS(systemStatus) : 1;

#ifdef USING_ZIP_API
		// if still successful ...
		if (!exitStatus)
		{
			// rename the file internal to the zip file to "datafile"
			int err = 0;
			zip* zi = zip_open(zipFileName.c_str(), 0, &err);

			if (!zi)
			{
				cerr << "zip_open: failed to open " << zipFileName << " (" << err << ")" << endl;
				exitStatus = 1;
			}
			else
			{
				if (!!(err = zip_rename(zi, 0, "datafile")))
				{
					cerr << "zip_open: failed to rename the data file in " << zipFileName << " (" << err << ")" << endl;
					exitStatus = 1;
				}

				zip_close(zi);
			}
		}
#endif

		// if still successful, write the zip file to standard output
		if (!exitStatus)
		{
			{
				stringstream cmd;
				cmd << " cat " << zipFileName;

				cmdStr = cmd.str();
			}

			systemStatus = system(cmdStr.c_str());
			exitStatus = WIFEXITED(systemStatus) ? WEXITSTATUS(systemStatus) : 1;
		}
	}
	catch(...)
	{
		exitStatus = 1;
	}

	// return the status
	return exitStatus;

	// temp files are closed/deleted on exit
}
