
// 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 "menu.hh"
#include "displays.hh"

extern bool draw_hotspots;


void menu::draw_dynamic()
{
  if(draw_hotspots)
    {
      int tagnum = 0;
      for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
	{
	  menu_item* m = le->data();

	  if(!m->mw && !m->sf)
	    {
	      tagnum++;
	      continue;  // disabled menu item, has no window or action
	    }
	  
	  if(tagnum!=selected_num)
	    {
	      int left   = m->offset.x - tag_left_margin + 1;
	      int top    = m->offset.y + 1;
	      int width  = m->width - 2;
	      int height = m->height - 2;
	      
	      point tl( left,            top);
	      point tr( left + width-1,  top);
	      point bl( left,            top+height-1);
	      point br( left + width-1,  top+height-1);
	      draw_line(tl, tr, GREEN);
	      draw_line(tl, bl, GREEN);
	      draw_line(tr, br, GREEN);
	      draw_line(bl, br, GREEN);
	    }
	  
	  tagnum++;
	}
    }

  int tagnum = 0;
  for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
    {
      menu_item* m = le->data();
      
      if(tagnum==selected_num && m->mw)
	m->mw->draw_dynamic();
      
      tagnum++;
    }
}


void menu::draw_dirty()
{
  //printf("In menu::draw_dirty()  dirty=%d\n", dirty);

  if(redraw_all)
    {
      int item_width = tag_right_offset - tag_left_offset + left_margin;
      int circle_width = item_width/20;
  
      clear(bgcolor);

      draw_rect_with_curved_edges(GROUPCOLOR, left_margin, top_margin, item_width, tag_bottom_offset-top_margin, circle_width);
    }

  int tagnum = 0;
  for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
    {
      menu_item* m = le->data();

      if(tagnum==selected_num)
	{
	  //if( (m->mw && m->mw->layout_dirty) || m->layout_dirty)
	  //layout();
	  if(m->mw && m->mw->dirty)
	    m->mw->draw_dirty();
	}

      if(redraw_all)
	draw_tag(m, tagnum==selected_num);

      tagnum++;
    }

  redraw_all = false;
  dirty = false;
}

void menu::select_a_child(int num)
{
  if(selected_num!=num)
    {
      selected_num = num;
      mark_layout_dirty();
    }
}

void menu::deselect_a_child()
{
  if(selected_num!=-1)
    {
      selected_num = -1;
      mark_layout_dirty();
    }
}

bool menu::handle_event(my_event& me)
{
  //printf("in menu::handle_event()\n");
  //print_event(me);

  int xoff = 0;
  int yoff = 0;
  window* p = this;
  while(p)
    {
      xoff += p->offset.x;
      yoff += p->offset.y;
      p = p->parent;
    }
  
  int tagnum = 0;
  for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
    {
      menu_item* m = le->data();
      
      int xtag = me.c[0].x - xoff - m->offset.x;
      int ytag = me.c[0].y - yoff - m->offset.y;
      
      if(xtag>=0 && xtag<m->width && ytag>=0 && ytag<m->height)
	{
	  if(tagnum!=selected_num)
	    {
	      display* disp = get_display();
	      disp->touch_recognized_beep();
	      if(m->mw)
		{
		  select_a_child(tagnum);
		}
	      else if(m->sf)
		{
		  m->sf(tagnum, this, m, m->data);
		}
	    }
	  trash_events_until_release();
	  return true;
	}
      
      if(!m->mw && !m->sf)
	{
	  // Disabled menu
	  tagnum++;
	  continue;
	}
      
      if(tagnum==selected_num)
	{
	  int x = me.c[0].x - xoff - m->mw->offset.x;
	  int y = me.c[0].y - yoff - m->mw->offset.y;
	  
	  if((x>0 && x<m->mw->width && y>0 && y<m->mw->height) || me.type == EVENT_KEY_PRESS || me.type == EVENT_KEY_RELEASE || me.type == EVENT_KEY_HOLD)
	    {
	      bool handled = m->mw->handle_event(me);
	      if(handled)
		{
		  //printf("Window at screen offset (%d,%d) claimed event.\n", xoff + w->offset.x, yoff + w->offset.y);
		  return true;
		}
	    }
	}
      
      tagnum++;
    }
  
  return false;
}


int menu::tag_largest_width(int font_height)
{
  set_text_size(font_height);
  int max_width = 0;
  
  for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
    {
      menu_item* m = le->data();
      int width = calculate_text_width(m->name);
      if(width>max_width)
	max_width = width;
    }
      
  return max_width;
}

