
#include <stdio.h>
#include <stdlib.h>
#include <errno.h> 
#include <pthread.h>
//#include <errno.h>
#include <string.h>
#include <regex.h>
#include <fcntl.h>
#include <unistd.h>

#include "aqualink.h"
//#include "utils.h"
#include "sensors.h"


/*
----------
GPIO and thermal sensors are simply read value in file
----------
1Wire is similar to
/sys/bus/w1/devices/28-011564e2feff/w1_slave 
90 01 4b 46 7f ff 0c 10 33 : crc=33 YES
90 01 4b 46 7f ff 0c 10 33 t=25000
read the t= value.

regex would be something like .*t=([0-9|\.]*)
-----------
*/


/*
 read sensor value from ie /sys/class/thermal/thermal_zone0/temp

 return true if current reading is different to last value stored
 */

struct sensorthread {
  pthread_t thread_id;
  pthread_mutex_t thread_mutex;
  pthread_cond_t thread_cond;
  struct aqualinkdata *aq_data;
  struct timespec timeout;
};

struct sensorthread *_sthread = NULL;

void *sensors_worker( void *ptr );

void stop_sensors_thread() {
  LOG(AQUA_LOG, LOG_INFO, "Stopping sensor thread\n");

  pthread_cond_broadcast(&_sthread->thread_cond);
}

void start_sensors_thread(struct aqualinkdata *aq_data) {

  _sthread = calloc(1, sizeof(struct sensorthread));

  _sthread->aq_data = aq_data;
  _sthread->thread_id = 0;

  if( pthread_create( &_sthread->thread_id , NULL ,  sensors_worker, (void*)_sthread) < 0) {
    LOG(AQUA_LOG, LOG_ERR, "could not create sensors thread\n");
    free(_sthread);
    return;
  }

  if ( _sthread->thread_id != 0 ) {
    pthread_detach(_sthread->thread_id);
  }
}

void *sensors_worker( void *ptr )
{
  struct sensorthread *sthread;
  sthread = (struct sensorthread *) ptr;
  int retval = 0;

  LOG(AQUA_LOG, LOG_NOTICE, "Started sensor thread\n");

  pthread_mutex_lock(&sthread->thread_mutex);

  do {
    if (retval != 0 && retval != ETIMEDOUT) {
       LOG(AQUA_LOG, LOG_ERR, "Sensor thread pthread_cond_timedwait failed for error %d %s\n",retval,strerror(retval));
      break;
    }

    for (int i=0; i < sthread->aq_data->num_sensors; i++) {
      //LOG(AQUA_LOG, LOG_DEBUG, "Sensor thread reading %s\n",sthread->aq_data->sensors[i].label);
      if (read_sensor(&sthread->aq_data->sensors[i]) ) {
        sthread->aq_data->updated = true;
      }
    }

    clock_gettime(CLOCK_REALTIME, &sthread->timeout);
    sthread->timeout.tv_sec += _aqconfig_.sensor_poll_time;
    //sthread->started_at = time(0);
    //LOG(AQUA_LOG, LOG_DEBUG, "Sensor thread will sleep for %d seconds\n",_aqconfig_.sensor_poll_time);
  } while ((retval = pthread_cond_timedwait(&sthread->thread_cond, &sthread->thread_mutex, &sthread->timeout)) == ETIMEDOUT);

  pthread_mutex_unlock(&sthread->thread_mutex);

  LOG(AQUA_LOG, LOG_DEBUG, "End sensor thread\n");

  free(sthread);
  pthread_exit(0);
}

#define READ_BUFFER_SIZE 256

