
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work 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.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

//

#ifndef PANEL_HH
#define PANEL_HH


#include <stdio.h>

#include "point.hh"
#include "color.hh"
#include "draw_text.hh"

#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))

//
// Drawable region, with drawing routines
//

class panel
{
public:

  // x axis from 0 to width-1, left to right.  w is the width.
  // y axis from 0 to height-1, top to bottom.  h is the height.

  int width;
  int height;
  int pitch;  // The number of pixels per line, greater than or equal to width
  
  panel(int w, int h)        { width = w; height = h; pitch = w;}
  panel(int w, int h, int p) { width = w; height = h; pitch = p;}
  virtual ~panel() {}

  
  bool  in_bounds(point p);
  bool  in_bounds(pointf p) { return in_bounds(point_from_pointf(p)); }
      
  //
  // Point drawing functions
  //
  virtual void   draw_point_no_boundscheck(point p, color c)       = 0;
  virtual color  get_point_no_boundscheck(point p)                 = 0;
  virtual void   draw_panel(panel* p, int offset_x=0, int offset_y=0);
  virtual void   draw_point(point p, color c);
  virtual void   draw_point_antialiased(point p, color c, float frac);
  virtual void   draw_point(pointf p, color c) { draw_point(point_from_pointf(p), c); }
  virtual color  get_point(point p);

  
  //
  // Rect, shape, and line drawing functions.
  //
  virtual void   clear(color c);
  virtual void   clear_rect(color c, point corner1, point corner2) = 0;
  virtual void   draw_line_known_background(pointf p0, pointf p1, color c, color bg) = 0;
  virtual void   draw_line(pointf p0, pointf p1, color c) = 0;
  virtual void   draw_line_vertical(point p0, point p1, color c) = 0;
  virtual void   draw_line_horizontal(point p0, point p1, color c) = 0;
  virtual void   draw_circle(pointf p0, float radius, color c);
  virtual void   draw_triangle(pointf p0, pointf p1, pointf p2, color c);
  
  //
  // Text drawing functions.
  //
  // multicolor text uses ASCII x = 10 (0x0A) and up to indicate a switch to color
  // c[x-10].  Flags are in draw_text.hh
  //
  virtual void   draw_text(const char* text, color c, int x, int y, int flags = 0) = 0;
  virtual void   draw_multicolored_text(const char* text, int start_color, dt_colors& c, int x, int y, int flags = 0) = 0;
  virtual int    calculate_text_width(const char* text) = 0;
  virtual int    calculate_text_height(const char* text) = 0;

  virtual void   draw_text(const wchar_t* text, color c, int x, int y, int flags = 0) = 0;
  virtual void   draw_multicolored_text(const wchar_t* text, int start_color, dt_colors& c, int x, int y, int flags = 0) = 0;
  virtual int    calculate_text_width(const wchar_t* text) = 0;
  virtual int    calculate_text_height(const wchar_t* text) = 0;

  virtual void   draw_text(const char16_t* text, color c, int x, int y, int flags = 0) = 0;
  virtual void   draw_multicolored_text(const char16_t* text, int start_color, dt_colors& c, int x, int y, int flags = 0) = 0;
  virtual int    calculate_text_width(const char16_t* text) = 0;
  virtual int    calculate_text_height(const char16_t* text) = 0;

  virtual void   draw_text(const char32_t* text, color c, int x, int y, int flags = 0) = 0;
  virtual void   draw_multicolored_text(const char32_t* text, int start_color, dt_colors& c, int x, int y, int flags = 0) = 0;
  virtual int    calculate_text_width(const char32_t* text) = 0;
  virtual int    calculate_text_height(const char32_t* text) = 0;

  template <class T> void draw_multicolored_text(T* text, color* c, int x, int y, int flags = 0)
  {
    draw_multicolored_text(text, 0, c, x, y, flags);
  }

  virtual void   set_text_size(int height_pixels) = 0;

  //
  // SVG drawing functions
  //
  virtual void   draw_svg_from_data(uint8_t* svg_data, int svg_data_length, int offset_x=0, int offset_y=0, int draw_width=0, int draw_height=0) = 0;
  
  //
  // Special functions
  //
  void   draw_rect_with_curved_edges(color c, int x, int y, int w, int h, int circle_width); 
  void   draw_color_with_panel_green_as_alpha(color c, panel* p, int offset_x, int offset_y);
};


//
// Removed this so we can refer SVG drawing down to the lowest level, allowing the display to accelerate it.
//
//panel* new_panel_from_svg(const char* filename, int max_width, int max_height, color bg);


inline void panel::draw_color_with_panel_green_as_alpha(color c, panel* p, int offset_x, int offset_y)
{
  for(int j=0; j<p->height; j++)
    for(int i=0; i<p->width; i++)
      {
	draw_point_antialiased(point(i+offset_x, j+offset_y), c, green_float(p->get_point_no_boundscheck(point(i, j))));
      }
}

inline void panel::draw_panel(panel* p, int offset_x, int offset_y)
{
  for(int j=0; j<p->height; j++)
    for(int i=0; i<p->width; i++)
      draw_point(point(i+offset_x, j+offset_y), p->get_point_no_boundscheck(point(i, j)));
}

inline void panel::clear(color c)
{
  clear_rect(c, point(0,0), point(width,height));
}

inline bool panel::in_bounds(point p)
{  
  return p.x>=0 && p.x<width && p.y>=0 && p.y<height;
}

inline void panel::draw_point(point p, color c)
{
  if(in_bounds(p))
    draw_point_no_boundscheck(p, c);
}


inline void panel::draw_point_antialiased(point p, color c, float frac)
{
  if(in_bounds(p))
    {
      color bg = get_point_no_boundscheck(p);
      color cc = scale(c, bg, sqrt(frac));
      draw_point_no_boundscheck(p, cc);
    }
}



inline color panel::get_point(point p)
{
  color c = BLACK;

  if(in_bounds(p))
    c = get_point_no_boundscheck(p);

  return c;
}





#endif
