/////////////////////////////////////////////////////////////////////////////
// File:        timecomponents.cpp
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Copyright:   (C) 2011 Cesar Mauri Loba - CREA Software Systems
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#ifndef TIMECOMPONENTS_H
	#define TIMECOMPONENTS_H
#else
	#error This file is intended to be included only once
#endif

#include "spcore/basictypes.h"
#include "spcore/pinimpl.h"
#include "spcore/component.h"
//#include <vector>
//#include <string>
//#include <stdio.h>
//#include <boost/thread/mutex.hpp>
//#include <boost/thread/condition.hpp>

#if defined(WIN32)
	#include <windows.h>
#else
	#include <time.h>
#endif

using namespace std;

namespace spcore {

/**
	chrono component - Chronograph
		
		Provides elapsed time in ms since reset.
		
	Input pins:
		reset (CTypeAny)- Any received message resets the chronograph
		read (CTypeAny)	- Any received message sends current elapsed time to output

	Output pins:
		elapsed (CTypeInt)

	Command line
*/
class Chrono : public CComponentAdapter {
public:
	static const char* getTypeName() { return "chrono"; }
	virtual const char* GetTypeName() const { return Chrono::getTypeName(); }
	Chrono(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	{
		m_opinElapsed= CTypeInt::CreateOutputPin("elapsed");
		if (RegisterOutputPin(*m_opinElapsed)!= 0)
			throw std::runtime_error("error registering output pin");

  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinInReset("reset", *this), false))!= 0)
			throw std::runtime_error("error creating input pin reset");


  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinInRead("read", *this), false))!= 0)
			throw std::runtime_error("error creating input pin read");

		m_elapsed= CTypeInt::CreateInstance();
	}

	int OnReset () {

#if defined(WIN32)
		m_lastTickCount= GetTickCount();
#else
		clock_gettime(CLOCK_MONOTONIC, &m_lastTStamp);
#endif
		return 0;
	}

	int OnRead () {

#if defined(WIN32)
		DWORD now= GetTickCount();
		if (now - m_lastTickCount> MAX_CHRONO_MS)
			m_elapsed->setValue (MAX_CHRONO_MS);
		else
			m_elapsed->setValue((int)(now - m_lastTickCount));
#else
		struct timespec now;
		clock_gettime(CLOCK_MONOTONIC, &now);

		if (now.tv_sec - m_lastTStamp.tv_sec>= MAX_CHRONO_MS / 1000)
			m_elapsed->setValue (MAX_CHRONO_MS);
		else {
			m_elapsed->setValue ((int) (now.tv_sec - m_lastTStamp.tv_sec) * 1000 + 
				(now.tv_nsec - m_lastTStamp.tv_nsec) / 1000000);
		}
#endif
		return m_opinElapsed->Send(m_elapsed);
	}

	virtual int	DoInitialize () {
		OnReset(); 
		return 0;
	}

private:
	virtual ~Chrono() {}

	// Max. measured ms
	enum { MAX_CHRONO_MS= 2147483646 };

	class InputPinInReset : public CInputPinWriteOnly<CTypeAny, Chrono> {
	public:
		InputPinInReset (const char * name, Chrono & component)
		: CInputPinWriteOnly<CTypeAny, Chrono>(name, component) { }

		virtual int DoSend(const CTypeAny & ) {	return this->m_component->OnReset(); }
	};

	class InputPinInRead : public CInputPinWriteOnly<CTypeAny, Chrono> {
	public:
		InputPinInRead (const char * name, Chrono & component)
		: CInputPinWriteOnly<CTypeAny, Chrono>(name, component) { }

		virtual int DoSend(const CTypeAny & ) {	return this->m_component->OnRead(); }
	};

	//
	// Data members
	//
	SmartPtr<IOutputPin> m_opinElapsed;
	SmartPtr<CTypeInt> m_elapsed;

#if defined(WIN32)
	DWORD m_lastTickCount;
#else
	struct timespec m_lastTStamp;
#endif
};

typedef ComponentFactory<Chrono> ChronoFactory;

} // namespace spcore