bool read_sensor(external_sensor *sensor) {
  int fp;
  float value = 0.0;
  char buffer[READ_BUFFER_SIZE];
  char *startptr = &buffer[0];
  char *endptr;

  fp = open(sensor->path, O_RDONLY, 0);
  if (fp < 0) {
    LOGSystemError(errno, AQUA_LOG, sensor->path);
    LOG(AQUA_LOG,LOG_ERR, "Reading sensor %s %s\n",sensor->label, sensor->path);
    return FALSE;
  }

  // Read the sensor
  if ( read(fp, buffer, READ_BUFFER_SIZE) <= 0 ) {
    LOG(AQUA_LOG,LOG_ERR, "Reading value from sensor %s %s\n",sensor->label, sensor->path);
    close(fp);
    return FALSE;
  }
  close(fp);

  // If regex pass that
  if (sensor->regex != NULL) {
    regex_t preg;
    regmatch_t pmatch[2];
    //const char *pattern = ".*t=([0-9|\\.]*)";
    //const char *pattern = ".*([0-9]+).*";
    int status;
     // Compile the regular expression
    status = regcomp(&preg, sensor->regex, REG_EXTENDED);
    if (status != 0) {
      LOG(AQUA_LOG,LOG_ERR, "Compiling sensor regex %s\n",sensor->regex);
      return 1;
    }

    // Run regex
    if ( (status = regexec(&preg, buffer, 2, pmatch, 0)) == 0) {
        startptr = buffer + pmatch[1].rm_so;
    } else if (status == REG_NOMATCH) {
        //LOG(AQUA_LOG,LOG_DEBUG, "No sensor regex match '%s' on line '%s'\n",sensor->regex,line_buffer);
    } else {
        LOG(AQUA_LOG,LOG_ERR, "regex match error %d using '%s' on line '%s'\n",status,sensor->regex,buffer);
    }
    // Free the compiled regular expression
    regfree(&preg);
  }

  // Convert value to float
  value = strtof(startptr, &endptr);
  if (endptr == buffer) {
    LOG(AQUA_LOG,LOG_ERR, "Reading sensor value from %s\n", sensor->path);
  }

  value = value * sensor->factor;

  LOG(AQUA_LOG,LOG_DEBUG, "Read sensor %s value=%.2f\n",sensor->label, value);

  if (sensor->value != value) {
    sensor->value = value;
    return TRUE;
  }

  return FALSE;
}



#ifdef DO_NOT_COMPILE
/* TRY not to use this unless have too, unbuffered is far quicker for sysfs */
bool read_sensor_buffered(external_sensor *sensor) {

  FILE *fp;
  float value;

  fp = fopen(sensor->path, "r");
  if (fp == NULL) {
    LOGSystemError(errno, AQUA_LOG, sensor->path);
    LOG(AQUA_LOG,LOG_ERR, "Reading sensor %s %s\n",sensor->label, sensor->path);
    return FALSE;
  }

  if (sensor->regex != NULL) {
    char line_buffer[256];
    regex_t preg;
    regmatch_t pmatch[2];
    int status;
     // Compile the regular expression
    status = regcomp(&preg, sensor->regex, REG_EXTENDED);
    if (status != 0) {

      LOG(AQUA_LOG,LOG_ERR, "Compiling sensor regex %s\n",sensor->regex);
      return 1;
    }

    while (fgets(line_buffer, sizeof(line_buffer), fp) != NULL) {
      if ( (status = regexec(&preg, line_buffer, 2, pmatch, 0)) == 0) {
        value = atof(line_buffer + pmatch[1].rm_so);
        break;
      } else if (status == REG_NOMATCH) {
        //LOG(AQUA_LOG,LOG_DEBUG, "No sensor regex match '%s' on line '%s'\n",sensor->regex,line_buffer);
      } else {
        LOG(AQUA_LOG,LOG_ERR, "regex match error %d using '%s' on line '%s'\n",status,sensor->regex,line_buffer);
      }
    }
    // Free the compiled regular expression
    regfree(&preg);
  } else {
    fscanf(fp, "%f", &value);
  }

  fclose(fp);

  value = value * sensor->factor;
  //printf("Converted value %f\n",value);
  LOG(AQUA_LOG,LOG_DEBUG, "Read sensor %s value=%.2f\n",sensor->label, value);

  if (sensor->value != value) {
    sensor->value = value;
    return TRUE;
  }

  return FALSE;
}
#endif

