/** \file
 * \brief GLToggle Control.
 *
 * See Copyright Notice in "iup.h"
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "iup.h"
#include "iupcbs.h"
#include "iupglcontrols.h"

#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_register.h"

#include "iup_glcontrols.h"
#include "iup_gldraw.h"
#include "iup_glicon.h"
#include "iup_glsubcanvas.h"


/* from IupRadio implementation */
IUP_SDK_API Ihandle *iupRadioFindToggleParent(Ihandle* ih_toggle);

/* from IupGLButton implementation */
void iupGLButtonDraw(Ihandle* ih);

static void iGLToggleDrawCheckMark(Ihandle* ih)
{
  char *image = iupAttribGet(ih, "IMAGE");
  char* title = iupAttribGet(ih, "TITLE");
  int active = iupAttribGetInt(ih, "ACTIVE");
  int pressed = iupAttribGetInt(ih, "PRESSED");
  int highlight = iupAttribGetInt(ih, "HIGHLIGHT");
  char* fgcolor = iupAttribGetStr(ih, "FGCOLOR");
  char* bgcolor = iupAttribGetStr(ih, "BGCOLOR");
  float bwidth = iupAttribGetFloat(ih, "BORDERWIDTH");
  int border_width = (int)ceil(bwidth);
  int check_width = iupAttribGetInt(ih, "CHECKMARKWIDTH");
  int spacing = iupAttribGetInt(ih, "SPACING");
  Ihandle* radio = iupRadioFindToggleParent(ih);
  int check = iupAttribGetBoolean(ih, "VALUE");
  int rightbutton = iupAttribGetBoolean(ih, "RIGHTBUTTON");
  char* bordercolor = iupAttribGetStr(ih, "BORDERCOLOR");
  int y = (ih->currentheight - check_width) / 2;
  int x1 = 0;
  int x2 = check_width + spacing;

  if (pressed)
  {
    char* presscolor = iupAttribGetStr(ih, "PSCOLOR");
    if (presscolor)
      bgcolor = presscolor;
  }
  else if (highlight)
  {
    char* hlcolor = iupAttribGetStr(ih, "HLCOLOR");
    if (hlcolor)
      bgcolor = hlcolor;
  }
  if (!bgcolor)
    bgcolor = "255 255 255";

  if (rightbutton)
  {
    x1 = ih->currentwidth - check_width - border_width;
    x2 = 0;
  }

  if (radio)
  {
    iupGLDrawSmallDisc(ih, x1 + check_width / 2, y + check_width / 2, check_width / 2, bgcolor, 1);
    iupGLDrawSmallCircle(ih, x1 + check_width / 2, y + check_width / 2, check_width / 2, bwidth, bordercolor, active);

    if (check)
    {
      iupGLDrawSmallDisc(ih, x1 + check_width / 2, y + check_width / 2, check_width / 4, bordercolor, active);
      iupGLDrawSmallCircle(ih, x1 + check_width / 2, y + check_width / 2, check_width / 4, bwidth, bordercolor, active);
    }
  }
  else
  {
    iupGLDrawBox(ih, x1 + border_width, x1 + check_width - border_width, y + border_width, y + check_width - border_width, bgcolor, 1);
    iupGLDrawRect(ih, x1, x1 + check_width, y, y + check_width, bwidth, bordercolor, active, 0);

    if (check)
    {
      int points[6];

      points[0] = x1 + 3;
      points[1] = y + check_width / 2;
      points[2] = x1 + check_width / 2;
      points[3] = y + check_width - 3;
      points[4] = x1 + check_width - 3;
      points[5] = y + 3;

      iupGLDrawPolyline(ih, points, 3, 3, bordercolor, active, 0);
    }
  }

  iupGLIconDraw(ih, x2, 0,
                    ih->currentwidth - check_width - spacing, ih->currentheight,
                    "IMAGE", image, title, fgcolor, active);
}

static int iGLToggleACTION(Ihandle* ih)
{
  int checkmark = iupAttribGetInt(ih, "CHECKMARK");
  if (checkmark)
    iGLToggleDrawCheckMark(ih);
  else
    iupGLButtonDraw(ih);

  return IUP_DEFAULT;
}

static void iGLToggleNotify(Ihandle* ih, int selected)
{
  IFni cb = (IFni)IupGetCallback(ih, "ACTION");
  if (cb)
  {
    int ret = cb(ih, selected);
    if (ret == IUP_CLOSE)
      IupExitLoop();
  }

  if (iupObjectCheck(ih))
    iupBaseCallValueChangedCb(ih);
}

static int iGLToggleBUTTON_CB(Ihandle* ih, int button, int pressed, int x, int y, char* status)
{
  if (button == IUP_BUTTON1)
  {
    Ihandle* radio = iupRadioFindToggleParent(ih);
    int selected = iupAttribGetInt(ih, "VALUE");
    Ihandle* last_tg = NULL;

    if (!pressed)
    {
      if (selected)  /* was ON */
      {
        if (!radio)
        {
          iupAttribSet(ih, "VALUE", "OFF");
          selected = 0;
        }
        else
          last_tg = ih;  /* to avoid the callback call */
      }
      else  /* was OFF */
      {
        if (radio)
        {
          last_tg = (Ihandle*)iupAttribGet(radio, "_IUP_GLTOGGLE_LASTRADIO");
          if (iupObjectCheck(last_tg) && last_tg != ih)
            iupAttribSet(last_tg, "VALUE", "OFF");
          else
            last_tg = NULL;

          iupAttribSet(radio, "_IUP_GLTOGGLE_LASTRADIO", (char*)ih);
        }

        iupAttribSet(ih, "VALUE", "ON");
        selected = 1;
      }
    }

    /* "PRESSED" was already updated */
    iupGLSubCanvasRedraw(ih);

    if (!pressed)
    {
      if (last_tg && ih != last_tg)
        iGLToggleNotify(last_tg, 0);

      if (!radio || ih != last_tg)
        iGLToggleNotify(ih, selected);
    }
  }

  (void)x;
  (void)y;
  (void)status;
  return IUP_DEFAULT;
}

