#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdarg.h>
#include <assert.h>

#define W1_ROC (1 << 0)
#define W1_RMIN (1 << 1)
#define W1_RMAX (1 << 2)

typedef struct {
  char *abbrv;
  char *name;
  char *units;
  double value;
  short valid;
  short flags;
  double rmin;
  double rmax;
  double roc;
  double lval;
  time_t ltime;
  short reason;
} w1_sensor_t;

typedef struct w1_devlist {
  time_t logtime;
  short allow_escape;
} w1_devlist_t;

static void w1_replog(w1_devlist_t *w1, const char *fmt, ...) {
  va_list va;
  char *p;
  int nc;

  va_start(va, fmt);
  if ((nc = vasprintf(&p, fmt, va)) != -1) {
    fputs(">>>>> ", stdout);
    fputs(p, stdout);
    fputc('\n', stdout);
    free(p);
  }
}

static int w1_validate(w1_devlist_t *w1, w1_sensor_t *s) {
  static char *restxt[] = {"OK", "RATE", "MIN", "undef", "MAX"};

  int chk = 0;
  float act = s->value;
  float rate = 0;

  s->valid = 1;

  if ((s->flags & W1_RMIN) && (s->value < s->rmin)) {
    s->value = (s->ltime) ? s->lval : s->rmin;
    chk = W1_RMIN;
  }
  if ((s->flags & W1_RMAX) && (s->value > s->rmax)) {
    s->value = (s->ltime) ? s->lval : s->rmax;
    chk = W1_RMAX;
  }

  if (chk == 0 && (s->flags & W1_ROC)) {
    if (!(w1->allow_escape == 1 && (s->reason & (W1_RMIN | W1_RMAX)) != 0)) {
      if (s->ltime > 0 && s->ltime != w1->logtime) {
        rate = fabs(s->value - s->lval) * 60.0 / (w1->logtime - s->ltime);
        if (rate > s->roc) {
          s->value = s->lval;
          chk = W1_ROC;
        }
      }
    }
  }

  s->reason = chk;
  if (chk == 0) {
    s->ltime = w1->logtime;
    s->lval = s->value;
  } else {
    w1_replog(w1,
              "%s result=%.2f actual=%.2f rate=%.2f prev=%.2f "
              "prevtime_t=%d logtime_t=%d reason=%s",
              s->abbrv, s->value, act, rate, s->lval, s->ltime, w1->logtime, restxt[chk]);
  }
  return (int)s->valid;
}

#define STARTSECS 1256243520

int main(void) {
  double d;
  int n;
  w1_devlist_t *w1, xw1 = {.logtime = 0, .allow_escape = 0};

  // Minimum is 2
  // Maximum is 100.04
  // Rate is 7 / min

  w1_sensor_t *s, sns = {.abbrv = "OHUM",
                         .name = "Humidity",
                         .units = "pct",
                         .value = -1,
                         .flags = (W1_ROC | W1_RMIN | W1_RMAX),
                         .rmin = 2,
                         .rmax = 100.04,
                         .roc = 7,
                         .lval = 0,
                         .ltime = 0};

  s = &sns;
  w1 = &xw1;

  puts("Start value 0 => min (2)");
  w1->logtime = STARTSECS;
  puts("Start t0");
  d = s->value = 0;
  n = w1_validate(w1, s);
  assert(s->value == 2.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 4, OK");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 4;
  n = w1_validate(w1, s);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 1, use last good value (4)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 1;
  n = w1_validate(w1, s);
  assert(s->value == 4.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 0, use last good value (4)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 0;
  n = w1_validate(w1, s);
  assert(s->value == 4.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 5, use  value (5)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 5;
  n = w1_validate(w1, s);
  assert(s->value == 5.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 50, (rate violation) use last good value (5)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 50;
  n = w1_validate(w1, s);
  assert(s->value == 5.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 98, OK (much later)");
  w1->logtime += 120000;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 98;
  n = w1_validate(w1, s);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 100, OK");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 100;
  n = w1_validate(w1, s);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 100.5, use last good (100.0)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 100.5;
  n = w1_validate(w1, s);
  assert(s->value == 100.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  puts("Value = 120, use last good (100.0)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 120.0;
  n = w1_validate(w1, s);
  assert(s->value == 100.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  w1->allow_escape = 1;
  puts("Value = 30, use value (even though rate violation)");
  w1->logtime += 120;
  printf("t0 + %d\n", (int)(w1->logtime - STARTSECS));
  d = s->value = 30.0;
  n = w1_validate(w1, s);
  assert(s->value == 30.0);
  printf("** %d read %.2f stored %.2f\n\n", n, d, s->value);

  return 0;
}
