/*
 * This file is part of the weatherdaemon project.
 * Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
 *
 * 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/>.
 */

// Some common functions and handlers for sensors

#include <pthread.h>

#include "weathlib.h"

// private functions (for plugins usage only)
//static int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*));
//static void common_kill(sensordata_t *);
//static int common_getval(sensordata_t*, val_t*, int);
//static int common_init(sensordata_t*, int, time_t, int);

/**
 * @brief sensor_new - call this function before calling `sensor_init`
 * @param N - plugin number in array
 * @return pointer to allocated sensor's structure
 */
sensordata_t *sensor_new(int N, const char *descr){
    sensordata_t *s = MALLOC(sensordata_t, 1);
    s->fdes = -1; // not inited
    s->onrefresh = common_onrefresh; // `init` function can redefine basic stubs
    s->get_value = common_getval;
    s->kill = common_kill;
    s->PluginNo = N; // `init` shouldn't change this value
    if(descr && *descr) snprintf(s->path, PATH_MAX, "%s", descr); // `init` shouldn't change this value
    else s->path[0] = 0; // no path
    pthread_mutex_init(&s->valmutex, NULL);
    return s;
}

/**
 * @brief sensor_alive - test if sensor's thread isn't dead
 * @param s - sensor
 * @return FALSE if thread is dead
 */
int sensor_alive(sensordata_t *s){
    if(!s || s->fdes < 0){
        LOGMSG("No sensor or fdes < 0");
        return FALSE;
    }
    if(pthread_kill(s->thread, 0)){
        if(s->path[0]) LOGMSG("Sensor's '%s @ %s' main thread is dead", s->name, s->path);
        else LOGMSG("Sensor's '%s' main thread is dead", s->name);
        return FALSE;
    }
    return TRUE;
}

/**
 * @brief common_onrefresh - common `change onrefresh handler`
 * @param s - sensor
 * @return FALSE if failed
 */
int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *)){
    FNAME();
    if(!s || !handler) return FALSE;
    s->freshdatahandler = handler;
    return TRUE;
}

/**
 * @brief common_kill - common `die` function (close, but don't destroy sensor)
 * @param s - sensor
 */
void common_kill(sensordata_t *s){
    FNAME();
    if(!s) return;
    if(s->fdes > -1){ // inited and maybe have opened file/socket
        close(s->fdes);
        s->fdes = -1;
        DBG("FD closed");
        usleep(5000);
        if(pthread_equal(pthread_self(), s->thread)){
            DBG("Don't cancel myself");
        }else{
            DBG("Cancel sensor's thread");
            if(0 == pthread_cancel(s->thread)){
                DBG("%s main thread canceled, join", s->name);
                pthread_join(s->thread, NULL);
                DBG("Done");
            }
        }
    }
    DBG("Delete RB");
    if(s->ringbuffer) sl_RB_delete(&s->ringbuffer);
    FREE(s->values);
    s->Nvalues = 0;
    if(s->privdatafree) s->privdatafree(s->privdata);
    else FREE(s->privdata);
    DBG("Sensor '%s' killed", s->name);
    if(s->path[0]) LOGERR("Sensor '%s' @ '%s' killed", s->name, s->path);
    else LOGERR("Sensor '%s' killed", s->name);
}

/**
 * @brief common_getval - common value getter
 * @param s (i) - station
 * @param o (o) - value or NULL (if you just wants test N)
 * @param N - number of sensor
 * @return FALSE if failed
 */
int common_getval(struct sensordata_t *s, val_t *o, int N){
    if(!s || N < 0 || N >= s->Nvalues) return FALSE;
    if(o){
        pthread_mutex_lock(&s->valmutex);
        *o = s->values[N];
        pthread_mutex_unlock(&s->valmutex);
    }
    return TRUE;
}
