
// 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 <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <displays.hh>


#include "document_window.hh"

extern bool draw_hotspots;

document_window::document_window(const char* directory, const char* document_name, color bg) : multiwindow(bg)
{
  active             = false;
  current_page       = 1;
  last_current_page  = 0;
  zoom               = 1.0;
  last_zoom          = 0.0;
  center_x           = width/2;
  center_y           = height/2;
  draw_grid          = false;
  draw_page_number   = false;
  page_absolute      = false;
  svg_width          = 0;
  svg_height         = 0;
  
  sprintf(this->name, "%.99s", document_name);
  sprintf(this->directory, "%.199s", directory);

  delayed_redraw_count = 0;
}


document_window::~document_window()
{
}


/*
void document_window::draw_dirty()
{
  if(!dirty)
    return;
  dirty = false;
  
  if(!svg_panel || current_page != last_current_page || zoom != last_zoom)
    {
      if(svg_panel)
	delete svg_panel;

      char filename[1000];
      sprintf(filename, "%.499s/%.99s_%03d.svg", full_directory, name, current_page);

      svg_panel = new_panel_from_svg(filename, int(zoom*width), int(zoom*height), WHITE);
      
      if(!svg_panel)
	{
	  char temp[1000];
	  sprintf(temp, "Page %d of document \"%.400s\" wasn't successfully rendered.", current_page, name);
	  set_text_size(40);
	  draw_text(temp, RED, width/2, height/2, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
	  return;
	}

      last_current_page = current_page;
      last_zoom = zoom;
    }

  int offset_x = center_x - svg_panel->width/2;
  int offset_y = center_y - svg_panel->height/2;

  clear(bgcolor);
  draw_panel(svg_panel, offset_x, offset_y);
}
*/


