/*
 * Copyright 2009 Funambol, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

#include "common/android/Timer.h"
#include "Logger/LoggerMacroses.h"
#include <time.h>
#include <pthread.h>


namespace NS_DM_Client
{
    namespace NS_Common
    {
        void * ThreadBody(void *);
    }
}

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Common;

const char* const c_LogName = "Timer";

Timer::Timer() : Event(false), 
    m_isRunning(false), m_shouldRun(true), m_ticksCount(0), m_ticksToCount(0), m_timerInterval(0), 
    m_callback(NULL), m_thread(0)
{
}


Timer::~Timer()
{
	Stop();
}


bool Timer::IsRunning()
{
	return m_isRunning;
}


void Timer::SetCallback(Callback *c)
{
	GDLDEBUG("Callback %p", c);
	m_callback = c;
}


void Timer::SetInterval(uint t)
{
	m_timerInterval = t;
}


void Timer::SetTicksCount(int count)
{
	m_ticksCount = count;
}


void Timer::Start()
{
	GDLDEBUG("ENTER >> Is running = %d", m_isRunning);
	if (!m_isRunning)
	{
		if (m_timerInterval)
		{
			if (m_ticksCount == -1 || m_ticksCount > 0)
				startTimer();
		}
	}
	GDLDEBUG("LEAVE <<");
}


void Timer::Stop()
{
	GDLDEBUG("ENTER >> Is running = %d", m_isRunning);
	if (m_isRunning)
	{
		pthread_mutex_lock(&m_mutex);
		
		m_shouldRun = false;
		pthread_cond_signal(&m_condition);
		pthread_mutex_unlock(&m_mutex);
		pthread_join(m_thread, NULL);
	}
	GDLDEBUG("LEAVE <<");
}


void Timer::notifyCallback()
{
	GDLDEBUG("ENTER >> Callback %p", m_callback);
	if (m_callback) m_callback->Call();
	GDLDEBUG("LEAVE <<");
}


void Timer::startTimer()
{
	GDLDEBUG("ENTER >> ");
	m_ticksToCount = m_ticksCount;
	
	// tick once before starting the timer
	if (m_ticksToCount)
		tick();

	if (m_ticksToCount)
	{
		m_shouldRun = true;
		int res = 0;
		if (0 != (res = pthread_create(&m_thread, NULL,  ThreadBody, (void*) this)))
			GDLERROR("failed to start timer [%d] !", res);
	}

	GDLDEBUG("LEAVE << ");
}


void Timer::tick()
{
	GDLDEBUG("ENTER >>");
	if (m_ticksToCount == 0)
	{
		Stop();
	}
	else
	{
		if (m_ticksToCount > 0)
			m_ticksToCount--;
		notifyCallback();				
	}
	GDLDEBUG("LEAVE <<");
}


void * NS_DM_Client::NS_Common::ThreadBody(void *p)
{
	GDLDEBUG("ENTER >>");
	if (p)
	{
		Timer &timer = *static_cast<Timer*>(p);
		timer.m_isRunning = true;
		
		bool run = true;
		while (run)
		{
			timer.tick();

			pthread_mutex_lock(&timer.m_mutex);
			
			if (timer.m_shouldRun)
			{
				struct timespec tt;
				clock_gettime(CLOCK_REALTIME, &tt);
				tt.tv_sec += timer.m_timerInterval;
				if (ETIMEDOUT != pthread_cond_timedwait(&timer.m_condition, &timer.m_mutex, &tt))
					run = timer.m_shouldRun;
			}
			else
				run = false;

			pthread_mutex_unlock(&timer.m_mutex);
		}
		timer.m_isRunning = false;
	}
	pthread_exit(NULL);
	GDLDEBUG("LEAVE <<");
}
