/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2024 Martin Preuss, all rights reserved.
 *
 * The license for this file can be found in the file COPYING which you
 * should have received along with this file.
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif


#include "./devicesread.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"

#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/stringlist.h>



/* ------------------------------------------------------------------------------------------------
 * defines
 * ------------------------------------------------------------------------------------------------
 */




/* ------------------------------------------------------------------------------------------------
 * forward declarations
 * ------------------------------------------------------------------------------------------------
 */

static int _readDeviceFileToList(const char *sFilename, AQHNODE_DEVICE_LIST *deviceList);
static AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHNODE_DEVICE_LIST *deviceList);
static AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
static AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode);
static AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
static int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
static int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
static int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);



/* ------------------------------------------------------------------------------------------------
 * implementations
 * ------------------------------------------------------------------------------------------------
 */

AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDeviceFile(const char *sFilename)
{
  int rv;

  rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE);
  if (rv<0) {
    DBG_ERROR(NULL, "File \"%s\" does not exists, writing later", sFilename);
    return NULL;
  }
  else {
    AQHNODE_DEVICE_LIST *deviceList;

    deviceList=AQHNODE_Device_List_new();
    rv=_readDeviceFileToList(sFilename, deviceList);
    if (rv<0) {
      DBG_ERROR(NULL, "File \"%s\" not found", sFilename);
      AQHNODE_Device_List_free(deviceList);
      return NULL;
    }

    if (AQHNODE_Device_List_GetCount(deviceList)<1) {
      AQHNODE_Device_List_free(deviceList);
      return NULL;
    }
    return deviceList;
  }
}



AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDataDeviceFiles()
{
  GWEN_STRINGLIST *sl;

  sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/nodes", "*.xml");
  if (sl) {
    AQHNODE_DEVICE_LIST *deviceList;

    deviceList=_readDeviceFiles(sl);
    GWEN_StringList_free(sl);
    if (deviceList==NULL) {
      DBG_INFO(NULL, "Error reading data device files");
      return NULL;
    }
    return deviceList;
  }
  else {
    DBG_ERROR(NULL, "No data device files");
    return NULL;
  }
}



AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl)
{
  GWEN_STRINGLISTENTRY *se;
  AQHNODE_DEVICE_LIST *deviceList;

  deviceList=AQHNODE_Device_List_new();
  se=GWEN_StringList_FirstEntry(sl);
  while(se) {
    const char *s;

    s=GWEN_StringListEntry_Data(se);
    if (s && *s) {
      int rv;

      DBG_INFO(NULL, "Reading device file \"%s\"", s);
      rv=_readDeviceFileToList(s, deviceList);
      if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
        DBG_WARN(NULL, "Error reading device file \"%s\" (%d), ignoring", s, rv);
      }
    }
    se=GWEN_StringListEntry_Next(se);
  }

  if (AQHNODE_Device_List_GetCount(deviceList)<1) {
    AQHNODE_Device_List_free(deviceList);
    return NULL;
  }

  return deviceList;
}



int _readDeviceFileToList(const char *sFilename, AQHNODE_DEVICE_LIST *deviceList)
{
  GWEN_XMLNODE *rootNode;
  GWEN_XMLNODE *deviceListNode;
  int rv;

  rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL);
  rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv);
    GWEN_XMLNode_free(rootNode);
    return rv;
  }

  deviceListNode=GWEN_XMLNode_FindFirstTag(rootNode, "devices", NULL, NULL);
  if (deviceListNode==NULL)
    deviceListNode=rootNode;

  rv=_readXmlDevices(deviceListNode, deviceList);
  if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error reading devices from file \"%s\" (%d)", sFilename, rv);
    GWEN_XMLNode_free(rootNode);
    return rv;
  }

  GWEN_XMLNode_free(rootNode);
  return 0;
}



int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHNODE_DEVICE_LIST *deviceList)
{
  GWEN_XMLNODE *deviceNode;

  deviceNode=GWEN_XMLNode_FindFirstTag(deviceListNode, "device", NULL, NULL);
  if (deviceNode) {
    while(deviceNode) {
      const char *driverName;

      driverName=GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL);
      if (driverName && *driverName && strcasecmp(driverName, "nodes")==0) {
        AQHNODE_DEVICE *device;

        device=_readXmlDevice(deviceNode);
        if (device==NULL) {
          DBG_INFO(NULL, "Error reading device from XML");
          return GWEN_ERROR_BAD_DATA;
        }
        else {
          const char *sDeviceName;

          sDeviceName=AQHNODE_Device_GetName(device);
          if (sDeviceName && *sDeviceName) {
            DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
            AQHNODE_Device_List_Add(device, deviceList);
          }
          else {
            DBG_ERROR(NULL, "Device with no name, not adding");
            AQHNODE_Device_free(device);
          }
        }
      }
      else {
        DBG_INFO(NULL, "Device is not an NODES device, ignoring");
      }
      deviceNode=GWEN_XMLNode_FindNextTag(deviceNode, "device", NULL, NULL);
    } /* while */
    return 0;
  }
  else {
    DBG_INFO(NULL, "No <device> element found");
    return GWEN_ERROR_NO_DATA;
  }
}



AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
{
  AQHNODE_DEVICE *device;
  GWEN_XMLNODE *valuesNode;
  int rv;

  device=AQHNODE_Device_new();
  AQHNODE_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
  AQHNODE_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));
  rv=_readManufacturer(device, deviceNode);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    AQHNODE_Device_free(device);
    return NULL;
  }
  rv=_readDeviceType(device, deviceNode);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    AQHNODE_Device_free(device);
    return NULL;
  }
  rv=_readDeviceVersion(device, deviceNode);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    AQHNODE_Device_free(device);
    return NULL;
  }

  /* read values */
  valuesNode=GWEN_XMLNode_FindFirstTag(deviceNode, "values", NULL, NULL);
  if (valuesNode) {
    AQHNODE_VALUE_LIST *valueList;

    valueList=_readXmlValueList(valuesNode);
    if (valueList)
      AQHNODE_Device_SetValueList(device, valueList);
  }

  return device;
}



AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode)
{
  AQHNODE_VALUE_LIST *valueList;
  GWEN_XMLNODE *node;

  valueList=AQHNODE_Value_List_new();
  node=GWEN_XMLNode_FindFirstTag(valuesNode, "value", NULL, NULL);
  while(node) {
    AQHNODE_VALUE *value;

    value=_readXmlValue(node);
    if (value)
      AQHNODE_Value_List_Add(value, valueList);
    else {
      DBG_INFO(NULL, "Error reading <value> element");
      AQHNODE_Value_List_free(valueList);
      return NULL;
    }
    node=GWEN_XMLNode_FindNextTag(node, "value", NULL, NULL);
  }
  if (AQHNODE_Value_List_GetCount(valueList)<1) {
    DBG_INFO(NULL, "No <value> element in <values> element");
    AQHNODE_Value_List_free(valueList);
    return NULL;
  }
  return valueList;
}



AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
{
  AQHNODE_VALUE *value;

  value=AQHNODE_Value_new();
  AQHNODE_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
  AQHNODE_Value_SetId(value, GWEN_XMLNode_GetIntProperty(valueNode, "id", 0));
  AQHNODE_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
  AQHNODE_Value_SetValueType(value, AQH_ValueType_fromString(GWEN_XMLNode_GetProperty(valueNode, "type", NULL)));
  AQHNODE_Value_SetDataType(value, AQH_ValueDataType_fromString(GWEN_XMLNode_GetProperty(valueNode, "dataType", NULL)));
  AQHNODE_Value_SetModality(value, AQH_ValueModality_fromString(GWEN_XMLNode_GetProperty(valueNode, "modality", NULL)));
  AQHNODE_Value_SetDenom(value, GWEN_XMLNode_GetIntProperty(valueNode, "denom", 1));

  return value;
}



int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
  const char *s;
  uint32_t v;

  s=GWEN_XMLNode_GetCharValue(deviceNode, "manufacturer", NULL);
  if (!(s && *s)) {
    DBG_ERROR(NULL, "Missing manufacturer");
    return GWEN_ERROR_BAD_DATA;
  }

  if (strlen(s)>4) {
    DBG_ERROR(NULL, "Bad manufacturer string (too long): \"%s\"", s);
    return GWEN_ERROR_BAD_DATA;
  }

  v=0;
  while(*s) {
    v<<=8;
    v|=*s & 0xff;
    s++;
  }
  v=((v>>24) & 0x000000ffL) |
    ((v>>8)  & 0x0000ff00L) |
    ((v<<8)  & 0x00ff0000L) |
    ((v<<24) & 0xff000000L);

  AQHNODE_Device_SetManufacturer(device, v);
  return 0;
}



int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
  const char *s;
  uint16_t v;

  s=GWEN_XMLNode_GetCharValue(deviceNode, "devicetype", NULL);
  if (!(s && *s)) {
    DBG_ERROR(NULL, "Missing device type");
    return GWEN_ERROR_BAD_DATA;
  }

  if (strlen(s)>2) {
    DBG_ERROR(NULL, "Bad devicetype string (too long): \"%s\"", s);
    return GWEN_ERROR_BAD_DATA;
  }

  v=0;
  while(*s) {
    v<<=8;
    v|=*s & 0xff;
    s++;
  }

  AQHNODE_Device_SetDeviceType(device, v);
  return 0;
}



int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
  int v;

  v=GWEN_XMLNode_GetIntValue(deviceNode, "deviceversion", -1);
  if (v<0) {
    DBG_ERROR(NULL, "Missing device version");
    return GWEN_ERROR_BAD_DATA;
  }

  AQHNODE_Device_SetDeviceVersion(device, v<<8);
  return 0;
}




