/****************************************************************************
 * 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 "./w_xaxis.h"
#include "./w_axis.h"
#include "aqdiagram/draw/w_drawable.h"
#include "aqdiagram/draw/w_label.h"
#include "aqdiagram/placement/o_layout.h"

#include <gwenhywfar/debug.h>



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

#define AQDG_WIDGET_XAXIS_TICK_MIN_X 10



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

static int _layout(AQDG_OBJECT *object);
static int _draw(AQDG_OBJECT *object);
static void _drawTicks(AQDG_OBJECT *o, AQDG_DRAW_CONTEXT *dc, int absY);
static int _getDefaultWidth(AQDG_OBJECT *o);
static int _getDefaultHeight(AQDG_OBJECT *o);
static int _calcHorizontalPos(double value, int contentSize, double minValue, double maxValue);
static void _setChildrenRelXFromValue(AQDG_OBJECT *o);
static void _setChildrenRelY(AQDG_OBJECT *o);
static void _setChildrenSizes(AQDG_OBJECT *o);
static int _screenDistBetweenLevelTicks(const AQDG_GRAPH_TICK_LIST *tickList, int lvl,
                                        int contentSize, double minValue, double maxValue);




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

AQDG_OBJECT *AQDG_XAxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex)
{
  AQDG_OBJECT *o;

  o=AQDG_AxisWidget_new(parent, options, graphObject, axisIndex);
  AQDG_Object_SetName(o, "XAxisWidget");

  AQDG_Object_SetLayoutFn(o, _layout);
  AQDG_DrawableWidget_SetDrawFn(o, _draw);
  AQDG_Object_SetGetDefaultWidthFn(o, _getDefaultWidth);
  AQDG_Object_SetGetDefaultHeightFn(o, _getDefaultHeight);
  AQDG_Object_SetBorderLeft(o, 0);
  AQDG_Object_SetBorderRight(o, 0);

  return o;
}



int _layout(AQDG_OBJECT *o)
{
  _setChildrenSizes(o);
  _setChildrenRelXFromValue(o);
  _setChildrenRelY(o);
  AQDG_DrawableWidget_UnhideAllChildren(o);
  AQDG_AxisWidget_HideCollidingChildren(o);
  AQDG_LayoutObject_LayoutChildren(o);
  return 1;
}



int _draw(AQDG_OBJECT *object)
{
  AQDG_DRAW_CONTEXT *dc;
  int y;
  int x1;
  int x2;
  int pen;

  AQDG_DrawableWidget_DefaultDraw(object);

  dc=AQDG_DrawableWidget_GetDrawContext(object);
  y=AQDG_Object_GetAbsoluteY(object);
  y+=AQDG_Object_GetBorderTop(object);
  x1=AQDG_Object_GetAbsoluteX(object)+AQDG_Object_GetBorderLeft(object);
  x2=AQDG_Object_GetAbsoluteX(object)+AQDG_Object_GetWidth(object)-AQDG_Object_GetBorderRight(object);
  pen=AQDG_DrawableWidget_GetForegroundPenId(object);
  AQDG_Draw_Context_DrawLine(dc, pen, x1, y, x2, y);
  _drawTicks(object, dc, y);

  return 1;
}



void _drawTicks(AQDG_OBJECT *o, AQDG_DRAW_CONTEXT *dc, int absY)
{
  AQDG_OBJECT *graphObject;
  AQDG_GRAPH *graph;
  AQDG_GRAPH_AXIS *axis;

  graphObject=AQDG_AxisWidget_GetGraphObject(o);
  graph=AQDG_GraphWidget_GetGraph(graphObject);
  axis=AQDG_Graph_GetAxisByIndex(graph, AQDG_AxisWidget_GetAxisIndex(o));
  if (axis) {
    const AQDG_GRAPH_TICK_LIST *tickList;

    tickList=AQDG_Graph_Axis_GetTickList(axis);
    if (tickList) {
      const AQDG_GRAPH_TICK *tick;
      int contentSize;
      int scaleSize;
      double minValue;
      double maxValue;
      int absX;
      int pen;
      int showLevel1;
      int showLevel2;
    
      contentSize=AQDG_Object_GetWidth(o)-AQDG_Object_GetBorderLeft(o)-AQDG_Object_GetBorderRight(o);
      scaleSize=AQDG_AxisWidget_GetScaleSize(o);
      absX=AQDG_Object_GetAbsoluteX(o);
      minValue=AQDG_AxisWidget_GetMinValue(o);
      maxValue=AQDG_AxisWidget_GetMaxValue(o);
      pen=AQDG_DrawableWidget_GetForegroundPenId(o);
      showLevel1=(_screenDistBetweenLevelTicks(tickList, 1, contentSize, minValue, maxValue)>AQDG_WIDGET_XAXIS_TICK_MIN_X)?1:0;
      showLevel2=(_screenDistBetweenLevelTicks(tickList, 2, contentSize, minValue, maxValue)>AQDG_WIDGET_XAXIS_TICK_MIN_X)?1:0;

      tick=AQDG_Graph_Tick_List_First(tickList);
      while(tick) {
        double value;
        int xpos;
        int tickLevel;

        value=AQDG_Graph_Tick_GetValue(tick);
        xpos=_calcHorizontalPos(value, contentSize, minValue, maxValue);
        xpos+=AQDG_Object_GetBorderLeft(o);
        tickLevel=AQDG_Graph_Tick_GetLevel(tick);
        if (AQDG_Graph_Tick_GetLevel(tick)==0)
          AQDG_Draw_Context_DrawLine(dc, pen, absX+xpos, absY-(scaleSize/2), absX+xpos, absY+(scaleSize/2));
        else if (tickLevel==1 && showLevel1)
          AQDG_Draw_Context_DrawLine(dc, pen, absX+xpos, absY-(scaleSize/4), absX+xpos, absY+(scaleSize/4));
        else if (tickLevel==2 && showLevel2)
          AQDG_Draw_Context_DrawLine(dc, pen, absX+xpos, absY-(scaleSize/6), absX+xpos, absY+(scaleSize/6));
        tick=AQDG_Graph_Tick_List_Next(tick);
      }
    }
  }
}



int _screenDistBetweenLevelTicks(const AQDG_GRAPH_TICK_LIST *tickList, int lvl,
                                 int contentSize, double minValue, double maxValue)
{
  double v;
  int pts;

  v=AQDG_Graph_Tick_List_DistanceBetweenTicks(tickList, lvl);
  pts=v*(contentSize/(maxValue-minValue));
  return pts;
}



int _getDefaultWidth(AQDG_OBJECT *o)
{
  return 1;
}



int _getDefaultHeight(AQDG_OBJECT *o)
{
  AQDG_OBJECT *child;
  int h=0;

  /* get max label height */
  child=AQDG_Object_Tree2_GetFirstChild(o);
  while(child) {
    int i;

    i=AQDG_Object_GetDefaultHeight(child);
    h=(i>h)?i:h;
    child=AQDG_Object_Tree2_GetNext(child);
  }

  /* add space for scale, top border and bottom border */
  h+=AQDG_AxisWidget_GetScaleSize(o);
  h+=AQDG_Object_GetBorderTop(o)+AQDG_Object_GetBorderBottom(o);
  return h;
}



