/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2025 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 "./xmlread.h"
#include "./server_p.h"
#include "aqhome-mqttlog/types/topic.h"
#include "aqhome-mqttlog/types/value.h"
#include "aqhome-mqttlog/types/translation.h"

#include <aqhome/api.h>
#include <aqhome/aqhome.h>

#include <gwenhywfar/endpoint_multilayer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/xml.h>
#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>




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




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

static AQHMQTT_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
static int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList);
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList);
static AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
static AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode);
static AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode);
static AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
static AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode);




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

AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename)
{
  DBG_ERROR(NULL, "Reading device file \"%s\"", sFilename);
  if (o) {
    AQH_MQTTLOG_SERVER *xo;

    xo=AQH_MqttLogServer_GetServerData(o);
    if (xo) {
      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 {
        AQHMQTT_DEVICE_LIST *deviceList;

        deviceList=AQHMQTT_Device_List_new();
        rv=_readDeviceFileToList(sFilename, deviceList);
        if (rv<0) {
          DBG_ERROR(NULL, "Could not read file \"%s\" (empty?)", sFilename);
          AQHMQTT_Device_List_free(deviceList);
          return NULL;
        }

        if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
          AQHMQTT_Device_List_free(deviceList);
          return NULL;
        }
        return deviceList;
      }
    }
  }
  return NULL;
}



AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o)
{
  if (o) {
    AQH_MQTTLOG_SERVER *xo;

    xo=AQH_MqttLogServer_GetServerData(o);
    if (xo) {
      GWEN_STRINGLIST *sl;

      sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/mqtt", "*.xml");
      if (sl) {
        AQHMQTT_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;
}



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

  deviceList=AQHMQTT_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 (AQHMQTT_Device_List_GetCount(deviceList)<1) {
    AQHMQTT_Device_List_free(deviceList);
    return NULL;
  }

  return deviceList;
}



int _readDeviceFileToList(const char *sFilename, AQHMQTT_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, AQHMQTT_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, "mqtt")==0) {
        AQHMQTT_DEVICE *device;

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

          sDeviceId=AQHMQTT_Device_GetId(device);
          sDeviceName=AQHMQTT_Device_GetName(device);
          if (sDeviceId && *sDeviceId) {
            DBG_ERROR(NULL, "Adding device \"%s\" (%s) to list", sDeviceId, sDeviceName?sDeviceName:"<no type name>");
          }
          else {
            DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
          }
          AQHMQTT_Device_List_Add(device, deviceList);
        }
      }
      else {
        DBG_INFO(NULL, "Device is not an MQTT 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;
  }
}



AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
{
  AQHMQTT_DEVICE *device;
  GWEN_XMLNODE *topicsNode;

  device=AQHMQTT_Device_new();
  AQHMQTT_Device_SetId(device, GWEN_XMLNode_GetProperty(deviceNode, "id", NULL));
  AQHMQTT_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
  AQHMQTT_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));

  topicsNode=GWEN_XMLNode_FindFirstTag(deviceNode, "mqtttopics", NULL, NULL);
  if (topicsNode) {
    AQHMQTT_TOPIC_LIST *topicList;

    topicList=_readXmlTopicList(topicsNode);
    if (topicList)
      AQHMQTT_Device_SetTopicList(device, topicList);
    else {
      DBG_INFO(NULL, "No mqtt topics read");
      AQHMQTT_Device_free(device);
      return NULL;
    }
  }
  else {
    DBG_INFO(NULL, "No <mqtttopics> element");
    AQHMQTT_Device_free(device);
    return NULL;
  }

  return device;
}



AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode)
{
  AQHMQTT_TOPIC_LIST *topicList;
  GWEN_XMLNODE *topicNode;

  topicList=AQHMQTT_Topic_List_new();
  topicNode=GWEN_XMLNode_FindFirstTag(parentNode, "mqtttopic", NULL, NULL);
  while(topicNode) {
    AQHMQTT_TOPIC *topic=_readXmlTopic(topicNode);
    if (topic)
      AQHMQTT_Topic_List_Add(topic, topicList);
    else {
      DBG_INFO(NULL, "Error reading <mqtttopic> element");
      AQHMQTT_Topic_List_free(topicList);
      return NULL;
    }
    topicNode=GWEN_XMLNode_FindNextTag(topicNode, "mqtttopic", NULL, NULL);
  }
  if (AQHMQTT_Topic_List_GetCount(topicList)<1) {
    AQHMQTT_Topic_List_free(topicList);
    return NULL;
  }

  return topicList;
}



AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode)
{
  AQHMQTT_TOPIC *topic;
  GWEN_XMLNODE *valuesNode;
  int i;
  const char *s;

  topic=AQHMQTT_Topic_new();
  s=GWEN_XMLNode_GetProperty(topicNode, "type", NULL);
  i=AQHMQTT_TopicType_fromString(s);
  if (i==AQHMQTT_TopicType_Unknown) {
    DBG_ERROR(NULL, "Invalid topic type \"%s\"", s?s:"<empty>");
    AQHMQTT_Topic_free(topic);
    return NULL;
  }
  AQHMQTT_Topic_SetTopicType(topic, i);

  s=GWEN_XMLNode_GetProperty(topicNode, "direction", NULL);
  i=AQHMQTT_TopicDir_fromString(s);
  if (i==AQHMQTT_TopicDir_Unknown) {
    DBG_ERROR(NULL, "Invalid topic direction \"%s\"", s?s:"<empty>");
    AQHMQTT_Topic_free(topic);
    return NULL;
  }
  AQHMQTT_Topic_SetDirection(topic, i);

  AQHMQTT_Topic_SetName(topic, GWEN_XMLNode_GetProperty(topicNode, "name", NULL));
  AQHMQTT_Topic_SetTopic(topic, GWEN_XMLNode_GetCharValue(topicNode, "topic", NULL));
  AQHMQTT_Topic_SetMask(topic, GWEN_XMLNode_GetCharValue(topicNode, "mask", NULL));
  AQHMQTT_Topic_SetBeforeId(topic, GWEN_XMLNode_GetCharValue(topicNode, "beforeId", NULL));
  AQHMQTT_Topic_SetAfterId(topic, GWEN_XMLNode_GetCharValue(topicNode, "afterId", NULL));

  valuesNode=GWEN_XMLNode_FindFirstTag(topicNode, "values", NULL, NULL);
  if (valuesNode) {
    AQHMQTT_VALUE_LIST *valueList;

    valueList=_readXmlValueList(valuesNode);
    if (valueList)
      AQHMQTT_Topic_SetValueList(topic, valueList);
    else {
      DBG_INFO(NULL, "No values read");
      AQHMQTT_Topic_free(topic);
      return NULL;
    }
  }
  else {
    DBG_INFO(NULL, "No <values> element");
    AQHMQTT_Topic_free(topic);
    return NULL;
  }

  return topic;
}



AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode)
{
  AQHMQTT_VALUE_LIST *valueList;
  GWEN_XMLNODE *valueNode;

  valueList=AQHMQTT_Value_List_new();
  valueNode=GWEN_XMLNode_FindFirstTag(parentNode, "value", NULL, NULL);
  while(valueNode) {
    AQHMQTT_VALUE *value=_readXmlValue(valueNode);
    if (value)
      AQHMQTT_Value_List_Add(value, valueList);
    else {
      DBG_INFO(NULL, "Error reading <value> element");
      AQHMQTT_Value_List_free(valueList);
      return NULL;
    }
    valueNode=GWEN_XMLNode_FindNextTag(valueNode, "value", NULL, NULL);
  }
  if (AQHMQTT_Value_List_GetCount(valueList)<1) {
    AQHMQTT_Value_List_free(valueList);
    return NULL;
  }

  return valueList;
}



AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
{
  const char *sValueName;
  AQHMQTT_VALUE *value;
  GWEN_XMLNODE *translationNode;
  const char *s;
  int i;

  sValueName=GWEN_XMLNode_GetProperty(valueNode, "name", NULL);
  value=AQHMQTT_Value_new();
  AQHMQTT_Value_SetName(value, sValueName);
  AQHMQTT_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
  AQHMQTT_Value_SetPath(value, GWEN_XMLNode_GetProperty(valueNode, "path", NULL));

  s=GWEN_XMLNode_GetProperty(valueNode, "type", NULL);
  i=AQHMQTT_ValueType_fromString(s);
  if (i==AQHMQTT_ValueType_Unknown) {
    DBG_ERROR(NULL, "Invalid value type \"%s\"", s?s:"<empty>");
    AQHMQTT_Value_free(value);
    return NULL;
  }
  AQHMQTT_Value_SetValueType(value, i);

  translationNode=GWEN_XMLNode_FindFirstTag(valueNode, "translations", NULL, NULL);
  if (translationNode) {
    AQHMQTT_TRANSLATION_LIST *translationList;

    translationList=_readXmlTranslationList(translationNode);
    if (translationList) {
      DBG_ERROR(NULL, "Translations read for value \"%s\"", sValueName);
      AQHMQTT_Value_SetTranslationList(value, translationList);
    }
  }
  else {
    DBG_ERROR(NULL, "No <translations> element in value %s", sValueName);
  }

  return value;
}



AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode)
{
  AQHMQTT_TRANSLATION_LIST *translationList;
  GWEN_XMLNODE *translationNode;

  translationList=AQHMQTT_Translation_List_new();
  translationNode=GWEN_XMLNode_FindFirstTag(parentNode, "translation", NULL, NULL);
  while(translationNode) {
    AQHMQTT_TRANSLATION *translation=_readXmlTranslation(translationNode);
    if (translation)
      AQHMQTT_Translation_List_Add(translation, translationList);
    else {
      DBG_ERROR(NULL, "Error reading <translation> element");
      AQHMQTT_Translation_List_free(translationList);
      return NULL;
    }
    translationNode=GWEN_XMLNode_FindNextTag(translationNode, "translation", NULL, NULL);
  }
  if (AQHMQTT_Translation_List_GetCount(translationList)<1) {
    AQHMQTT_Translation_List_free(translationList);
    return NULL;
  }

  return translationList;
}



AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode)
{
  int aqhValue;
  const char *sDriverValue;

  aqhValue=GWEN_XMLNode_GetIntProperty(translationNode, "aqhValue", 0);
  sDriverValue=GWEN_XMLNode_GetProperty(translationNode, "driverValue", NULL);
  if (sDriverValue && *sDriverValue) {
    AQHMQTT_TRANSLATION *translation;

    translation=AQHMQTT_Translation_new();
    AQHMQTT_Translation_SetAqhValue(translation, aqhValue);
    AQHMQTT_Translation_SetDriverValue(translation, sDriverValue);
    return translation;
  }
  else {
    DBG_ERROR(AQH_LOGDOMAIN, "Either AqhValue or DriverValue missing in device description file");
    return NULL;
  }
}




