/****************************************************************************
 * 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 "./mdevices_vgraph.h"

#include "aqhome-cgi/modules/mdataclient.h"

#include <aqcgi/service/module.h>

#include <aqdiagram/graph/timegraph.h>
#include <aqdiagram/graph/w_graph.h>
#include <aqdiagram/draw/context_cairo.h>
//#include <aqdiagram/data/date.h>
//#include <aqdiagram/data/floatingavg.h>

#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>



/* ------------------------------------------------------------------------------------------------
 * defs and enums
 * ------------------------------------------------------------------------------------------------
 */

#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs



/* ------------------------------------------------------------------------------------------------
 * vars
 * ------------------------------------------------------------------------------------------------
 */

typedef struct MY_GRAPH_PARAMS MY_GRAPH_PARAMS;
struct MY_GRAPH_PARAMS {
  const char *name;
  const char *title;
  const char *modifiers;
  int startTimeDiff;
  int acceptedAgeInSeconds;
};



static MY_GRAPH_PARAMS _graphParamsAny[]={
  {"4h", "last 4 hours",   "Lm5",    4*60*60,      2*60},
  {"1d", "last 24 hours",  "Lm30",   24*60*60,     5*60},
  {"1w", "last 7 days",    "Lm240",  7*24*60*60,   15*60},
  {"1m", "last 30 days",   "Lm480",  30*24*60*60,  60*60},
  {"6m", "last 6 months",  "Lm720",  182*24*60*60, 60*60},
  {"12m","last 12 months", "Lm1440", 365*24*60*60, 60*60},
  {NULL, NULL, NULL, 0, 0}
};



static MY_GRAPH_PARAMS _graphParamsBool[]={
  {"4h", "last 4 hours",   "Sa5",    4*60*60,      2*60},
  {"1d", "last 24 hours",  "Sa30",   24*60*60,     5*60},
  {"1w", "last 7 days",    "Sa240",  7*24*60*60,   15*60},
  {"1m", "last 30 days",   "Sa480",  30*24*60*60,  60*60},
  {"6m", "last 6 months",  "Sa720",  182*24*60*60, 60*60},
  {"12m","last 12 months", "Sa1440", 365*24*60*60, 60*60},
  {NULL, NULL, NULL, 0, 0}
};



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

static void _runGraphValueWithArgs(AQCGI_MODULE *m,
                                   AQCGI_REQUEST *rq,
                                   AQH_DATACLIENT *dc,
                                   const char *sDeviceName,
                                   const char *sValueName,
                                   GWEN_BUFFER *dbuf);
static void _createGraph(AQH_DATACLIENT *dc,
                         const AQH_VALUE *v,
                         const MY_GRAPH_PARAMS *graphParams,
                         const char *graphTitle, int precision, const char *curveLabel,
                         const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
static AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision);
static void _mkPathForValueAndPeriod(AQCGI_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf);
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s, int modality);



/* ------------------------------------------------------------------------------------------------
 * code
 * ------------------------------------------------------------------------------------------------
 */

void AQH_ModDevices_RunGraphValue(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
  GWEN_DB_NODE *dbQuery;
  const char *sDeviceName;
  const char *sValueName;

  DBG_DEBUG(NULL, "GraphValue");
  dbQuery=AQCGI_Request_GetDbQuery(rq);
  sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
  sValueName=GWEN_DB_GetCharValue(dbQuery, "value", 0, NULL);
  DBG_DEBUG(NULL, "Device=%s, value=%s", sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>");
  if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
    GWEN_BUFFER *bufDeviceName;
    GWEN_BUFFER *bufValueName;

    bufDeviceName=GWEN_Buffer_new(0, 64, 0, 1);
    GWEN_Text_UnescapeToBufferTolerant(sDeviceName, bufDeviceName);
    bufValueName=GWEN_Buffer_new(0, 64, 0, 1);
    GWEN_Text_UnescapeToBufferTolerant(sValueName, bufValueName);
    _runGraphValueWithArgs(m, rq, dc,
                           GWEN_Buffer_GetStart(bufDeviceName),
                           GWEN_Buffer_GetStart(bufValueName),
                           dbuf);
    GWEN_Buffer_free(bufValueName);
    GWEN_Buffer_free(bufDeviceName);
  }
}



