
// 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 "number_box.hh"
#include "multiwindow.hh"
#include "numeric_entry.hh"
#include "language_support.hh"
#include "selection.hh"
#include "language_support.hh"

extern bool draw_hotspots;

void number_box::layout()
{
  //printf("int number_box::layout();\n");

  circle_width = height/20;
  font_height  = height/10;

  top_margin           = height/20;
  bottom_margin        = top_margin;  
  left_margin          = width/20;
  right_margin         = left_margin;

  title_bottom_margin  = top_margin;
  between_x_margin     = left_margin;
  between_y_margin     = top_margin*0.7;

  number_x_margin      = left_margin/4;
  number_y_margin      = top_margin/8;

  set_text_size(font_height);

  text_max_width = 0;
  if(!scalar)
    {
      for(int i=0; i<num_units_to_display; i++)
	{
	  const number_box_unit* nbu = &unit_list[i];
	  
	  int width = calculate_text_width(nbu->name);
	  text_max_width = max(text_max_width, width);
	}
    }

  number_width = calculate_text_width("-8.888888888");

  text_max_width += number_width + between_x_margin;

  int title_width = calculate_text_width(name);

  text_max_width = max(text_max_width, title_width);

  width = text_max_width + left_margin + right_margin;

  if(scalar)
    number_width = text_max_width;
  
  height  = top_margin + (num_units_to_display+1) * font_height + title_bottom_margin + (num_units_to_display-1) * between_y_margin + bottom_margin;

  layout_dirty = false;
  dirty = true;
}


void number_box::draw_dirty()
{
  clear(BG_COLOR);

  //printf("circle_width=%d\n", circle_width);
  //printf("font_height=%d\n", font_height);
  draw_rect_with_curved_edges(GROUPCOLOR,
			      0,
			      0,
			      width,
			      height,
			      circle_width);

  set_text_size(font_height);

  draw_text(name, TITLECOLOR, width/2, top_margin, DRAW_TEXT_X_CENTER|DRAW_TEXT_Y_TOP|DRAW_TEXT_ROTATE_0);

  int num = 0;
  for(int i=0; i<num_units_to_display; i++)
    {
      const number_box_unit* nbu = &unit_list[i];

      //if(as->type == type)
	{
	  double v = nbu->scale * *value + nbu->offset;

	  int num_left_x    = left_margin;
	  int num_right_x   = num_left_x + number_width;
	  int num_top_y     = top_margin + font_height + title_bottom_margin + num * (font_height + between_y_margin);
	  int num_bottom_y  = num_top_y + font_height;

	  clear_rect(BLACK,
		     point(num_left_x  - number_x_margin, (num_bottom_y+num_top_y)/2 - (font_height/2 + between_y_margin*0.3) ),
		     point(num_right_x + number_x_margin, (num_bottom_y+num_top_y)/2 + (font_height/2 + between_y_margin*0.3) ));

	  char tmp[200];
	  //	  sprintf(tmp, "%f", v);
	  float2text(v, tmp);
	  //printf("number_box: %f -> %s.  scale=%s\n", v, tmp, nbu->name);
		  
	  draw_text(tmp, NUM_COLOR, num_right_x, (num_bottom_y+num_top_y)/2, DRAW_TEXT_X_RIGHT|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);

	  if(!scalar)
	    draw_text(nbu->name, TEXT_COLOR, num_right_x + between_x_margin, (num_bottom_y+num_top_y)/2, DRAW_TEXT_X_LEFT|DRAW_TEXT_Y_CENTER|DRAW_TEXT_ROTATE_0);
	  
	  num++;
	}
    }

  dirty = false;
}


