//---------------------------------------------------------------------------------
// <copyright file="LqrSMACTempFile.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
// 	Implementation of TempFile class, which creates a file with an arbitrary name
//  and makes it available to the caller.  The object lifespan is used to determine
//  the file life span: when the object is deleted, the file is also deleted.
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include "LqrSMACTempFile.h"
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

using namespace std;

namespace LiquidRobotics {
namespace SMACFramework {

TempFile::TempFile(unsigned maxTries) throw()
: fd(-1)
{
	unsigned timesLeft = maxTries;

	while((timesLeft-- > 0) && (fd == -1))
	{
		{
			char fileNameBuffer[256];
			tmpnam(fileNameBuffer);

			fileName = fileNameBuffer;
		}

		fd = open(fileName.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
	}
}

TempFile::~TempFile()
{
	if (fd != -1)
		close(fd);

	if (fileName.length())
		unlink(fileName.c_str());
}

void TempFile::Close() throw()
{
	if (fd != -1)
	{
		close(fd);
		fd = -1;
	}
}

const string& TempFile::GetName() const
{
	return fileName;
}

bool TempFile::IsOpen() const throw()
{
	return fd != -1;
}

void TempFile::Write(const ByteArray& data) throw(std::runtime_error)
{
	if (fd == -1)
		throw runtime_error("Temp file is not open");

	if (!data.size())
		return;

	lseek(fd, 0L, SEEK_END);

	const size_t toWrite = data.size();
	const size_t writeSize = write(fd, &data.at(0), toWrite);

	if (toWrite != writeSize)
		throw runtime_error("Temp file write failure");
}

void TempFile::GetContent(ByteArray& result) const
{
	if (fd == -1)
		throw runtime_error("Temp file is not open");

	off_t endPos = lseek(fd, 0L, SEEK_END);
	off_t startPos = lseek(fd, 0L, SEEK_SET);

	if (endPos < 0L)
		throw runtime_error("Failed seek to temp file end");

	if (startPos < 0L)
		throw runtime_error("Failed seek to temp file start");

	result.resize(endPos);

	if (result.size())
	{
		size_t readSize = read(fd, &result.front(), (size_t) endPos);

		if (readSize != (size_t) endPos)
			throw runtime_error("Failed to read temp file content");
	}
}

// returns the size of the file
off_t TempFile::GetSize(bool throwOnError) const
{
	off_t endPos = (off_t) -1;

	if (fd == -1)
	{
		if (throwOnError)
			throw runtime_error("Temp file is not open");
	}
	else
	{
		off_t initPos = lseek(fd, (off_t) 0, SEEK_CUR);

		if (initPos == (off_t) -1)
		{
			if (throwOnError)
				throw runtime_error("Temp file seek to current position failed");
		}
		else
		{
			endPos = lseek(fd, (off_t) 0, SEEK_END);

			if (endPos == (off_t) -1)
			{
				if (throwOnError)
					throw runtime_error("Temp file seek to end failed");
			}
			else
			{
				off_t lastPos = lseek(fd, initPos, SEEK_SET);

				if ((lastPos != initPos) && throwOnError)
					throw runtime_error("Temp file return to original position failed");
			}
		}
	}

	return endPos;
}

}
}