void menu::draw_tag(menu_item* m, bool selected)
{
  color c = TAGCOLOR;

  if(selected)
    {
      int item_width = tag_right_offset - tag_left_offset;
      int circle_width = m->height/2;
      draw_rect_with_curved_edges(c,
				  left_margin + selection_margin,
				  m->offset.y,
				  item_width - selection_margin,
				  m->height,
				  circle_width);
      
      float triangle_height = m->height - 1 - circle_width*1414/4000;
      float triangle_left   = item_width - 1 - circle_width*1414/8000;
      float triangle_right  = mw_left_edge;
      float triangle_top    = m->offset.y + circle_width*1414/8000;

      draw_triangle( pointf(triangle_left, triangle_top),
		     pointf(triangle_left, triangle_top + triangle_height),
		     pointf(triangle_right, triangle_top + triangle_height/2),
		     c );
    }
  
  set_text_size(tag_font_height);

  color tc = ( selected      ?  SELECTEDCOLOR      :
	       (m->sf)       ?  ACTIONTEXTCOLOR    :
	       (m->mw)       ?  TEXTCOLOR          :
	       /**/             DISABLEDTEXTCOLOR  );
  
  draw_text(m->name, tc, m->offset.x, m->offset.y+m->height/2, DRAW_TEXT_X_LEFT|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
}


int menu::get_selected_y_offset()
{
  return selected_y_offset;
}


static int limit(int v, int minval, int maxval)
{
  if(v<minval)
    return minval;
  if(v>maxval)
    return maxval;

  return v;
}

//
// Lays out menu items on left edge of screen, taking into account their sizes.
// resizes subselection_area to fill rest.  This assumes that the menu is a normal
// type of menu.  This is overridden for other menu types, including final
// selections.
//
void menu::layout()
{
  //printf("enerting menu::do_layout()\n");

  int num_windows_for_layout = mil.numInList();  

  if(num_windows_for_layout==0)
    return;

  float designed_height = 1080.0;
  float designed_width  = 1920.0;
  display* d = get_display();
  float ratio_x = d->width / designed_width;
  float ratio_y = d->height / designed_height;
  float ratio = (ratio_x<ratio_y) ? ratio_x : ratio_y;
  
  int base_margin       = limit(designed_height * 3/100, 10, 20) * ratio;

  // If we're laying out the menu above also, then reset the top_margin.  Otherwise, keep it.
  // This makes sure the menu doesn't jump around if items are subtracted from it or added to it.

  if(parent->layout_dirty)
    top_margin            = base_margin;

  bottom_margin         = base_margin;  
  left_margin           = 0;//base_margin;
  right_margin          = base_margin;
  mw_margin             = base_margin;
  tag_top_margin        = base_margin/2;
  tag_bottom_margin     = base_margin/2;
  tag_left_margin       = base_margin;
  tag_right_margin      = 2*base_margin;
  selection_margin      = tag_left_margin/2;

  tag_left_offset       = left_margin + tag_left_margin;

 redo:
  
  item_area_height      = designed_height*ratio - top_margin - bottom_margin;
  item_max_spacing      = num_windows_for_layout ? item_area_height / num_windows_for_layout : 0;
  
  tag_font_height       = limit(designed_height * 4/100, 10, item_max_spacing * 3/4) * ratio;

  tag_spacing           = limit(tag_font_height * 4/3, 14, item_max_spacing);

  tag_bottom_offset     = tag_spacing * num_windows_for_layout + top_margin + tag_top_margin + tag_bottom_margin;

  //
  // This makes sure that multiple layers of menus
  // have the pointer for the first not be below the second-level
  // menu.
  //
  int parent_selected_y_offset = parent->get_selected_y_offset();
  if(parent_selected_y_offset + tag_spacing > tag_bottom_offset)
    {
      top_margin += tag_spacing;
      goto redo;
    }

  max_tag_width = tag_largest_width(tag_font_height);
  tag_right_offset = tag_right_margin + max_tag_width + tag_left_offset;  
  mw_left_edge = tag_right_offset + mw_margin;

  //
  // Place each tag, and place the selected multiwindow
  //
  int x_position = tag_left_offset;
  int y_position = top_margin + tag_top_margin;
  int num = 0;
  for(ListElement<menu_item>* le=mil.first(); le; le=le->next())
    {
      menu_item* item = le->data();
      item->resize(x_position, y_position, tag_right_offset - tag_left_offset, tag_spacing);

      if(num==selected_num)
	{
	  selected_y_offset = y_position + tag_spacing/2;

	  if(item->mw)
	    {
	      item->mw->resize(mw_left_edge, 0, width-mw_left_edge, height);
	      item->mw->layout();
	    }
	}
      
      y_position += tag_spacing;
      num++;
    }

  layout_dirty = false;
  dirty = true;
  redraw_all = true;
}


menu::menu(color bg)
  : multiwindow(bg)
{
  GROUPCOLOR        = GREY4;
  TAGCOLOR          = GREY6;
  TEXTCOLOR         = WHITE;
  SELECTEDCOLOR     = GREY9;
  ACTIONTEXTCOLOR   = GREEN7;
  DISABLEDTEXTCOLOR = GREY5;

  redraw_all = true;
  
  selected_num = -1;
  tag_font_height = 16;
  selected_y_offset = 0;
}

menu::~menu()
{
  //printf("deleting menu\n");
}
