/****************************************************************************
 * This file is part of the project AqDiagram.
 * AqDiagram (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 "./timegraph.h"

#include <aqdiagram/data/date.h>
#include <aqdiagram/data/floatingavg.h>
#include <aqdiagram/data/accumulate.h>
#include <aqdiagram/data/negate.h>

#include <gwenhywfar/debug.h>

#include <ctype.h>


/* ------------------------------------------------------------------------------------------------
 * definitions
 * ------------------------------------------------------------------------------------------------
 */

#define AQDG_TIMEGRAPH_MARGINSPERCENT_X 1.0
#define AQDG_TIMEGRAPH_MARGINSPERCENT_Y 10.0



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

static int _readNumFromString(const char **sPtr);



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

AQDG_GRAPH *AQDG_TimeGraph_new(const char *sTitle,
                               const char *sSubTitle,
                               const char *sYLabel,
                               const char *sYUnits,
                               int yPrecision)
{
  AQDG_GRAPH *g;
  AQDG_GRAPH_AXIS *xAxis;
  AQDG_GRAPH_AXIS *yAxis;
  AQDG_GRAPH_SUBGRAPH *subGraph;

  g=AQDG_Graph_new();
  AQDG_Graph_SetTitle(g, sTitle);
  AQDG_Graph_SetSubTitle(g, sSubTitle);

  xAxis=AQDG_Graph_Axis_new();
  AQDG_Graph_Axis_SetLabel(xAxis, "Time");
  AQDG_Graph_Axis_SetPrecision(xAxis, 0);
  AQDG_Graph_SetAxis(g, AQDG_GRAPH_AXISPOS_BOTTOM, xAxis);

  yAxis=AQDG_Graph_Axis_new();
  AQDG_Graph_Axis_SetLabel(yAxis, sYLabel);
  AQDG_Graph_Axis_SetPrecision(yAxis, yPrecision);
  AQDG_Graph_Axis_SetUnits(yAxis, sYUnits);
  AQDG_Graph_SetAxis(g, AQDG_GRAPH_AXISPOS_LEFT, yAxis);

  subGraph=AQDG_Graph_SubGraph_new();
  AQDG_Graph_SubGraph_SetIndexAxisX(subGraph, AQDG_GRAPH_AXISPOS_BOTTOM);
  AQDG_Graph_SubGraph_SetIndexAxisY(subGraph, AQDG_GRAPH_AXISPOS_LEFT);
  AQDG_Graph_AddSubGraph(g, subGraph);

  return g;
}



void AQDG_TimeGraph_AddCurve(AQDG_GRAPH *g, const char *sLabel, int graphType, AQDG_GRAPH_DATAPAIR_LIST *dpList)
{
  AQDG_GRAPH_SUBGRAPH *subGraph;

  subGraph=AQDG_Graph_GetFirstSubGraph(g);
  if (subGraph) {
    AQDG_GRAPH_CURVE *curve;

    curve=AQDG_Graph_Curve_new();
    AQDG_Graph_Curve_SetLabel(curve, sLabel);
    AQDG_Graph_Curve_SetGraphType(curve, graphType);
    AQDG_Graph_Curve_SetDataPairs(curve, dpList);
    AQDG_Graph_SubGraph_AddCurve(subGraph, curve);
  }
}



void AQDG_TimeGraph_SetupTicks(AQDG_GRAPH *g, uint32_t flags, double minY, double maxY)
{
  AQDG_GRAPH_AXIS *axis;

  AQDG_Graph_CalcMinMaxValues(g);

  /* create ticks for X axis */
  axis=AQDG_Graph_GetAxisByIndex(g, AQDG_GRAPH_AXISPOS_BOTTOM);
  if (axis) {
    AQDG_Graph_Axis_AddMargins(axis, AQDG_TIMEGRAPH_MARGINSPERCENT_X);
    AQDG_Graph_Axis_GenTimeTicks(axis);
  }

  /* create ticks for Y axis */
  axis=AQDG_Graph_GetAxisByIndex(g, AQDG_GRAPH_AXISPOS_LEFT);
  if (axis) {
    AQDG_Graph_Axis_AddMargins(axis, AQDG_TIMEGRAPH_MARGINSPERCENT_Y);
    if (flags & AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MINY)
      AQDG_Graph_Axis_SetMinValue(axis, minY);
    if (flags & AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MAXY)
      AQDG_Graph_Axis_SetMaxValue(axis, maxY);
    AQDG_Graph_Axis_GenLog10Ticks(axis);
  }
}



void AQDG_TimeGraph_ModifyDataAndAddCurve(AQDG_GRAPH *g, const char *sLabel, const char *sModifier,
                                          AQDG_GRAPH_DATAPAIR_LIST *dpList)
{
  int graphType=AQDG_GRAPH_TYPE_LINE;
  const char *s;
  int num;
  AQDG_GRAPH_DATAPAIR_LIST *newDp;

  AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
  s=sModifier;
  while(*s) {
    while(*s && isblank(*s))
      s++;
    switch(*s) {
    case 'L': graphType=AQDG_GRAPH_TYPE_LINE;     s++; break;
    case 'S': graphType=AQDG_GRAPH_TYPE_STEPLINE; s++; break;
    case 'P': graphType=AQDG_GRAPH_TYPE_POINTS;   s++; break;
    case 'B': graphType=AQDG_GRAPH_TYPE_BARS;     s++; break;

    case 'A':
      newDp=AQDG_Data_Accumulate(dpList);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'D':
      newDp=AQDG_Data_DaySums(dpList);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'M':
      newDp=AQDG_Data_MonthSums(dpList);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'N':
      newDp=AQDG_Data_NegateY(dpList);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'd':
      newDp=AQDG_Data_DayAverage(dpList);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'm':
      s++;
      num=_readNumFromString(&s);
      newDp=AQDG_Data_MinutesAverage(dpList, num);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'f':
      s++;
      num=_readNumFromString(&s);
      newDp=AQDG_Data_FloatingAverage(dpList, num);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'a':
      s++;
      num=_readNumFromString(&s);
      newDp=AQDG_Data_MinutesMax(dpList, num);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    case 'i':
      s++;
      num=_readNumFromString(&s);
      newDp=AQDG_Data_MinutesMin(dpList, num);
      AQDG_Graph_DataPair_List_free(dpList);
      dpList=newDp;
      AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
      break;
    default:
      DBG_ERROR(NULL, "Invalid modifier \"%c\"", *s);
      s++;
      break;
    }
  }

  AQDG_TimeGraph_AddCurve(g, sLabel, graphType, dpList);
}



int _readNumFromString(const char **sPtr)
{
  int x=0;
  const char *s;

  s=*sPtr;
  while(*s && isdigit(*s)) {
    unsigned int i;
  
    i=*(s++)-'0';
    x*=10;
    x+=i;
  }
  *sPtr=s;
  return x;
}



