//----------------------------------------------------------------------------------
// <copyright file="LqrSMACTZTools.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 some utility functions that assist in converting times; note
//   that time_t does not imply anything about zone, and in general it should always
//   indicate the time in the local time zone in order for functions like localtime
//   and gmtime to work properly.
//
//	 These functions throw std::runtime_error in case of failure.
//
//	 Be sure to set the appropriate time zone variables (TZ) and call tzset() at
//	 program startup.
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#include "LqrSMACTZTools.h"
#include "LqrSMACGetErrorDescription.h"
#include <sstream>
#include <string>
#include <stdexcept>
#include <errno.h>
#include <string.h>

using namespace std;

namespace LiquidRobotics {
namespace SMACFramework {
namespace TZTools {

time_t ComponentsToTime(tm input)
{
	time_t result = mktime(&input);
	const int errSave = errno;

	if (result == (time_t) -1)
	{
		stringstream msg;
		msg << "mktime() failed with error #" << errSave << ": " << GetErrorDescription(errSave);

		throw runtime_error(msg.str());
	}

	return result;
}

time_t ComponentsToTime(int year, int month, int day, int hour, int minute, int second, int dstflag)
{
	tm comp;
	memset(&comp, '\0', sizeof(comp));

	comp.tm_year = year-1900;
	comp.tm_mon = month-1;
	comp.tm_mday = day;
	comp.tm_hour = hour;
	comp.tm_min = minute;
	comp.tm_sec = second;
	comp.tm_isdst = dstflag;

	return ComponentsToTime(comp);
}

tm TimeToLocal(time_t input)
{
	tm result;

	if (!localtime_r(&input, &result))
	{
		const int errSave = errno;

		stringstream msg;
		msg << "localtime_r() failed with error #" << errSave << ": " << GetErrorDescription(errSave);

		throw runtime_error(msg.str());
	}

	return result;
}

tm TimeToUTC(time_t input)
{
	tm result;

	if (!gmtime_r(&input, &result))
	{
		const int errSave = errno;

		stringstream msg;
		msg << "gmtime_r() failed with error #" << errSave << ": " << GetErrorDescription(errSave);

		throw runtime_error(msg.str());
	}

	return result;
}

time_t UTCToLocal(int year, int month, int day, int hour, int minute, int second)
{
	const time_t gmt1 = ComponentsToTime(year, month, day, hour, minute, second);
	const tm utccomp1 = TimeToUTC(gmt1);
	const time_t gmt2 = ComponentsToTime(utccomp1);
	const time_t tzdelta = gmt1-gmt2;

	time_t localTimeResult = gmt1+tzdelta;
	const tm testcomp = TimeToUTC(localTimeResult);

	if ((testcomp.tm_hour != hour) || (testcomp.tm_min != minute))
	{
		tm testcomp2 = testcomp;
		testcomp2.tm_isdst = 1;

		const time_t resultingGMT = ComponentsToTime(testcomp2);
		const time_t tzdelta2 = tzdelta+gmt1-resultingGMT;

		localTimeResult = gmt1+tzdelta2;
	}

	return localTimeResult;
}

}
}
}
