//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>

#include "common.h"
#include "executor.h"
#include "dispatcher.h"
#include "utils.h"
#include "peer.h"
#include "alarm.h"

extern char tmp[MAXMAXLEN];

typedef struct {
    char        *file;
    char        *macro;
} type_alarm;


SingleList * _alarmList = NULL;

//////////////////////////////////////////////////////////////////////////////////
//
// Functions related to alarms
//
//////////////////////////////////////////////////////////////////////////////////

// mode: 0 - clear alarms
//       1 - if fired - process, then clear alarms
void manageAlarms(int mode)
{
    struct stat buf;
    
    SingleList* list = _alarmList;
    while (list) {

        type_alarm * v = (type_alarm *) list->data;

        // If file exists
        int fs = stat(v->file, &buf);
        if (fs == 0) {
            if (mode == ALARM_CLEAN) {
                logger(L_DBG,"[ML]: Remove file");
            } else {
                INFO2("[ML]: Alarm %s fired", v->macro);

                eMessage* em = (eMessage*) malloc(sizeof(eMessage));
                em->peer  = 0;
                em->type  = EM_ALARM;
                em->value = strdup(v->macro);

                sendToExecutor(em);
            }
            remove(v->file);
        }
        list = listSingleNext(list);
    }
}

SingleList * getAlarms() 
{
    return _alarmList;
}

void forgetAlarms()    // just set to NULL, no cleanup
{
    _alarmList = NULL;
}

void addAlarm(char *file, char *macro)
{
    sprintf(tmp,"addAlarm() >%s< >%s<\n", file,macro);
    logger(L_DBG,tmp);

    type_alarm * v = (type_alarm *) calloc(sizeof(type_alarm),1);
    v->file  = strdup(file);
    v->macro = strdup(macro);
    
    _alarmList = listSingleAppend(_alarmList, v);
}

void destroyAlarm(void* ptr) 
{
    type_alarm * v = (type_alarm *) ptr;
    
    //sprintf(tmp,"destroyAlarm() >%s<", v->file);
    //logger(L_DBG,tmp);
    
    free(v->file);
    free(v->macro);
    free(v);
}

void freeAlarms(SingleList* chain)
{
    listSingleFullFree((chain ? chain : _alarmList), destroyAlarm);
    if (!chain) {
        _alarmList = NULL;
    }
}

//////////////////////////////////////////////////////////////////////////////////
//
// Functions related to keepalive messages handling
//
//////////////////////////////////////////////////////////////////////////////////

static int _keepaliveTimeout     = 0;
static int _keepaliveFlag        = BOOL_YES;
static time_t _keepaliveSendTime;
static time_t _keepaliveDropTime;

static void checkKeepalive()
{
    DEBUG2("[ML]: checkKeepalive %d", _keepaliveFlag);
    
    if (!_keepaliveFlag) {
	
	    _keepaliveTimeout = 0;

	    DEBUG2("[ML]: no keepalive message, drop connection");

	    // drop connection
	    dMessage* dm = allocDMessage();
	    dm->type     = DM_EVENT;
        dm->subtype  = ID_EVENT_DISCONNECT;

	    sendToDispatcher(dm);
    }
   _keepaliveFlag = BOOL_NO; 
}

void keepaliveTest()
{
    //DEBUG2("[ML]: keepaliveTest %d", _keepaliveTimeout);
    if (_keepaliveTimeout > 0) {

        time_t now = time(NULL);   
        double st = difftime(now, _keepaliveSendTime);
        double dt = difftime(now, _keepaliveDropTime);
 
    	if (dt > _keepaliveTimeout * 2) { // about (_keepaliveTimeout * 2) seconds
    	    checkKeepalive();
            time(&_keepaliveDropTime);
    	}
	
    	if (st > _keepaliveTimeout) {

            DEBUG2("[ML]: send keepalive message");
	    
	        dMessage* dm = allocDMessage();
	        dm->value    = strdup("Get(ping);");
	        dm->size     = strlen(dm->value);
	        dm->type     = DM_GET;
            dm->subtype  = ID_GET_PING;
            
	        sendToDispatcher(dm);
	    
    	    time(&_keepaliveSendTime);
    	}
    }
}

void addKeepalive(const char* tmout)
{
    if (!isServerMode()) {
        logger(L_DBG,"[ML]: can not set keepalive timeout for non-Server mode");
        return;
    }
    
    _keepaliveFlag        = BOOL_YES;
    _keepaliveTimeout     = 0;
    
    if (tmout && atoi(tmout) > 0) {
    
        _keepaliveTimeout = atoi(tmout);
        DEBUG2("[ML]: keepalive timeout set to %d seconds", _keepaliveTimeout);
	
       time(&_keepaliveSendTime);
       time(&_keepaliveDropTime);
	
    } else {
        _keepaliveTimeout = -1;
        DEBUG2("[ML]: drop keepalive check");
    }
}

boolean_t useKeepalive()
{
    return (_keepaliveTimeout > 0 ? BOOL_YES : BOOL_NO);
}

void setKeepalive()
{
     DEBUG2("[ML]: got keepalive message");
    _keepaliveFlag = BOOL_YES;
}
