//------------------------------------------------------------------------------
// <copyright file="AwaitPPS.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>
//   Implements a function to await PPS transition.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#include "AwaitPPS.h"
#include "LqrSMACGetErrorDescription.h"
#include "LqrSMACZeroObject.h"
#include <stdexcept>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
#include <sstream>
#include <unistd.h>

using namespace std;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace GPSListenerLib {

struct AutoCloseFD
{
	AutoCloseFD(int _fd) : fd(_fd) { }
	~AutoCloseFD() { if (fd != -1) close(fd); }
	const int fd;
};

SHARED_LIB_PUBLIC AwaitPPSStatus AwaitPPS(unsigned gpio, int timeoutMS, bool throwOnError)
{
	stringstream fname_s;
	fname_s << "/sys/class/gpio/gpio" << gpio << "/value";

	const string fname = fname_s.str();
	AutoCloseFD ac(open(fname.c_str(), O_RDONLY));

	if (ac.fd < 0)
	{
		if (throwOnError)
		{
			stringstream msg_s;
			msg_s << "Failed to open kernel resource " << fname << " in AwaitPPS: " << GetErrorDescription(errno);

			const string msg = msg_s.str();
			throw invalid_argument(msg);
		}

		return AwaitPPSStatus_BadArguments;
	}

	struct pollfd pfd;

	ZeroObject(pfd);
	pfd.fd = ac.fd;
	pfd.events = POLLPRI;

	char rdbuf[5];
	read(ac.fd, rdbuf, sizeof(rdbuf)-1);

	ZeroObject(rdbuf);
	lseek(ac.fd, 0, SEEK_SET);

	const int status = poll(&pfd, 1, timeoutMS);

	if (status < 0)
	{
		if (throwOnError)
		{
			stringstream msg_s;
			msg_s << "poll() operation failed in AwaitPPS: " << GetErrorDescription(errno);

			const string msg = msg_s.str();
			throw runtime_error(msg);
		}

		return AwaitPPSStatus_Error;
	}
	else if (!status)
	{
		if (throwOnError)
		{
			stringstream msg_s;
			msg_s << "Timeout (" << timeoutMS << " ms) in AwaitPPS: no PPS line transition detected";

			const string msg = msg_s.str();
			throw runtime_error(msg);
		}

		return AwaitPPSStatus_Timeout;
	}

	read(ac.fd, rdbuf, sizeof(rdbuf)-1);
	return AwaitPPSStatus_OK;
}

}
}
