/*
 * Copyright (C) 2005 Jonathan Hudson <jh+w1retap@daria.co.uk>
 *
 * 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 2
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/file.h>
#include "w1retap.h"
#include <sql.h>
#include <sqlext.h>

void w1_init(w1_devlist_t *w1, char *params) {
  SQLHENV env;
  SQLHDBC dbc;
  SQLHSTMT stmt;
  SQLRETURN ret;
  SQLSMALLINT columns;
  const char *sql = "select * from w1sensors order by device";
  SQLLEN rows = 0;
  w1_device_t *devs = NULL;

  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
  SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
  SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
  ret = SQLDriverConnect(dbc, NULL, (unsigned char *)params, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);

  if (SQL_SUCCEEDED(ret)) {
    char cnam[256];
    SQLSMALLINT lcnam;
    int i;
    char **flds, **f;
    int id = -1, it = -1;
    int nx = 0, ni = 0, nn = 0;
    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);

    SQLNumResultCols(stmt, &columns);
    SQLRowCount(stmt, &rows);
    devs = malloc(sizeof(w1_device_t) * rows);
    memset(devs, 0, sizeof(w1_device_t) * rows);
    flds = malloc((columns * sizeof(char *)));

    for (f = flds, i = 0; i < columns; i++, f++) {
      SQLDescribeCol(stmt, i + 1, (SQLCHAR *)cnam, sizeof(cnam), &lcnam, NULL, NULL, NULL, NULL);
      *f = strdup(cnam);
      if (strcmp(cnam, "device") == 0) {
        id = i;
      } else if (strcmp(cnam, "type") == 0) {
        it = i;
      }
    }

    while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) {
      int i;
      char device[32] = {0};
      char type[32] = {0};
      SQLLEN indicator;

      SQLGetData(stmt, id + 1, SQL_C_CHAR, device, sizeof(device), &indicator);
      SQLGetData(stmt, it + 1, SQL_C_CHAR, type, sizeof(type), &indicator);

      nn = w1_get_device_index(devs, ni, device, type);
      if (nn == -1) {
        nx = ni;
        ni++;
      } else {
        nx = nn;
      }

      for (i = 0; i < columns; i++) {
        char buf[512];

        ret = SQLGetData(stmt, i + 1, SQL_C_CHAR, buf, sizeof(buf), &indicator);
        if (SQL_SUCCEEDED(ret)) {
          char *sv;
          if (indicator == SQL_NULL_DATA) {
            sv = NULL;
          } else {
            sv = strdup(buf);
          }
          if (sv)
            w1_set_device_data(devs + nx, flds[i], sv);
        }
      }
      w1_enumdevs(devs + nx);
    }
    w1->numdev = ni;
    w1->devs = devs;
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);

    for (f = flds, i = 0; i < columns; i++, f++) {
      free(*f);
    }
    free(flds);

    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    ret = SQLExecDirect(stmt, (unsigned char *)"select name,value,rmin,rmax from ratelimit", SQL_NTS);
    SQLNumResultCols(stmt, &columns);
    SQLRowCount(stmt, &rows);
    while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) {
      SQLLEN indicator;
      char buf[512];
      char *s;
      short flags = 0;
      float roc = 0, rmin = 0, rmax = 0;

      ret = SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &indicator);
      if (SQL_SUCCEEDED(ret)) {
        if (indicator == SQL_NULL_DATA) {
          s = NULL;
        } else {
          s = strdup(buf);
          ret = SQLGetData(stmt, 2, SQL_C_CHAR, buf, sizeof(buf), &indicator);
          if (SQL_SUCCEEDED(ret)) {
            if (indicator != SQL_NULL_DATA) {
              roc = strtof(buf, NULL);
              flags |= W1_ROC;
            }
          }
          ret = SQLGetData(stmt, 3, SQL_C_CHAR, buf, sizeof(buf), &indicator);
          if (SQL_SUCCEEDED(ret)) {
            if (indicator != SQL_NULL_DATA) {
              rmin = strtof(buf, NULL);
              flags |= W1_RMIN;
            }
          }
          ret = SQLGetData(stmt, 4, SQL_C_CHAR, buf, sizeof(buf), &indicator);
          if (SQL_SUCCEEDED(ret)) {
            if (indicator != SQL_NULL_DATA) {
              rmax = strtof(buf, NULL);
              flags |= W1_RMAX;
            }
          }
          if (flags) {
            w1_sensor_t *sensor;
            if (NULL != (sensor = w1_find_sensor(w1, (const char *)s))) {
              sensor->flags = flags;
              if (flags & W1_ROC)
                sensor->roc = roc;
              if (flags & W1_RMIN)
                sensor->rmin = rmin;
              if (flags & W1_RMAX)
                sensor->rmax = rmax;
            }
          }
          free(s);
        }
      }
    }
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  }
  SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  SQLFreeHandle(SQL_HANDLE_ENV, env);
}

static SQLHENV env;
static SQLHDBC dbc;
static SQLHSTMT stmt;

void w1_cleanup(void) {
  SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  SQLFreeHandle(SQL_HANDLE_ENV, env);
  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  dbc = env = stmt = NULL;
}

void w1_logger(w1_devlist_t *w1, char *params) {
  w1_device_t *devs = NULL;
  int i = 0;

  if (env == NULL) {
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
    SQLDriverConnect(dbc, NULL, (unsigned char *)params, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    SQLPrepare(stmt, (unsigned char *)"insert into readings(date,name,value)  values (?,?,?)", SQL_NTS);
  }

  for (devs = w1->devs, i = 0; i < w1->numdev; i++, devs++) {
    if (devs->init) {
      int j;
      for (j = 0; j < devs->ns; j++) {
        if (devs->s[j].valid) {
          SQLLEN psz;
          SQL_TIMESTAMP_STRUCT sqlts;
          SQLSMALLINT ValueType;
          SQLSMALLINT ParameterType;
          SQLPOINTER ParameterValuePtr;

          if (w1->timestamp) {
            struct tm *tm = localtime(&w1->logtime);
            sqlts.year = tm->tm_year + 1900;
            sqlts.month = tm->tm_mon + 1;
            sqlts.day = tm->tm_mday;
            sqlts.hour = tm->tm_hour;
            sqlts.minute = tm->tm_min;
            sqlts.second = tm->tm_sec;
            sqlts.fraction = 0;
            ValueType = SQL_C_TIMESTAMP;
            ParameterType = SQL_TIMESTAMP;
            ParameterValuePtr = &sqlts;
            psz = sizeof(sqlts);
          } else {
            ValueType = SQL_C_LONG;
            ParameterType = SQL_INTEGER;
            ParameterValuePtr = &w1->logtime;
            psz = sizeof(&w1->logtime);
          }

          SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, ValueType, ParameterType, 0, 0, ParameterValuePtr, psz, &psz);
          psz = strlen(devs->s[j].abbrv);
          SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, devs->s[j].abbrv, psz, &psz);
          psz = sizeof(float);
          SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 0, 0, &devs->s[j].value, psz, &psz);
          SQLExecute(stmt);
        }
      }
    }
  }
}

#if defined(TESTBIN)
int main() {
  w1_devlist_t w = {0}, *w1;
  int n;

  w1 = &w;
  w1_init(w1, "DSN=w1retap");

  for (n = 0; n < w1->numdev; n++) {
    fprintf(stderr, "%s %s\n", w1->devs[n].serial, w1->devs[n].devtype);
    fprintf(stderr, "\t0: %s %s\n", w1->devs[n].s[0].abbrv, w1->devs[n].s[0].name);
    fprintf(stderr, "\t1: %s %s\n", w1->devs[n].s[1].abbrv, w1->devs[n].s[1].name);
  }

  w1->logtime = time(0);
  w1->devs[0].init = 1;
  w1->devs[0].s[0].valid = 1;
  w1->devs[0].s[0].value = 22.22;

  w1->devs[1].init = 1;
  w1->devs[1].s[0].valid = 1;
  w1->devs[1].s[0].value = 69;
  w1->devs[1].s[1].valid = 99.0;
  w1->devs[1].s[1].value = 18.88;

  w1->devs[2].init = 1;
  w1->devs[2].s[0].valid = 1;
  w1->devs[2].s[0].value = 1001.45;
  w1_logger(w1, "DSN=w1retap");

  sleep(5);

  w1->logtime = time(0);
  w1->devs[0].init = 1;
  w1->devs[0].s[0].valid = 1;
  w1->devs[0].s[0].value = 25.77;

  w1->devs[1].init = 1;
  w1->devs[1].s[0].valid = 1;
  w1->devs[1].s[0].value = 66;
  w1->devs[1].s[1].valid = 1;
  w1->devs[1].s[1].value = 12.565;

  w1->devs[2].init = 0;
  w1_logger(w1, "DSN=w1retap");
  return 0;
}
#endif
