//
// "Gleam" scheme box drawing routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2018 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
//     https://www.fltk.org/bugs.php
//
// These box types provide a sort of Clearlooks Glossy scheme
// for FLTK.
//
// Copyright 2001-2005 by Colin Jones.
//
// Modified 2012-2013 by Edmanuel Torres
// This is a new version of the fl_gleam. The gradients are on the top
// and the bottom, the text area looks like in the classic FLTK way.
//

#include <FL/Fl.H>
#include <FL/fl_draw.H>

/*
  Implementation notes:

  All box types have 2 pixel wide borders, there is no distinction
  of "thin" box types, hence Fl::box_dx() = Fl::box_dy() = 2,
  Fl::box_dw() = Fl::box_dh() = 4 for all box types.

  All coordinates of function calls (x, y, w, h) describe the full
  box size with borders, i.e. the original box coordinates.
  The interior (background) of the box is drawn with reduced size.
  This is adjusted only in function shade_rect_top_bottom().

  Note: There is one pixel "leaking" out of the box border at each
  corner which is currently not drawn in a full box redraw.
*/

// Standard box drawing code

static void gleam_color(Fl_Color c) {
  Fl::set_box_color(c);
}

/* Draw the shaded background of the box.

  This is called before the box frame (border) is drawn.
  The interior of the box (shaded background) should be inset by two
  pixels, hence width and height should be reduced by 4 (border size).
  Since not all pixels of the border are drawn (border lines are shorter
  by one or two pixels on each side) setting other offsets (+1, -2 instead
  of +2, -4) would leak the box background color instead of the parent
  widget's background color.
  Note: the original implementation was maybe equivalent to using +1 and -2
  for offset and width, respectively instead of +2 and -4. It is not clear
  whether this was intended or by accident.
*/

static void shade_rect_top_bottom(int x, int y, int w, int h, Fl_Color fg1, Fl_Color fg2, float th) {
  // calculate background size w/o borders
  x += 2; y += 2; w -= 4; h -= 4;
  // draw the shiny background using maximum limits
  int h_top    = ((h/2) < (20) ? (h/2) : (20)); // min(h/2, 20);
  int h_bottom = ((h/6) < (15) ? (h/6) : (15)); // min(h/6, 15);
  int h_flat = h - h_top - h_bottom;
  float step_size_top = h_top > 1 ? (0.999f/float(h_top)) : 1;
  float step_size_bottom = h_bottom > 1 ? (0.999f/float(h_bottom)) : 1;
  // draw the gradient at the top of the widget
  float k = 1;
  for (int i = 0; i < h_top; i++, k -= step_size_top) {
    gleam_color(fl_color_average(fl_color_average(fg1, fg2, th), fg1, k));
    fl_xyline(x, y+i, x+w-1);
  }

  // draw a "flat" rectangle in the middle area of the box
  gleam_color(fg1);
  fl_rectf(x, y + h_top, w, h_flat);

  // draw the gradient at the bottom of the widget
  k = 1;
  for (int i = 0; i < h_bottom; i++, k -= step_size_bottom) {
    gleam_color(fl_color_average(fg1, fl_color_average(fg1, fg2, th), k));
    fl_xyline(x, y+h_top+h_flat+i, x+w-1);
  }
}

// See shade_rect_top_bottom()
static void shade_rect_top_bottom_up(int x, int y, int w, int h, Fl_Color bc, float th) {
  shade_rect_top_bottom(x, y, w, h, bc, FL_WHITE, th);
}

// See shade_rect_top_bottom()
static void shade_rect_top_bottom_down(int x, int y, int w, int h, Fl_Color bc, float th) {
  shade_rect_top_bottom(x, y, w, h, bc, FL_BLACK, th);
}

// Draw box borders. Color arguments:
// - fg1: outer border line (left, right, top, bottom)
// - fg2: inner border line (left, right)
// - lc : inner border line (top, bottom)

