/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdio.h>
#include <errno.h>
#include <string.h>

#ifndef WIN32
#include <signal.h>
#include <sys/time.h>
#endif

#include <mia/core/watch.hh>
#include <mia/core/msgstream.hh>

NS_MIA_BEGIN

const int TIMERSPAN = 2000;

#ifndef WIN32

class CWatchUnix: public CWatch
{
public:
       CWatchUnix();
       ~CWatchUnix();

private:
       static void overlap_handler(int p_sig);
       double do_get_seconds() const;

       static uint64_t m_overlaps;
       struct sigaction m_old_action;
};

CWatch::CWatch()
{
}

CWatchUnix::CWatchUnix()
{
       itimerval value;
       itimerval oldvalue;
       value.it_interval.tv_sec = TIMERSPAN;
       value.it_interval.tv_usec = 0;
       value.it_value.tv_sec  = TIMERSPAN;
       value.it_value.tv_usec = 0;
       struct sigaction act;
       sigemptyset (&act.sa_mask);
       act.sa_flags = 0;
       act.sa_handler = CWatchUnix::overlap_handler;

       if (sigaction(SIGVTALRM, &act, &m_old_action) < 0 )
              cvwarn() << "Unable to catch  signal:" << strerror(errno) << "\n";

       if (setitimer(ITIMER_VIRTUAL, &value, &oldvalue))
              cvwarn() << "setitimer failed:" << strerror(errno) << "\n";
}

CWatchUnix::~CWatchUnix()
{
       sigaction(SIGVTALRM, &m_old_action, NULL);
}


const CWatch& CWatch::instance()
{
       static CWatchUnix me;
       return me;
}

double CWatch::get_seconds() const
{
       return do_get_seconds();
}

double CWatchUnix::do_get_seconds() const
{
       itimerval value;

       if (getitimer(ITIMER_VIRTUAL, &value))
              cvwarn() << "setitimer failed:" << strerror(errno) << "\n";

       double result = TIMERSPAN - value.it_value.tv_sec;
       double resultlow = value.it_value.tv_usec / 1e+6;
       return (result - resultlow) + TIMERSPAN * double(m_overlaps);
}

void CWatchUnix::overlap_handler(int p_sig)
{
       if (p_sig == SIGVTALRM) {
              m_overlaps++;
       }

       signal(SIGVTALRM, CWatchUnix::overlap_handler);
}

#else
CWatch::CWatch()
{
       cvwarn() << "CWatch::CWatch: fake implementation on WIN32\n";
}
double CWatch::get_seconds() const
{
       return 0.0;
}
void CWatch::overlap_handler(int p_sig)
{
}
#endif
uint64_t CWatchUnix::m_overlaps = 0;


NS_MIA_END
