
// 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/>.
//

#include "window.hh"
#include "multiwindow.hh"


bool window::handle_event(my_event& me)
{
  // By default, say we can't handle an event that's in our area.  Typically this is overridden.
  return false;
}


window::window(color bg) : panel(10, 10)  // Panel size overriden quickly with a resize();
{
  this->offset = offset;

  dirty = true;
  layout_dirty = true;
  bgcolor = bg;

  parent = 0;
  
  window_subject_to_layout = true;
}



window::~window()
{
  //printf("deleting window %p\n", this);

  release_events();
  if(parent)
    parent->remove_no_delete(this);
}


void window::layout()
{
  layout_dirty = false;
  dirty        = true;
}


void window::draw_dynamic()
{
}


void window::draw_dirty()
{
  if(dirty)
    dirty = false;
}


void window::draw_point_no_boundscheck(point p, color c)
{
  if(p.y<0 || p.y>=height || p.x<0 || p.x>=width)
    abort();
  parent->draw_point(p+offset, c);
}


color window::get_point_no_boundscheck(point p)
{
  return parent->get_point(p+offset);
}


void window::draw_text(const char* text, color c, int x, int y, int flags)
{
  parent->draw_text(text, c, x + offset.x, y + offset.y, flags);
}

void window::draw_text(const wchar_t* text, color c, int x, int y, int flags)
{
  parent->draw_text(text, c, x + offset.x, y + offset.y, flags);
}

void window::draw_text(const char16_t* text, color c, int x, int y, int flags)
{
  parent->draw_text(text, c, x + offset.x, y + offset.y, flags);
}

void window::draw_text(const char32_t* text, color c, int x, int y, int flags)
{
  parent->draw_text(text, c, x + offset.x, y + offset.y, flags);
}


//
// Passing these to the parent rather than rendering in the window allows
// them to escape the bounding box of the window.  On the other hand, it
// allows them to be rendered correctly for the display type.  Perhaps
// a bounding box needs to be included with the text?
//
void window::draw_multicolored_text(const char* text, int start_color, dt_colors& c, int x, int y, int flags)
{
  parent->draw_multicolored_text(text, start_color, c, x + offset.x, y + offset.y, flags);
}

void window::draw_multicolored_text(const wchar_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
  parent->draw_multicolored_text(text, start_color, c, x + offset.x, y + offset.y, flags);
}

void window::draw_multicolored_text(const char16_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
  parent->draw_multicolored_text(text, start_color, c, x + offset.x, y + offset.y, flags);
}

void window::draw_multicolored_text(const char32_t* text, int start_color, dt_colors& c, int x, int y, int flags)
{
  parent->draw_multicolored_text(text, start_color, c, x + offset.x, y + offset.y, flags);
}


int  window::calculate_text_width(const char* text)
{
  return parent->calculate_text_width(text);
}

int  window::calculate_text_width(const wchar_t* text)
{
  return parent->calculate_text_width(text);
}

int  window::calculate_text_width(const char16_t* text)
{
  return parent->calculate_text_width(text);
}

int  window::calculate_text_width(const char32_t* text)
{
  return parent->calculate_text_width(text);
}


int  window::calculate_text_height(const char* text)
{
  return parent->calculate_text_height(text);
}

int  window::calculate_text_height(const wchar_t* text)
{
  return parent->calculate_text_height(text);
}

int  window::calculate_text_height(const char16_t* text)
{
  return parent->calculate_text_height(text);
}

int  window::calculate_text_height(const char32_t* text)
{
  return parent->calculate_text_height(text);
}


void window::set_text_size(int height_pixels)
{
  parent->set_text_size(height_pixels);
}

void window::draw_svg_from_data(uint8_t* svg_data, int svg_data_length, int offset_x, int offset_y, int draw_width, int draw_height)
{
  parent->draw_svg_from_data(svg_data, svg_data_length, offset_x + offset.x, offset_y + offset.y, draw_width, draw_height);
}

