
// 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 LOCAL_DISPLAY_HH
#define LOCAL_DISPLAY_HH

#include "panel.hh"
#include "multiwindow_multipane.hh"
#include "event_listener.hh"
#include "input_event_handler.hh"
#include "global_beep.hh"
#include "draw_text.hh"
#include "display.hh"
#include "all_svg.hh"

extern volatile bool enable_display;

// There may be multiple displays.

//
// display_panel handles drawing to each of the buffers.  There are three
// display_panels per display, all accessible from the graphics
// hardware.  This allows them to be flipped out for double buffering.
//
class local_display_panel : public panel
{
public:

  uint16_t* buff;
  int       buffsize;
  uint32_t  fb_id;
  uint32_t  dumb_handle;
  int       dri_fd;

  local_display_panel(int w, int h, int pitch, uint16_t* buff, int buffsize, uint32_t dumb_handle, uint32_t fb_id, int dri_fd);
  
  virtual ~local_display_panel();

  virtual void   draw_point_no_boundscheck(point p, color c);
  virtual color  get_point_no_boundscheck(point p);

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

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

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

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

  virtual void   set_text_size(int height_pixels);

  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);

  virtual void   clear_rect(color c, point corner1, point corner2);
  virtual void   draw_line_known_background(pointf p0, pointf p1, color c, color bg);
  virtual void   draw_line(pointf p0, pointf p1, color c);
  virtual void   draw_line_vertical(point p0, point p1, color c);
  virtual void   draw_line_horizontal(point p0, point p1, color c);
};


//
// This handles the three buffers for each local_display, and flips the active
// one when asked for double-buffering.  You draw into the drawing panel.
// The active panel is currenly being or soon to be displayed.  The
// flip_pending panel is possibly still being displayed but soon will not
// be.
//
// There is also a static_panel in the window that can be copied directly
// over to the local_display for quick erasure of all dynamic drawing while
// keeping all static items.
//
class local_display : public display
{
  static int       dri_fd;
  static uint32_t  crtc_id;
  static uint32_t  connector_id;
  static volatile bool    lock_dri_fd;

  static volatile bool is_drm_master;
  
  int pitch;
  
  int              cursor_x;
  int              cursor_y;
  bool             cursor_is_on;

  struct timeval   last_mouse_event_time;  // For making the mouse cursor disappear when not used

  int              draw_to_static;

  
public:

  class touchscreen_calibration* touchscreen_calibration_window;
  bool             touchscreen_calibrated;
  bool             touchscreen_calibration_in_progress;

  float            touchscreen_x_offset;
  float            touchscreen_y_offset;
  float            touchscreen_x_gain;
  float            touchscreen_y_gain;
  
  struct timeval   last_event_time;        // For determining which display should be active

  virtual void layout();

  virtual bool need_fast_display() { return touchscreen_calibration_in_progress; }

  virtual void touch_recognized_beep() {  global_touch_recognized_beep();  }
  virtual void entry_accepted_beep()   {  global_entry_accepted_beep();    }
  virtual void entry_rejected_beep()   {  global_entry_rejected_beep();    }

  virtual void beginDrawToStatic()     { draw_to_static++; }
  virtual void endDrawToStatic()       { draw_to_static--; }
  virtual void clearDrawToStatic()     { draw_to_static=0; }

  backing_panel*   static_panel;

  //
  // Event handling
  //
  input_event_handler*  ieh;
    
  local_display_panel* active_panel;       // Currently being sent to the local_display.  Drawing here is immediately visible.
  local_display_panel* drawing_panel;      // Panels to draw to, that then get flipped to with page_flip().
  local_display_panel* flip_pending_panel; // Panels that have been drawn to and awaiting becoming active.  Don't draw here.
  // Also have a static_panel of type backing_panel from the window class
  
  // Note that if drawing is too fast, then page_flip() may get called twice before a vblank interval.  In that case,
  // the second update is thrown out and never displayed.

  void           page_flip();
  void           copy_static_to_drawing();
  virtual bool   draw();

  //virtual void pane_change();
  
  virtual void   draw_cursor(local_display_panel* p);

  virtual void   get_and_process_events();

  //
  // Point, rect, and line drawing functions.
  //
  virtual void   draw_point_no_boundscheck(point p, color c);
  virtual color  get_point_no_boundscheck(point p);

  //
  // 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);
  virtual void   draw_multicolored_text(const char* text, int start_color, dt_colors& c, int x, int y, int flags = 0);
  virtual int    calculate_text_width(const char* text);
  virtual int    calculate_text_height(const char* text);

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

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

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

  virtual void   set_text_size(int height_pixels);

  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);

  virtual void   clear_rect(color c, point corner1, point corner2);
  virtual void   draw_line_known_background(pointf p0, pointf p1, color c, color bg);
  virtual void   draw_line(pointf p0, pointf p1, color c);
  virtual void   draw_line_vertical(point p0, point p1, color c);
  virtual void   draw_line_horizontal(point p0, point p1, color c);
  virtual void   draw_circle(pointf p0, float radius, color c);
  virtual void   draw_triangle(pointf p0, pointf p1, pointf p2, color c);

  virtual void   draw_graph_data(uint32_t     color,
				 int          num_points,
				 float        x_start,
				 float        x_step,
				 float        x_at_left,
				 float        x_at_right,
				 float        y_at_bottom,
				 float        y_at_top,
				 float        x_offset,
				 float        y_offset,
				 float        width,
				 float        height,
				 data_point*  y_data);

  virtual void draw_ephemeral_alert(const char* text, int text_height, color c, color bg);

  static local_display_panel* create_local_display_panel(int dri_fd, int width, int height);
  
  static void check_for_new_displays(class displays* dspys, int num_panes_per_display);
  
  virtual ~local_display();

  // New local_displays are created by check_for_new_displays().  Only it calls this constructor.
