/*
* field.cc
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#include "field.h"
#include "font.h"
#include "utils.h"
#include "input.h"
#include "utils.h"
#include "tcl_interp.h"
#include "log.h"
#include "console.h"

#include <string>
#include <sstream>
#include <list>
#include <cstdlib>

using namespace std;
extern int lmb;
extern int mousex, mouseyy;
char get_typed_char ();
extern tcl_interp interpreter;
extern char BUFFER [];

int field::handle_input () {

  widget::handle_input ();

  // locate mouse in field
  if (lmb) {
    if (lmb_clicked == 0) {
			lmb_clicked = 1;
      if (hittest (mousex, mouseyy)) {
				calc_cursor ();
				edited |= 0;
        widget::focus = this;
      } else {
        if (widget::focus == this) {
					defocus (this);
          lmb_clicked = 0;
					return 1;
        }
      }
    }
  } else {
    lmb_clicked = 0;
  }

  if (focus) { // can edit text

    char c = get_typed_char ();

    if (c != 0) {

      edited = 1;

      if (mode == pushback) 
				text.push_back (c); 
			else 
				text.insert (text.begin() + cursor, c);

      ++len;
      ++last;
      ++cursor;

      calc_cursor ();

			if (typing_lsnr) typing_lsnr->typing (*this);

    }

		if (keypressed (SDLK_ESCAPE)) { // abort editing
			focus = 0;
			defocus (this);
			return 1;
		} else
    if (keypressed (SDLK_RETURN)) { // finished editing
			focus = 0;
      call_listener ();
			defocus(this);
      return 1;
    }
    else if (keypressedd (SDLK_LEFT)) { // move cursor left
			mode = insert;
      if (--cursor < 0) cursor = 0;
      calc_cursor ();
    } else if (keypressedd (SDLK_RIGHT)) { // move cursor right
			mode = insert;
      if (++cursor >= len) {
        cursor = len;
        mode = pushback;
      }
      calc_cursor ();
    } else if (keypressedd (SDLK_BACKSPACE)) { // delete
      if (cursor) {
        --cursor;
        --len;
        if (--last < 0) {
	  			last = 0;
					len = 0;
	  			mode = pushback;
				}
				string::iterator e = text.begin () + cursor;
				if (e != text.end()) text.erase (e);
				calc_cursor ();
				if (typing_lsnr) typing_lsnr->typing (*this);
        edited = 1;
      }
    }
  }
  return focus;

}

void field::set_text (const string& txt, int _edited) {
	edited = _edited;
  text = txt;
  len = text.length ();
	if (len == 0) {
		last = 0;
		cursor = 0;
	} else {
  	last = len - 1;
		cursor = len;
	}
	mode = pushback;
  calc_cursor ();
}

void field::set_text (int i, int _edited) {
  sprintf (BUFFER, "%d", i);
  set_text (BUFFER, _edited);
}

void field::set_text (float f, int _edited) {
  sprintf (BUFFER, fmts[fmt], f);
  set_text (BUFFER, _edited);
}

void field::calc_cursor () {
  string cstr (text.substr (0, cursor));
  offset = get_char_width (cstr);// - 2;
  update ();
}

void field::update () {
  const box<int>& e = extents;
  set_extents (e.left, e.bottom, e.left + get_char_width (text) + 2 * fnt.charwidth.avg, e.bottom + fnt.charheight.max);
}

void field::draw_cursor (int x, int y) {
  int cx = x + offset;
  glBegin (GL_LINES);
    glVertex2i (cx, y);
    glVertex2i (cx, y + fnt.charheight.max);
  glEnd ();
  /*static int pts [4] = {0};
  pts[0]=cx;pts[1]=y;
  pts[2]=cx;pts[3]=y+fnt.charheight.max;
  glVertexPointer (2, GL_INT, 0, pts);
  glDrawArrays (GL_LINES, 0, 2);*/
}

void field::draw () {
  draw_and_fill_bbox ();
  int x = posx, y = posy;
  x += fnt.charwidth.avg;
  if (focus) draw_cursor (x, y);
  draw_string (text, x, y);
}

void field::init () {
  lmb_clicked = 0;
  focus = 0;
  edited = 0;
	change_lsnr = 0;
	typing_lsnr = 0;
	type = "";
	expr = 1;
	fmt = dec3;
}

field::field () {
	set_text ("");
	init ();
}

field::field (int x, int y, const string& txt) {
  set_text (txt);
  set_pos (x, y);
	init ();
}

field::operator int() const {
  return (atoi (text.c_str()));
}

field::operator float() const {
  return ((float) atof (text.c_str()));
}

field::operator double() const {
	return atof (text.c_str());
}

field::operator short() const {
	return ((short) atoi(text.c_str()));
}

int field::hittest (int x, int y) {

  const box<int>& e = extents;

  if (hover) { // in the box

    focus = 1; // so got focus

    int n = text.length ();

    if (n == 0) { 
      cursor = 0;
      mode = pushback;
      return focus;
    }

    // locate cursor in text
		//
    int l = e.left + fnt.charwidth.avg, r;
    if (x <= l) {
      cursor = 0;
      mode = insert;
      return focus;
    }

    cursor = 0;
		int r0 = l;
    for (int i = 0; i < n; ++i) {
      r = r0 + get_char_width (text.substr (0, i + 1)); 
      if (inrange (l, x, r)) { // found!
				int rn = r - fnt.spacing.ch;
        int xl = x - l, xr = rn - x;
        if (xl <= xr) cursor = i; else cursor = i + 1;
        if (cursor > last) {
					cursor = len;
					mode = pushback; 
				} else mode = insert;
        return focus;
      }
			l = r;
    }
    cursor = len;
    mode = pushback;
    return focus;
  }

  focus = 0;
  return focus;

}

void field::call_listener () {
	if (edited) {
		if (expr) {
			sprintf (BUFFER, "eval-expr %s %s", text.c_str(), type.c_str());
			interpreter (BUFFER);
			if (interpreter.result_status == 0) {
				set_text (interpreter.result, 1);
				if (change_lsnr) change_lsnr->changed (*this);
			}
		} else {
			if (change_lsnr) change_lsnr->changed (*this);
		}
		edited = 0;
	}
}