void _setChildrenRelXFromValue(AQDG_OBJECT *o)
{
  AQDG_OBJECT *child;
  int contentSize;
  double minValue;
  double maxValue;

  contentSize=AQDG_Object_GetWidth(o);
  minValue=AQDG_AxisWidget_GetMinValue(o);
  maxValue=AQDG_AxisWidget_GetMaxValue(o);

  child=AQDG_Object_Tree2_GetFirstChild(o);
  while(child) {
    int pos;
    double value;

    value=AQDG_DrawableWidget_GetValue(child);
    pos=_calcHorizontalPos(value, contentSize, minValue, maxValue);
    pos+=AQDG_Object_GetBorderLeft(o);
    pos-=AQDG_Object_GetWidth(child)/2;
    AQDG_Object_SetRelativeX(child, pos); /* TODO: take label size into account */
    child=AQDG_Object_Tree2_GetNext(child);
  }
}



void _setChildrenRelY(AQDG_OBJECT *o)
{
  int contentSize;

  contentSize=AQDG_Object_GetHeight(o);

  if (AQDG_AxisWidget_GetAxisIndex(o)==AQDG_GRAPH_AXISPOS_TOP) {
    AQDG_OBJECT *child;
    int borderSize;

    borderSize=AQDG_Object_GetBorderTop(o);
    /* align left */
    child=AQDG_Object_Tree2_GetFirstChild(o);
    while(child) {
      AQDG_Object_SetRelativeY(child, borderSize);
      child=AQDG_Object_Tree2_GetNext(child);
    }
  }
  else {
    AQDG_OBJECT *child;
    int borderSize;

    /* align right */
    borderSize=AQDG_Object_GetBorderBottom(o);
    child=AQDG_Object_Tree2_GetFirstChild(o);
    while(child) {
      int pos;
      int cheight;

      cheight=AQDG_Object_GetHeight(child);
      pos=contentSize-cheight-borderSize;
      AQDG_Object_SetRelativeY(child, pos);
      child=AQDG_Object_Tree2_GetNext(child);
    }
  }
}



void _setChildrenSizes(AQDG_OBJECT *o)
{
  AQDG_OBJECT *child;

  child=AQDG_Object_Tree2_GetFirstChild(o);
  while(child) {
    AQDG_Object_SetWidth(child, AQDG_Object_GetDefaultWidth(child));
    AQDG_Object_SetHeight(child, AQDG_Object_GetDefaultHeight(child));
    child=AQDG_Object_Tree2_GetNext(child);
  }
}



int _calcHorizontalPos(double value, int contentSize, double minValue, double maxValue)
{
  return ((value-minValue)*(contentSize/(maxValue-minValue)));
}