bool window::draw_svg_from_file(const char* filename, int offset_x, int offset_y, int draw_width, int draw_height)
{
  return parent->draw_svg_from_file(filename, offset_x + offset.x, offset_y + offset.y, draw_width, draw_height);
}

bool window::svg_dimensions_from_file(const char* filename, int& width, int& height)
{
  return parent->svg_dimensions_from_file(filename, width, height);
}


void window::draw_line(pointf p0, pointf p1, color c)
{
  parent->draw_line(p0 + pointf(offset), p1 + pointf(offset), c);
}


void window::draw_line_horizontal(point p0i, point p1i, color c)
{
  if(p0i.y<0||p0i.y>=height)
    return;
  
  parent->draw_line_horizontal(p0i + offset, p1i + offset, c);
}


void window::draw_line_vertical(point p0i, point p1i, color c)
{
  if(p0i.x<0||p0i.x>=width)
    return;
  
  parent->draw_line_vertical(p0i + offset, p1i + offset, c);
}


void window::draw_line_known_background(pointf p0, pointf p1, color c, color bg)
{
  parent->draw_line_known_background(p0 + pointf(offset), p1 + pointf(offset), c, bg);
}


void window::draw_circle(pointf p0, float radius, color c)
{
  parent->draw_circle(p0 + pointf(offset), radius, c);
}

void window::draw_triangle(pointf p0, pointf p1, pointf p2, color c)
{
  parent->draw_triangle(p0 + pointf(offset), p1 + pointf(offset), p2 + pointf(offset), c);
}


void window::clear_rect(color c, point corner1, point corner2)
{
  if(corner1.x<0)             corner1.x = 0;
  if(corner1.x>=width)        corner1.x = width-1;
  if(corner1.y<0)             corner1.y = 0;
  if(corner1.y>=height)       corner1.y = height-1;
  
  if(corner2.x<0)             corner2.x = 0;
  if(corner2.x>=width)        corner2.x = width-1;
  if(corner2.y<0)             corner2.y = 0;
  if(corner2.y>=height)       corner2.y = height-1;

  if(corner1.x>corner2.x)
    {
      int t = corner1.x;
      corner1.x = corner2.x;
      corner2.x = t;
    }
  
  if(corner1.y>corner2.y)
    {
      int t = corner1.y;
      corner1.y = corner2.y;
      corner2.y = t;
    }

  parent->clear_rect(c, corner1+offset, corner2+offset);
}


void window::claim_events(window* w)
{
  if(!w)
    w = this;

  if(parent)
    parent->claim_events(w);
}

void window::release_events(window* w)
{
  if(!w)
    w = this;

  if(parent)
    parent->release_events(w);
}


void window::trash_events_until_release()
{
  if(parent)
    parent->trash_events_until_release();
}


void window::resize_quietly(int x, int y, int w, int h)
{
  offset.x = x;
  offset.y = y;
  width = w;
  height = h;
}


void window::resize(int x, int y, int w, int h)
{
  bool size_changed = (offset.x!=x) || (offset.y!=y) || (width!=w) || (height!=h);

  //printf("in window::resize to x=%d, y=%d (%dx%d) changed=%d\n", x, y, w, h, size_changed);
  
  offset.x = x;
  offset.y = y;
  width = w;
  height = h;

  //
  // This needs new layout.  Parent does also.  Above that doesn't; they're only marked dirty.
  //
  if(size_changed)
    {
      layout_dirty = true;
      if(parent)
	parent->mark_layout_dirty();
    }
}


class display* window::get_display()
{
  if(!parent)
    {
      printf("ERROR in window::get_display():  Trying to get the display of a window that has no parent.\n");
      abort();
    }
  
  return parent->get_display();
}


void window::mark_layout_dirty()
{
  layout_dirty=true;
  if(parent)
    parent->mark_dirty();  // Parent doesn't need to be fully redrawn. Just mark it dirty.
}


void window::mark_dirty()
{
  dirty=true;
  if(parent)
    parent->mark_dirty();
}