static int iGLToggleSetValueAttrib(Ihandle* ih, const char* value)
{
  Ihandle* radio = iupRadioFindToggleParent(ih);
  if (radio)
  {
    /* can only set Radio to ON */
    if (iupStrEqualNoCase(value, "TOGGLE") || iupStrBoolean(value))
    {
      Ihandle* last_tg = (Ihandle*)iupAttribGet(radio, "_IUP_GLTOGGLE_LASTRADIO");
      if (iupObjectCheck(last_tg) && last_tg != ih)
        iupAttribSet(last_tg, "VALUE", "OFF");

      iupAttribSet(radio, "_IUP_GLTOGGLE_LASTRADIO", (char*)ih);
    }
    else
      return 0;
  }
  else
  {
    if (iupStrEqualNoCase(value, "TOGGLE"))
    {
      int oldcheck = iupAttribGetBoolean(ih, "VALUE");
      if (oldcheck)
        iupAttribSet(ih, "VALUE", "OFF");
      else
        iupAttribSet(ih, "VALUE", "ON");

      return 0;
    }
  }

  return 1;
}

static char* iGLToggleGetRadioAttrib(Ihandle* ih)
{
  Ihandle* radio = iupRadioFindToggleParent(ih);
  return iupStrReturnBoolean(radio!=NULL);
}

static void iGLToggleComputeNaturalSizeMethod(Ihandle* ih, int *w, int *h, int *children_expand)
{
  int check = iupAttribGetInt(ih, "CHECKMARK");
  if (check)
  {
    int check_width = iupAttribGetInt(ih, "CHECKMARKWIDTH");
    int spacing = iupAttribGetInt(ih, "SPACING");
    float bwidth = iupAttribGetFloat(ih, "BORDERWIDTH");
    int border_width = (int)ceil(bwidth);
    if (border_width == 0)
      return;

    /* undo add to the label natural size */
    *w -= 2 * border_width;
    *h -= 2 * border_width;

    /* add to the label natural size */
    *w += check_width + spacing;
    *h = iupMAX(*h, check_width);
  }

  (void)children_expand; /* unset if not a container */
}

static int iGLToggleMapMethod(Ihandle* ih)
{
  Ihandle* radio = iupRadioFindToggleParent(ih);
  if (radio)
  {
    if (!iupAttribGet(radio, "_IUP_GLTOGGLE_LASTRADIO"))
    {
      /* this is the first toggle in the radio, and then set it with VALUE=ON */
      iupAttribSet(ih, "VALUE", "ON");
    }

    /* make sure it has at least one name */
    if (!iupAttribGetHandleName(ih))
      iupAttribSetHandleName(ih);
  }
  return IUP_NOERROR;
}

static int iGLToggleCreateMethod(Ihandle* ih, void** params)
{
  IupSetCallback(ih, "GL_ACTION", iGLToggleACTION);
  IupSetCallback(ih, "GL_BUTTON_CB", (Icallback)iGLToggleBUTTON_CB);

  (void)params; /* button create already parsed title */
  return IUP_NOERROR;
}


/******************************************************************************/


Iclass* iupGLToggleNewClass(void)
{
  Iclass* ic = iupClassNew(iupRegisterFindClass("glbutton"));

  ic->name = "gltoggle";
  ic->cons = "GLToggle";
  ic->format = "s"; /* one string */
  ic->format_attr = "TITLE";
  ic->nativetype = IUP_TYPEVOID;
  ic->childtype = IUP_CHILDNONE;
  ic->is_interactive = 0;

  /* Class functions */
  ic->New = iupGLToggleNewClass;
  ic->Create = iGLToggleCreateMethod;
  ic->ComputeNaturalSize = iGLToggleComputeNaturalSizeMethod;
  ic->Map = iGLToggleMapMethod;

  iupClassRegisterCallback(ic, "ACTION", "i");
  iupClassRegisterCallback(ic, "VALUECHANGED_CB", "");

  iupClassRegisterAttribute(ic, "VALUE", NULL, iGLToggleSetValueAttrib, NULL, NULL, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "RADIO", iGLToggleGetRadioAttrib, NULL, NULL, NULL, IUPAF_READONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "CHECKMARK", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "CHECKMARKWIDTH", NULL, NULL, IUPAF_SAMEASSYSTEM, "14", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "RIGHTBUTTON", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "IGNORERADIO", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);

  return ic;
}

Ihandle* IupGLToggle(const char* title)
{
  void *params[2];
  params[0] = (void*)title;
  params[1] = NULL;
  return IupCreatev("gltoggle", params);
}

