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

  crect_temp.h

  (c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.

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

#define IMPLEMENT_RECT_CLASS(__struct, __name, __gtype, __ctype, __sign, __return, __this, __pstruct, __pname)                \
                                                                                                                              \
static void __struct##_normalize(__struct *_object)                                                                           \
{                                                                                                                             \
  if (__this->w < 0)                                                                                                          \
  {                                                                                                                           \
    __this->w = (- __this->w);                                                                                                \
    __this->x -= __this->w;                                                                                                   \
  }                                                                                                                           \
                                                                                                                              \
  if (__this->h < 0)                                                                                                          \
  {                                                                                                                           \
    __this->h = (- __this->h);                                                                                                \
    __this->y -= __this->h;                                                                                                   \
  }                                                                                                                           \
}                                                                                                                             \
                                                                                                                              \
__struct * __struct##_create(void)                                                                                            \
{                                                                                                                             \
  return GB.New(GB.FindClass(#__name), NULL, NULL);                                                                           \
}                                                                                                                             \
                                                                                                                              \
                                                                                                                              \
BEGIN_METHOD(__name##_new, __gtype x; __gtype y; __gtype w; __gtype h)                                                        \
                                                                                                                              \
  if (!MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h))                                                               \
  {                                                                                                                           \
    __this->x = VARG(x);                                                                                                      \
    __this->y = VARG(y);                                                                                                      \
    __this->w = VARG(w);                                                                                                      \
    __this->h = VARG(h);                                                                                                      \
    __struct##_normalize(__this);                                                                                             \
  }                                                                                                                           \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_call, __gtype x; __gtype y; __gtype w; __gtype h)                                                       \
                                                                                                                              \
  __struct *rect = __struct##_create();                                                                                       \
                                                                                                                              \
  rect->x = VARG(x);                                                                                                          \
  rect->y = VARG(y);                                                                                                          \
  rect->w = VARG(w);                                                                                                          \
  rect->h = VARG(h);                                                                                                          \
  __struct##_normalize(rect);                                                                                                 \
                                                                                                                              \
  GB.ReturnObject(rect);                                                                                                      \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_PROPERTY(__name##_X)                                                                                                    \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->x);                                                                                                      \
  else                                                                                                                        \
    __this->x = VPROP(__gtype);                                                                                               \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Y)                                                                                                    \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->y);                                                                                                      \
  else                                                                                                                        \
    __this->y = VPROP(__gtype);                                                                                               \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Width)                                                                                                \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->w);                                                                                                      \
  else                                                                                                                        \
  {                                                                                                                           \
    __this->w = VPROP(__gtype);                                                                                               \
    __struct##_normalize(__this);                                                                                             \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Height)                                                                                               \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->h);                                                                                                      \
  else                                                                                                                        \
  {                                                                                                                           \
    __this->h = VPROP(__gtype);                                                                                               \
    __struct##_normalize(__this);                                                                                             \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Left)                                                                                                 \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->x);                                                                                                      \
  else                                                                                                                        \
  {                                                                                                                           \
    __ctype dx = VPROP(__gtype) - __this->x;                                                                                  \
    if (dx > __this->w)                                                                                                       \
      dx = __this->w;                                                                                                         \
                                                                                                                              \
    __this->x += dx;                                                                                                          \
    __this->w -= dx;                                                                                                          \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Top)                                                                                                  \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->y);                                                                                                      \
  else                                                                                                                        \
  {                                                                                                                           \
    __ctype dy = VPROP(__gtype) - __this->y;                                                                                  \
    if (dy > __this->h)                                                                                                       \
      dy = __this->h;                                                                                                         \
                                                                                                                              \
    __this->y += dy;                                                                                                          \
    __this->h -= dy;                                                                                                          \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Right)                                                                                                \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->x + __this->w);                                                                                          \
  else                                                                                                                        \
  {                                                                                                                           \
    __ctype x2 = VPROP(__gtype);                                                                                              \
    if (x2 < __this->x)                                                                                                       \
      x2 = __this->x;                                                                                                         \
                                                                                                                              \
    __this->w = x2 - __this->x;                                                                                               \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_PROPERTY(__name##_Bottom)                                                                                               \
                                                                                                                              \
  if (READ_PROPERTY)                                                                                                          \
    __return(__this->y + __this->h);                                                                                          \
  else                                                                                                                        \
  {                                                                                                                           \
    __ctype y2 = VPROP(__gtype);                                                                                              \
    if (y2 < __this->y)                                                                                                       \
      y2 = __this->y;                                                                                                         \
                                                                                                                              \
    __this->h = y2 - __this->y;                                                                                               \
  }                                                                                                                           \
                                                                                                                              \
END_PROPERTY                                                                                                                  \
                                                                                                                              \
BEGIN_METHOD_VOID(__name##_Clear)                                                                                             \
                                                                                                                              \
  __this->x = __this->y = __this->w = __this->h = 0;                                                                          \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD_VOID(__name##_IsVoid)                                                                                            \
                                                                                                                              \
  GB.ReturnBoolean(__this->w <= 0 || __this->h <= 0);                                                                         \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD_VOID(__name##_Copy)                                                                                              \
                                                                                                                              \
  __struct *copy = __struct##_create();                                                                                       \
                                                                                                                              \
  copy->x = __this->x;                                                                                                        \
  copy->y = __this->y;                                                                                                        \
  copy->w = __this->w;                                                                                                        \
  copy->h = __this->h;                                                                                                        \
                                                                                                                              \
  GB.ReturnObject(copy);                                                                                                      \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Move, __gtype x; __gtype y; __gtype w; __gtype h)                                                       \
                                                                                                                              \
  __this->x = VARG(x);                                                                                                        \
  __this->y = VARG(y);                                                                                                        \
  if (!MISSING(w) && !MISSING(h))                                                                                             \
  {                                                                                                                           \
    __this->w = VARG(w);                                                                                                      \
    __this->h = VARG(h);                                                                                                      \
    __struct##_normalize(__this);                                                                                             \
  }                                                                                                                           \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Resize, __gtype w; __gtype h)                                                                           \
                                                                                                                              \
  __this->w = VARG(w);                                                                                                        \
  __this->h = VARG(h);                                                                                                        \
  __struct##_normalize(__this);                                                                                               \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Translate, __gtype dx; __gtype dy)                                                                      \
                                                                                                                              \
  __this->x += VARG(dx);                                                                                                      \
  __this->y += VARG(dy);                                                                                                      \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Union, GB_OBJECT rect)                                                                                  \
                                                                                                                              \
  __struct *dest;                                                                                                             \
  __ctype x, y, w, h;                                                                                                         \
  __struct *rect = (__struct *)VARG(rect);                                                                                    \
                                                                                                                              \
  if (GB.CheckObject(rect))                                                                                                   \
    return;                                                                                                                   \
                                                                                                                              \
  dest = __struct##_create();                                                                                                 \
                                                                                                                              \
  x = Min(__this->x, rect->x);                                                                                                \
  y = Min(__this->y, rect->y);                                                                                                \
  w = Max(__this->x + __this->w, rect->x + rect->w) - x;                                                                      \
  h = Max(__this->y + __this->h, rect->y + rect->h) - y;                                                                      \
                                                                                                                              \
  dest->x = x;                                                                                                                \
  dest->y = y;                                                                                                                \
  dest->w = w;                                                                                                                \
  dest->h = h;                                                                                                                \
                                                                                                                              \
  GB.ReturnObject(dest);                                                                                                      \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Intersection, GB_OBJECT rect)                                                                           \
                                                                                                                              \
  __struct *dest;                                                                                                             \
  __ctype x, y, x2, y2;                                                                                                       \
  __struct *rect = (__struct *)VARG(rect);                                                                                    \
                                                                                                                              \
  if (GB.CheckObject(rect))                                                                                                   \
    return;                                                                                                                   \
                                                                                                                              \
  x = Max(__this->x, rect->x);                                                                                                \
  y = Max(__this->y, rect->y);                                                                                                \
  x2 = Min(__this->x + __this->w, rect->x + rect->w);                                                                         \
  y2 = Min(__this->y + __this->h, rect->y + rect->h);                                                                         \
                                                                                                                              \
  if (x2 > x && y2 > y)                                                                                                       \
  {                                                                                                                           \
    dest = __struct##_create();                                                                                               \
                                                                                                                              \
    dest->x = x;                                                                                                              \
    dest->y = y;                                                                                                              \
    dest->w = x2 - x;                                                                                                         \
    dest->h = y2 - y;                                                                                                         \
                                                                                                                              \
    GB.ReturnObject(dest);                                                                                                    \
  }                                                                                                                           \
  else                                                                                                                        \
  {                                                                                                                           \
    GB.ReturnNull();                                                                                                          \
  }                                                                                                                           \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Contains, __gtype x; __gtype y)                                                                         \
                                                                                                                              \
  __ctype x = VARG(x);                                                                                                        \
  __ctype y = VARG(y);                                                                                                        \
                                                                                                                              \
  GB.ReturnBoolean((x >= __this->x) && (x < (__this->x + __this->w)) && (y >= __this->y) && (y < (__this->y + __this->h)));   \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Adjust, __gtype left; __gtype top; __gtype right; __gtype bottom)                                       \
                                                                                                                              \
  __ctype left = VARG(left);                                                                                                  \
  __ctype top = VARGOPT(top, left);                                                                                           \
  __ctype right = VARGOPT(right, left);                                                                                       \
  __ctype bottom = VARGOPT(bottom, top);                                                                                      \
                                                                                                                              \
  __this->x += left;                                                                                                          \
  __this->w -= (left + right);                                                                                                \
  __this->y += top;                                                                                                           \
  __this->h -= (top + bottom);                                                                                                \
                                                                                                                              \
  if (__this->w < 1 || __this->h < 1)                                                                                         \
    __this->w = __this->h = 0;                                                                                                \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD_VOID(__name##_Center)                                                                                            \
                                                                                                                              \
  __pstruct *point = GB.New(GB.FindClass(#__pname), NULL, NULL);                                                              \
	point->x = __this->x + __this->w / 2;                                                                                       \
	point->y = __this->y + __this->h / 2;                                                                                       \
	GB.ReturnObject(point);                                                                                                     \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
BEGIN_METHOD(__name##_Stretch, __gtype width; __gtype height; GB_OBJECT frame; GB_INTEGER align)                              \
                                                                                                                              \
  __struct *frame = (__struct *)VARG(frame);                                                                                  \
  int align = VARGOPT(align, ALIGN_CENTER);                                                                                   \
  __ctype w = VARG(width);                                                                                                    \
  __ctype h = VARG(height);                                                                                                   \
                                                                                                                              \
	__struct *result;                                                                                                           \
	double scalew, scaleh;                                                                                                      \
                                                                                                                              \
	if (GB.CheckObject(frame))                                                                                                  \
		return;                                                                                                                   \
                                                                                                                              \
	result = __struct##_create();                                                                                               \
	if (w <= 0 || h <= 0 || frame->w <= 0 || frame->h <= 0)                                                                     \
	{                                                                                                                           \
		GB.ReturnObject(result);                                                                                                  \
		return;                                                                                                                   \
	}                                                                                                                           \
                                                                                                                              \
	scalew = (double)frame->w / w;                                                                                              \
	scaleh = (double)frame->h / h;                                                                                              \
	if (scalew > scaleh)                                                                                                        \
		scalew = scaleh;                                                                                                          \
	else                                                                                                                        \
		scaleh = scalew;                                                                                                          \
                                                                                                                              \
	result->w = w * scalew;                                                                                                     \
	result->h = h * scaleh;                                                                                                     \
                                                                                                                              \
	if (ALIGN_IS_LEFT(align))                                                                                                   \
		result->x = frame->x;                                                                                                     \
	else if (ALIGN_IS_CENTER(align))                                                                                            \
		result->x = frame->x + (frame->w - result->w) / 2;                                                                        \
	else if (ALIGN_IS_RIGHT(align))                                                                                             \
		result->x = frame->x + frame->w - result->w;                                                                              \
                                                                                                                              \
	if (ALIGN_IS_TOP(align))                                                                                                    \
		result->y = frame->y;                                                                                                     \
	else if (ALIGN_IS_MIDDLE(align))                                                                                            \
		result->y = frame->y + (frame->h - result->h) / 2;                                                                        \
	else if (ALIGN_IS_BOTTOM(align))                                                                                            \
		result->y = frame->y + frame->h - result->h;                                                                              \
                                                                                                                              \
	GB.ReturnObject(result);                                                                                                    \
                                                                                                                              \
END_METHOD                                                                                                                    \
                                                                                                                              \
GB_DESC __name##Desc[] =                                                                                                      \
{                                                                                                                             \
  GB_DECLARE(#__name, sizeof(__struct)),                                                                                      \
                                                                                                                              \
  GB_METHOD("_new", NULL, __name##_new, "[(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign "]"),                   \
  GB_STATIC_METHOD("_call", #__name, __name##_call, "(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign),            \
                                                                                                                              \
  GB_PROPERTY("X", __sign, __name##_X),                                                                                       \
  GB_PROPERTY("Y", __sign, __name##_Y),                                                                                       \
  GB_PROPERTY("W", __sign, __name##_Width),                                                                                   \
  GB_PROPERTY("H", __sign, __name##_Height),                                                                                  \
  GB_PROPERTY("Width", __sign, __name##_Width),                                                                               \
  GB_PROPERTY("Height", __sign, __name##_Height),                                                                             \
  GB_PROPERTY("Left", __sign, __name##_Left),                                                                                 \
  GB_PROPERTY("Top", __sign, __name##_Top),                                                                                   \
  GB_PROPERTY("Right", __sign, __name##_Right),                                                                               \
  GB_PROPERTY("Bottom", __sign, __name##_Bottom),                                                                             \
                                                                                                                              \
  GB_METHOD("Clear", NULL, __name##_Clear, NULL),                                                                             \
  GB_METHOD("IsVoid", "b", __name##_IsVoid, NULL),                                                                            \
  GB_METHOD("Copy", #__name, __name##_Copy, NULL),                                                                            \
  GB_METHOD("Move", NULL, __name##_Move, "(X)" __sign "(Y)" __sign "[(Width)" __sign "(Height)" __sign "]"),                  \
  GB_METHOD("Resize", NULL, __name##_Resize, "(Width)" __sign "(Height)" __sign ""),                                          \
  GB_METHOD("Translate", NULL, __name##_Translate, "(DX)" __sign "(DY)" __sign ""),                                           \
  GB_METHOD("Union", #__name, __name##_Union, "(Rect)" #__name ";"),                                                          \
  GB_METHOD("Intersection", #__name, __name##_Intersection, "(Rect)" #__name ";"),                                            \
  GB_METHOD("Contains", "b", __name##_Contains, "(X)" __sign "(Y)" __sign ""),                                                \
  GB_METHOD("Adjust", NULL, __name##_Adjust, "(Left)" __sign "[(Top)" __sign "(Right)" __sign "(Bottom)" __sign "]"),         \
  GB_METHOD("Center", #__pname, __name##_Center, NULL),                                                                       \
                                                                                                                              \
  GB_STATIC_METHOD("Stretch", #__name, __name##_Stretch, "(Width)" __sign "(Height)" __sign "(Frame)" #__name ";[(Alignment)i]"), \
                                                                                                                              \
  GB_END_DECLARE                                                                                                              \
};