static void frame_rect(int x, int y, int w, int h, Fl_Color fg1, Fl_Color fg2, Fl_Color lc) {
  // outer border line:
  gleam_color(fg1);
  fl_xyline(x+1,   y,     x+w-2);   // top
  fl_yxline(x+w-1, y+1,   y+h-2);   // right
  fl_xyline(x+1,   y+h-1, x+w-2);   // bottom
  fl_yxline(x,     y+1,   y+h-2);   // left

  // inner border line (left, right):
  gleam_color(fg2);
  fl_yxline(x+1,   y+2,   y+h-3);   // left
  fl_yxline(x+w-2, y+2,   y+h-3);   // right

  // inner border line (top, bottom):
  gleam_color(lc);
  fl_xyline(x+2,   y+1,   x+w-3);   // top
  fl_xyline(x+2,   y+h-2, x+w-3);   // bottom
}

// Draw box borders with different colors (up/down effect).

static void frame_rect_up(int x, int y, int w, int h, Fl_Color bc, Fl_Color lc, float th1, float th2) {
  frame_rect(x, y, w, h, fl_color_average(fl_darker(bc), FL_BLACK, th1), fl_color_average(bc, FL_WHITE, th2), lc);
}

static void frame_rect_down(int x, int y, int w, int h, Fl_Color bc, Fl_Color lc, float th1, float th2) {
  frame_rect(x,y,w,h,fl_color_average(bc, FL_WHITE, th1), fl_color_average(FL_BLACK, bc, th2), lc);
}

// Draw the different box types. These are the actual box drawing functions.

static void up_frame(int x, int y, int w, int h, Fl_Color c) {
  frame_rect_up(x, y, w, h, c, fl_color_average(c, FL_WHITE, .25f), .55f, .05f);
}

static void up_box(int x, int y, int w, int h, Fl_Color c) {
  shade_rect_top_bottom_up(x, y, w, h, c, .15f);
  frame_rect_up(x, y, w, h, c, fl_color_average(c, FL_WHITE, .05f), .15f, .05f);
}

static void thin_up_box(int x, int y, int w, int h, Fl_Color c) {
  shade_rect_top_bottom_up(x, y, w, h, c, .25f);
  frame_rect_up(x, y, w, h, c, fl_color_average(c, FL_WHITE, .45f), .25f, .15f);
}

static void down_frame(int x, int y, int w, int h, Fl_Color c) {
  frame_rect_down(x, y, w, h, fl_darker(c), fl_darker(c), .25f, .95f);
}

static void down_box(int x, int y, int w, int h, Fl_Color c) {
  shade_rect_top_bottom_down(x, y, w, h, c, .65f);
  frame_rect_down(x, y, w, h, c, fl_color_average(c, FL_BLACK, .05f), .05f, .95f);
}

static void thin_down_box(int x, int y, int w, int h, Fl_Color c) {
  shade_rect_top_bottom_down(x, y, w, h, c, .85f);
  frame_rect_down(x, y, w, h, c, fl_color_average(c, FL_BLACK, .45f), .35f, 0.85f);
}

extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*, Fl_Box_Draw_Focus_F* =NULL);

Fl_Boxtype fl_define_FL_GLEAM_UP_BOX() {
  fl_internal_boxtype(_FL_GLEAM_UP_BOX, up_box);
  fl_internal_boxtype(_FL_GLEAM_DOWN_BOX, down_box);
  fl_internal_boxtype(_FL_GLEAM_UP_FRAME, up_frame);
  fl_internal_boxtype(_FL_GLEAM_DOWN_FRAME, down_frame);
  fl_internal_boxtype(_FL_GLEAM_THIN_UP_BOX, thin_up_box);
  fl_internal_boxtype(_FL_GLEAM_THIN_DOWN_BOX, thin_down_box);
  fl_internal_boxtype(_FL_GLEAM_ROUND_UP_BOX, up_box);
  fl_internal_boxtype(_FL_GLEAM_ROUND_DOWN_BOX, down_box);
  return _FL_GLEAM_UP_BOX;
}