void document_window::draw_dirty()
{
  bool success;
  int offset_x;
  int offset_y;

  if(!dirty)
    return;
  dirty = false;

  parent->clear_rect(parent->bgcolor, point(0,0), point(parent->width, parent->height));
  
  char filename[1000];
  sprintf(filename, "%.499s/%.99s_%03d.svg", full_directory, name, current_page);

  int max_width  = zoom * width;
  int max_height = zoom * height;

  if(svg_width==0)
    {
      success = svg_dimensions_from_file(filename, svg_width, svg_height);

      if(!success || svg_width==0 || svg_height==0)
	goto error;
    }

  if(float(svg_width)/svg_height > float(max_width)/max_height)
    {
      // Object is wider than panel in aspect.  Scale object to match width.

      draw_width  = max_width;
      draw_height = svg_height * max_width / svg_width;
    }
  else
    {
      // Object is narrower than panel in aspect.  Scale object to match height.
	  
      draw_width  = svg_width * max_height / svg_height;
      draw_height = max_height;
    }
  
  offset_x = center_x - draw_width/2;
  offset_y = center_y - draw_height/2;

  clear(bgcolor);
  
  clear_rect(WHITE, point(offset_x, offset_y), point(offset_x+draw_width-1, offset_y+draw_height-1));

  success = draw_svg_from_file(filename, offset_x, offset_y, draw_width, draw_height);

  if(!success)
    goto error;

  last_current_page = current_page;
  last_zoom = zoom;

  parent->clear_rect(parent->bgcolor, point(0,0), point(10, parent->height));
  parent->clear_rect(parent->bgcolor, point(0,0), point(parent->width, 10));
  parent->clear_rect(parent->bgcolor, point(parent->width-10,0), point(parent->width, parent->height));
  parent->clear_rect(parent->bgcolor, point(0,parent->height-10), point(parent->width, parent->height));

  return;

 error:
  
  char temp[1000];
  sprintf(temp, "Page %d of document \"%.400s\" wasn't successfully rendered.", current_page, name);
  set_text_size(40);
  draw_text(temp, RED, width/2, height/2, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
  return;

}




void document_window::layout()
{      
  center_x = width/2;
  center_y = height/2;

  //
  // This is done here so error messages can be printed to the screen.
  //
  if(!active)
    {
      sprintf(full_directory, "%.199s/%.99s", directory, name);
      
      //
      // Find how many pages are out there
      //
      
      DIR* dp = opendir(full_directory);
      
      if(!dp)
	{
	  char temp[200];
	  sprintf(temp,  "Document \"%s\" isn't available.", name);
	  set_text_size(40);
	  draw_text(temp, RED, width/2, height/2, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
	  return;
	}
      
      for(num_pages=0;;num_pages++)
	{
	  bool got_match = false;
	  rewinddir(dp);
	  char pagename[200];
	  sprintf(pagename, "%s_%03d.svg", name, num_pages+1);
	  
	  for(;;)
	    {
	      
	      dirent *de = readdir(dp);
	      
	      if(!de)
		break;
	      
	      if(!strcmp(pagename, de->d_name))
		{
		  got_match = true;
		  break;
		}
	      
	    }
	  
	  if(!got_match)
	    break;
	}
      
      if(!num_pages)
	{
	  char temp[200];
	  sprintf(temp, "Document \"%s\" has no available pages.", name);
	  set_text_size(40);
	  draw_text(temp, RED, width/2, height/2, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
	  return;
	}

      closedir(dp);
    }
  
  active = true;

  layout_dirty = false;
  dirty = true;
}



bool document_window::handle_event(my_event& me)
{
  display* disp = get_display();

  bool handled = multiwindow::handle_event(me);

  //printf("got event in document_window.cc\n");
  //::print_event(me);
  
  if(handled)
    return true;

  if(me.num_touches>4)
    return false;
  
  int xoff = 0;
  int yoff = 0;
  window* p = this;
  while(p)
    {
      xoff += p->offset.x;
      yoff += p->offset.y;
      p = p->parent;
    }

  if(me.type == EVENT_WHEEL)
    {

      if(!draw_grid)
	{
	  selected_zoom = zoom;
	  selected_x = me.c[0].x - xoff;
	  selected_y = me.c[0].y - yoff;
	  selected_center_x = center_x;
	  selected_center_y = center_y;
	}
      
      draw_grid = true;

      zoom = pow(1.1, me.count) * zoom; 

      if(zoom<0.5)
	zoom = 0.5;
      if(zoom>4.0)
	zoom = 4.0;

      center_x = selected_x - (selected_x-selected_center_x) * zoom / selected_zoom;
      center_y = selected_y - (selected_y-selected_center_y) * zoom / selected_zoom;

      delayed_redraw_count = 100;

      return true;
    }

  if(me.source_mouse)
    {
      int x = me.c[0].x - xoff;
      int y = me.c[0].y - yoff;

      if(me.mouse_buttons_pressed==MOUSE_BUTTON_LEFT)
	{
	  if(me.type == EVENT_TOUCH)
	    {
	      claim_events();
	      draw_page_number = true;
	      if(x>width/2)
		{
		  current_page++;
		  if(current_page>num_pages)
		    {
		      //disp->entry_rejected_beep();
		      current_page=num_pages;
		    }
		}
	      else
		{
		  current_page--;
		  if(current_page<1)
		    {
		      //disp->entry_rejected_beep();
		      current_page=1;
		    }
		}
	      return true;
	    }
	  else if(me.type == EVENT_MOVE)
	    {
	      current_page = (x-width/10) * num_pages / (8*width/10) + 1;
	      if(current_page<1)
		current_page = 1;
	      if(current_page>num_pages)
		current_page = num_pages;
	      return true;
	    }
	  else if(me.type==EVENT_RELEASE)
	    {
	      disp->touch_recognized_beep();
	      release_events();
	      draw_page_number = false;
	      mark_dirty();
	      return true;
	    }     
	}
      else if(me.mouse_buttons_pressed==MOUSE_BUTTON_MIDDLE)
	{
	  if(me.type == EVENT_HOLD && me.count>=5)
	    {
	      // 2-finger autoscale
	      disp->touch_recognized_beep();
	      draw_grid = false;
	      zoom = 1.0;
	      center_x = width/2;
	      center_y = height/2;
	      mark_layout_dirty();
	      release_events();
	      trash_events_until_release();
	      return true;
	    }
	  else if(me.type == EVENT_TOUCH)
	    {
	      //disp->touch_recognized_beep();
	      claim_events();
	      draw_grid = true;
		  
	      selected_zoom = zoom;
	      selected_x = x;
	      selected_y = y;
	      selected_center_x = center_x;
	      selected_center_y = center_y;
	    }
	  else if(me.type==EVENT_MOVE)
	    {
	      center_x = (x-selected_x) + selected_center_x;
	      center_y = (y-selected_y) + selected_center_y;

	      if(center_x + draw_width/2 < width/10)
		center_x = width/10 - draw_width/2;
	      else if(center_x - draw_width/2 > width * 9/10)
		center_x = width * 9/10 + draw_width/2;

	      if(center_y + draw_height/2 < height/10)
		center_y = height/10 - draw_height/2;
	      else if(center_y - draw_height/2 > height * 9/10)
		center_y = height * 9/10 + draw_height/2;
	    }
	  else if(me.type==EVENT_RELEASE)
	    {
	      disp->touch_recognized_beep();
	      // Put selected_x and selected_y at new mouse loation
	      center_x = (x-selected_x) + selected_center_x;
	      center_y = (y-selected_y) + selected_center_y;

	      if(center_x + draw_width/2 < width/10)
		center_x = width/10 - draw_width/2;
	      else if(center_x - draw_width/2 > width * 9/10)
		center_x = width * 9/10 + draw_width/2;

	      if(center_y + draw_height/2 < height/10)
		center_y = height/10 - draw_height/2;
	      else if(center_y - draw_height/2 > height * 9/10)
		center_y = height * 9/10 + draw_height/2;

	      release_events();
	      draw_grid = false;
	      mark_dirty();
	    }
	}
      else
	{
	  // multiple buttons pressed simultaneously, or right mouse button
	  draw_grid = false;
	  draw_page_number = false;
	  disp->entry_rejected_beep();
	  release_events();
	  trash_events_until_release();
	  return true;
	}

      // If it's a middle button only, fall through to event handling like it's a touch
    }


  else if(me.num_touches==2)
    {
      int x0 = me.c[0].x - xoff;
      int y0 = me.c[0].y - yoff;
      int x1 = me.c[1].x - xoff;
      int y1 = me.c[1].y - yoff;
      int dx = x1-x0;
      int dy = y1-y0;
      int x  = (x1+x0)/2;
      int y  = (y1+y0)/2;

      if(me.type == EVENT_HOLD && me.count>=5)
	{
	  // 2-finger autoscale
	  disp->touch_recognized_beep();
	  draw_grid = false;
	  zoom = 1.0;
	  center_x = width/2;
	  center_y = height/2;
	  mark_layout_dirty();
	  release_events();
	  trash_events_until_release();
	  return true;
	}
      else if(me.type == EVENT_TOUCH)
	{
	  //disp->touch_recognized_beep();
	  claim_events();
	  draw_grid = true;

	  selected_zoom = zoom;
	  selected_x = (x0+x1)/2;
	  selected_y = (y0+y1)/2;
	  selected_h = sqrt(dx*dx+dy*dy);
	  selected_center_x = center_x;
	  selected_center_y = center_y;
	}
      else if(me.type==EVENT_MOVE)
	{
	  double h = sqrt(dx*dx+dy*dy);
	  zoom = selected_zoom * h / selected_h;
	  if(zoom<0.5)
	    zoom = 0.5;
	  if(zoom>4.0)
	     zoom = 4.0;
	  center_x = x - (selected_x-selected_center_x) * zoom / selected_zoom;
	  center_y = y - (selected_y-selected_center_y) * zoom / selected_zoom;
	}
      else if(me.type==EVENT_RELEASE)
	{
	  disp->touch_recognized_beep();
	  double h = sqrt(dx*dx+dy*dy);
	  zoom = selected_zoom * h / selected_h;
	  if(zoom<0.5)
	    zoom = 0.5;
	  if(zoom>4.0)
	     zoom = 4.0;
	  center_x = x - (selected_x-selected_center_x) * zoom / selected_zoom;
	  center_y = y - (selected_y-selected_center_y) * zoom / selected_zoom;
	  release_events();
	  draw_grid = false;
	  mark_dirty();
	}
    }


  else if(me.num_touches==1)
    {
      //int x0 = me.c[0].x - xoff;
      //int x1 = me.c[1].x - xoff;
      //int x2 = me.c[2].x - xoff;
      //int x = (x0+x1+x2)/3;
      //int y0 = me.c[0].y - yoff;
      //int y1 = me.c[1].y - yoff;
      //int y2 = me.c[2].y - yoff;
      //int y = (y0+y1+y2)/3;

      int x = me.c[0].x - xoff;

      //printf("got 1-finger touch in document_window.cc\n");
      //::print_event(me);
      
      if(me.type == EVENT_TOUCH)
	{
	  //disp->touch_recognized_beep();
	  claim_events();
	  draw_page_number = true;
	  page_absolute = false;

	  if(x>width/2)
	    {
	      current_page++;
	      if(current_page>num_pages)
		{
		  //disp->entry_rejected_beep();
		  current_page=num_pages;
		}
	    }
	  else
	    {
	      current_page--;
	      if(current_page<1)
		{
		  //disp->entry_rejected_beep();
		  current_page=1;
		}
	    }
	}
      else if(me.type==EVENT_MOVE)
	{
	  current_page = (x-width/10) * num_pages / (8*width/10) + 1;
	  
	  if(current_page<1)
	    current_page = 1;
	  if(current_page>num_pages)
	    current_page = num_pages;
	}
      else if(me.type==EVENT_RELEASE)
	{
	  disp->touch_recognized_beep();
	  release_events();
	  draw_page_number = false;
	  mark_dirty();
	}
    }
  else
    {
      return false;
    }

  return true;
}



void document_window::draw_dynamic()
{
  multiwindow::draw_dynamic();

  if(draw_grid)
    {
      double zoomdelta = zoom / selected_zoom;
      int x0 = center_x - zoomdelta * draw_width/2;
      int y0 = center_y - zoomdelta * draw_height/2;
      int x1 = center_x + zoomdelta * draw_width/2;
      int y1 = center_y + zoomdelta * draw_height/2;

      int num_steps = 20;
      
      for(int i=0; i<=num_steps; i++)
	{
	  int x = x0 + (x1-x0)*i/num_steps;
	  draw_line(point(x, y0), point(x, y1), GREEN);
	}
      
      for(int i=0; i<=num_steps; i++)
	{
	  int y = y0 + (y1-y0)*i/num_steps;
	  draw_line(point(x0, y), point(x1, y), GREEN);
	}
    }


  if(draw_page_number)
    {
      static int textw = 0;
      static int texth;
      
      char temp[100];
      sprintf(temp, "Page %d", current_page);

      set_text_size(40);

      if(!textw)
	{
	  textw = calculate_text_width("Page 1000");
	  texth = calculate_text_height("Page 1000");
	}
      
      clear_rect(GREY3, point(width-2*textw, 0), point(width-1, 2*texth));
      
      draw_text(temp, GREEN, width-textw, texth, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
    }

  if(draw_hotspots)
    {
      point tl(       0,        0);
      point tr( width-1,        0);
      point bl(       0, height-1);
      point br( width-1, height-1);
      draw_line(tl, tr, GREEN);
      draw_line(tl, bl, GREEN);
      draw_line(tr, br, GREEN);
      draw_line(bl, br, GREEN);
    }

  if(delayed_redraw_count)
    {
      delayed_redraw_count--;

      if(!delayed_redraw_count)
	{
	  display* disp = get_display();
	  disp->touch_recognized_beep();
	  draw_grid = false;
	  mark_dirty();
	}
    }


  if(draw_page_number||draw_hotspots||draw_grid)
    {
      display* primary_display = this->get_display();

      float designed_height = 1080.0;
      float designed_width  = 1920.0;
      float ratio_x = primary_display->width / designed_width;
      float ratio_y = primary_display->height / designed_height;
      float ratio = (ratio_x<ratio_y) ? ratio_x : ratio_y;
      
      clear_rect(GREY3, point(2, 2), point(140*ratio-2, 140*ratio-2));

      set_text_size(25 * ratio);

      draw_text("Press Here",
		GREEN,
		70 * ratio,
		30 * ratio,
		DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);

      draw_text("to Return",
		GREEN,
		70 * ratio,
		64 * ratio,
		DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);

      draw_text("to Menu",
		GREEN,
		65 * ratio,
		98 * ratio,
		DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);

      //int textw = calculate_text_width("Press Here");
      //int texth = calculate_text_height("Press Here");

      //clear_rect(GREY3, point(10, 10), point(10+1.5*textw, 10+3*texth));
      
      //draw_text("Press Here", GREEN, 10+0.75*textw, 10, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_TOP|DRAW_TEXT_ROTATE_0);
      //draw_text("For Menu",   GREEN, 10+0.75*textw, 10+1.5*texth, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_TOP|DRAW_TEXT_ROTATE_0);
    }
}