void number_box::draw_dynamic()
{
  if(draw_hotspots || highlight_count)
    {
      if(highlight_count)
	highlight_count--;

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



number_box::number_box(multilingua&            name,
		       double&                 value,
		       double                  min_value,
		       double                  max_value,
		       multilingua&            min_text,
		       multilingua&            max_text,		       
		       bool                    integer_only,
		       const number_box_unit*  unit_list,
		       int                     num_units,
		       int                     num_units_to_display,
		       color                   textcolor,
		       color                   fg,
		       color                   bg)
  : window(bg)
{
  //printf("axl name is %s\n", unit_list->name);

  this->name = name;

  this->integer_only = integer_only;

  if(integer_only)
    value = int(floor(value+0.5));
  
  const static number_box_unit scalar_units[1] =
    {
      { U"",   1.0,   0.0  },
    };

  this->num_units            = num_units;
  this->num_units_to_display = num_units_to_display;

  if(unit_list)
    {
      this->unit_list = unit_list;
      scalar = false;
    }
  else
    {
      this->unit_list            = scalar_units;
      this->num_units            = 1;
      this->num_units_to_display = 1;
      scalar = true;
    }
    
  this->value          = &value;
  this->min_value      = min_value;
  this->max_value      = max_value;

  this->min_text       = min_text;
  this->max_text       = max_text;
  
  BG_COLOR             = bg;
  FG_COLOR             = fg;
  TEXT_COLOR           = textcolor;
  NUM_COLOR            = GREY9;
  GROUPCOLOR           = GREY4;
  TITLECOLOR           = WHITE;
    
  highlight_count      = 0;
}



number_box::~number_box()
{
}


bool get_number(void* data, const char* text, int num_selected)
{
  number_box* nb = (number_box*)data;

  return nb->get_number(text, num_selected);
}


//
// Allow a little leeway for rounding error in floating point numbers.
//
bool number_box::number_ok(double value)
{
  double delta = 1e-6 * (max_value - min_value);
  return (value>=min_value-delta) && (value<=max_value+delta);
}


bool number_box::get_number(const char* text, int num_selected)
{
  if(text[0]=='-' && text[1]=='.' && text[2]==0)
    return false;

  if(text[0]=='-' && text[1]==0)
    return false;

  if(text[0]=='.' && text[1]==0)
    return false;

  // If the text is empty, return with value unchanged
  if(text[0]==0)
    return true;

  double val = atof(text);

  int num_total = (num_units>4) ? 4 : num_units;

  //
  // ####################################################  ERROR
  //
  // Number box had a linkin with scales for an axis.  Should not be part of number box.  Fix later.
  //

  int num = num_selected+(num_total-4);
  const number_box_unit* nbu = &unit_list[num];
  double v = (val - nbu->offset ) / nbu->scale;

  if(number_ok(v))
    {
      *value = v;
      int returnval = 0;
      send_event(this, *value, returnval);
      mark_layout_dirty();
      return true;
    }
  else
    {
      unalingua out_of_range_text;

      char minrangetxt[50];
      char maxrangetxt[50];

      float2text(min_value*nbu->scale + nbu->offset, minrangetxt);
      float2text(max_value*nbu->scale + nbu->offset, maxrangetxt);
    
      out_of_range_text.set_text(min_text);
      out_of_range_text.append_text(minrangetxt);
      out_of_range_text.append_text(nbu->name);
      out_of_range_text.append_text(max_text);
      out_of_range_text.append_text(maxrangetxt);
      out_of_range_text.append_text(nbu->name);
      
      const char32_t* question = out_of_range_text;
      const char32_t* answer[1];
      answer[0] = U"\x2713"; // Checkmark

      color w = WHITE;
      
      int result = ask_selection(get_display(),
				 question,
				 1,
				 answer,
				 GREY4, // BG_COLOR,
				 RED,   // TITLE_COLOR,
				 GREY6, // BUTTON_COLOR,
				 &w);

      result = result;  // avoid unused variable error
      
      return false;
    }
}


bool number_box::handle_event(my_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 x = me.c[0].x - xoff;
  int y = me.c[0].y - yoff;

  if(x<0 || x>=width || y<0 || y>=height)
    return false;
  
  if(me.type == EVENT_TOUCH)
    {
      display* disp = get_display();
      disp->touch_recognized_beep();
      //claim_events();
      trash_events_until_release();

      unalingua names[4];

      for(int i=0; i<4; i++)
	names[i] = "";
      
      int num = 0;
      for(int i=0; i<num_units; i++)
	{
	  const number_box_unit* nbu = &unit_list[i];
	  names[num++] = nbu->name;
	  
	  if(num==4)
	    break;
	}

      
      bool positive_only = (min_value>=0.0) && (max_value>=0.0);

      const char32_t* title = name;

      display* d = get_display();

      int default_scale_number  = 0;
      int default_return_number = (4-num >= 0) ? 4-num : 0;
      double default_value      = *value * unit_list[default_scale_number].scale;
            
      // This deletes itself when done, so no need to get the return.
	new numeric_entry(d,
			  title,
			  (num>=4) ? (const char32_t*)names[num-4] : 0,
			  (num>=3) ? (const char32_t*)names[num-3] : 0,
			  (num>=2) ? (const char32_t*)names[num-2] : 0,
			  scalar   ? U"\x23CE" : (const char32_t*)names[num-1],
			  integer_only,
			  positive_only,
			  ::get_number,
			  (void*)this,
			  default_return_number,
			  default_value);
	d->event_loop();
    }

  return true;
}