void _runGraphValueWithArgs(AQCGI_MODULE *m,
                            AQCGI_REQUEST *rq,
                            AQH_DATACLIENT *dc,
                            const char *sDeviceName,
                            const char *sValueName,
                            GWEN_BUFFER *dbuf)
{
  GWEN_DB_NODE *dbQuery;
  AQH_VALUE *value;
  const MY_GRAPH_PARAMS *graphParams;
  const char *sPeriod;

  DBG_DEBUG(NULL, "GraphValue with args");
  dbQuery=AQCGI_Request_GetDbQuery(rq);
  sPeriod=GWEN_DB_GetCharValue(dbQuery, "period", 0, NULL);
  DBG_DEBUG(NULL, "Device=%s, value=%s, period=%s",
            sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>",
            sPeriod?sPeriod:"<empty>");

  value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
  if (value) {
    GWEN_BUFFER *fbuf;

    graphParams=_getParamsByName(sPeriod, AQH_Value_GetModality(value));
    fbuf=GWEN_Buffer_new(0, 256, 0, 1);
    _mkPathForValueAndPeriod(m, value, graphParams, fbuf);
    if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
      DBG_DEBUG(NULL, "Creating graph");
      _createGraph(dc,
                   value,
                   graphParams,
                   sValueName,
                   2,
                   AQH_ValueModality_toString(AQH_Value_GetModality(value)),
                   GWEN_Buffer_GetStart(fbuf),
                   AQH_MODDEVICES_GRAPH_WIDTH, AQH_MODDEVICES_GRAPH_HEIGHT,
                   100000);
    }

    AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);

    GWEN_Buffer_free(fbuf);
    AQH_Value_free(value);
  }
  else {
    DBG_ERROR(NULL, "Could not get value \"%s/%s\"", sDeviceName, sValueName);
  }
}




void _createGraph(AQH_DATACLIENT *dc,
                  const AQH_VALUE *v,
                  const MY_GRAPH_PARAMS *graphParams,
                  const char *graphTitle, int precision,
                  const char *curveLabel,
                  const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints)
{
  const char *sValue;
  AQDG_GRAPH *g;
  AQDG_DRAW_CONTEXT *drawContext;
  AQDG_OBJECT *graphObject;
  uint64_t tsBegin;
  uint64_t tsEnd;
  AQDG_GRAPH_DATAPAIR_LIST *dpList;

  sValue=AQH_Value_GetNameForSystem(v);
  tsEnd=time(0);
  tsBegin=time(0)-(graphParams->startTimeDiff);
  g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);

  dpList=AQH_ModDevices_RequestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
  if (dpList) {
    DBG_DEBUG(NULL, "Adding data for %s", sValue);
    AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
  }
  else {
    DBG_ERROR(NULL, "No data for %s", sValue);
    AQDG_Graph_free(g);
    return;
  }

  AQDG_TimeGraph_SetupTicks(g, 0, 0.0, 0.0);

  DBG_DEBUG(NULL, "Draw graph for %s", sValue);
  drawContext=AQDG_Draw_ContextCairo_Png_new(sImgFile, imgWidth, imgHeight);
  graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
  AQDG_Object_SetWidth(graphObject, imgWidth);
  AQDG_Object_SetHeight(graphObject, imgHeight);

  AQDG_GraphWidget_SetupDefaultPens(graphObject);
  AQDG_GraphWidget_SetupDefaultFonts(graphObject);

  AQDG_GraphWidget_FinishWithGraph(graphObject, g);

  AQDG_Object_free(graphObject);
}



AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision)
{
  AQDG_GRAPH *g;
  GWEN_BUFFER *tbuf;

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  GBAA(tbuf, "%s - %s", graphTitle, graphParams->title);

  g=AQDG_TimeGraph_new(GWEN_Buffer_GetStart(tbuf), NULL, "Value", NULL, precision);
  GWEN_Buffer_free(tbuf);
  return g;
}


void _mkPathForValueAndPeriod(AQCGI_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf)
{
  AQCGI_SERVICE *sv;
  const char *s;

  sv=AQH_ModService_GetService(m);

  /* cache folder */
  s=AQCGI_Service_GetCacheFolder(sv);
  GBAA(dbuf, "%s%s", s, GWEN_DIR_SEPARATOR_S);

  /* var name */
  s=AQH_Value_GetNameForSystem(v);
  AQH_ModService_EscapeToBuffer(s, dbuf);

  GBAA(dbuf, "-%s.png", graphParams->name);
}



const MY_GRAPH_PARAMS *_getParamsByName(const char *s, int modality)
{
  const MY_GRAPH_PARAMS *startP;
  const MY_GRAPH_PARAMS *p;

  switch(modality) {
    case AQH_ValueModality_Door:
    case AQH_ValueModality_Motion:
    case AQH_ValueModality_OnOff:   startP=_graphParamsBool; break;
    default:                        startP=_graphParamsAny;  break;
  }

  p=startP;
  while(p->name) {
    if (strcasecmp(p->name, s)==0)
      return p;
    p++;
  }

  return startP;
}