private:
    local_display(int w, int h, local_display_panel* active, local_display_panel* drawing, local_display_panel* flip_pending, uint32_t crtc_id, int dri_fd, int num_panes);

};


inline void local_display::draw_point_no_boundscheck(point p, color c)
{
  if(draw_to_static)
    static_panel->draw_point_no_boundscheck(p, c);
  else
    drawing_panel->draw_point_no_boundscheck(p, c);
}

inline color local_display::get_point_no_boundscheck(point p)
{
  if(draw_to_static)
    return static_panel->get_point_no_boundscheck(p);
  else
    return drawing_panel->get_point_no_boundscheck(p);
}

inline void local_display::clear_rect(color c, point corner1, point corner2)
{
  //printf("in local_display::clear_rect() x=%d,y=%d (%dx%d)\n", corner1.x, corner1.y, corner2.x-corner1.x, corner2.y-corner1.y);
  
  if(draw_to_static)
    static_panel->clear_rect(c, corner1, corner2);
  else
    drawing_panel->clear_rect(c, corner1, corner2);
}



inline void local_display::draw_text(const char* text, color c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_text(text, c, x, y, flags);
 else
   drawing_panel->draw_text(text, c, x, y, flags);
}


inline void local_display::draw_text(const wchar_t* text, color c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_text(text, c, x, y, flags);
 else
   drawing_panel->draw_text(text, c, x, y, flags);
}


inline void local_display::draw_text(const char16_t* text, color c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_text(text, c, x, y, flags);
 else
   drawing_panel->draw_text(text, c, x, y, flags);
}


inline void local_display::draw_text(const char32_t* text, color c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_text(text, c, x, y, flags);
 else
   drawing_panel->draw_text(text, c, x, y, flags);
}


inline void local_display::draw_multicolored_text(const char* text, int start_color, dt_colors& c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
 else   
   drawing_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
}


inline void local_display::draw_multicolored_text(const wchar_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
 else   
   drawing_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
}


inline void local_display::draw_multicolored_text(const char16_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
 else   
   drawing_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
}


inline void local_display::draw_multicolored_text(const char32_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
 if(draw_to_static)
   static_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
 else   
   drawing_panel->draw_multicolored_text(text, start_color, c, x, y, flags);
}



inline int  local_display::calculate_text_width(const char* text)
{
  return global_calculate_text_width(text);
}

inline int  local_display::calculate_text_width(const wchar_t* text)
{
  return global_calculate_text_width(text);
}

inline int  local_display::calculate_text_width(const char16_t* text)
{
  return global_calculate_text_width(text);
}

inline int  local_display::calculate_text_width(const char32_t* text)
{
  return global_calculate_text_width(text);
}


inline int  local_display::calculate_text_height(const char* text)
{
  return global_calculate_text_height(text);
}

inline int  local_display::calculate_text_height(const wchar_t* text)
{
  return global_calculate_text_height(text);
}

inline int  local_display::calculate_text_height(const char16_t* text)
{
  return global_calculate_text_height(text);
}

inline int  local_display::calculate_text_height(const char32_t* text)
{
  return global_calculate_text_height(text);
}


inline void local_display::set_text_size(int height_pixels)
{
  global_set_text_size(height_pixels);
}


inline void local_display::draw_svg_from_data(uint8_t* svg_data, int svg_data_length, int offset_x, int offset_y, int draw_width, int draw_height)
{
 if(draw_to_static)
   static_panel->draw_svg_from_data(svg_data, svg_data_length, offset_x, offset_y, draw_width, draw_height);
 else   
   drawing_panel->draw_svg_from_data(svg_data, svg_data_length, offset_x, offset_y, draw_width, draw_height);
}


inline void local_display::draw_line_known_background(pointf p0, pointf p1, color c, color bg)
{
  if(draw_to_static)
    static_panel->draw_line_known_background(p0, p1, c, bg);
  else
    drawing_panel->draw_line_known_background(p0, p1, c, bg);    
}

inline void local_display::draw_line(pointf p0, pointf p1, color c)
{
  if(draw_to_static)
    static_panel->draw_line(p0, p1, c);    
  else
    drawing_panel->draw_line(p0, p1, c);        
}

inline void local_display::draw_line_vertical(point p0, point p1, color c)
{
  if(draw_to_static)
    static_panel->draw_line_vertical(p0, p1, c);
  else
    drawing_panel->draw_line_vertical(p0, p1, c);
}

inline void local_display::draw_line_horizontal(point p0, point p1, color c)
{
  if(draw_to_static)
    static_panel->draw_line_horizontal(p0, p1, c);
  else
    drawing_panel->draw_line_horizontal(p0, p1, c);
}

inline void local_display::draw_circle(pointf p0, float radius, color c)
{
  if(draw_to_static)
    static_panel->draw_circle(p0, radius, c);
  else
    drawing_panel->draw_circle(p0, radius, c);
}


inline  void local_display::draw_triangle(pointf p0, pointf p1, pointf p2, color c)
{
  if(draw_to_static)
    static_panel->draw_triangle(p0, p1, p2, c);
  else
    drawing_panel->draw_triangle(p0, p1, p2, c);
}


#endif
