/*
* widget.h
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/


#ifndef __widget
#define __widget

#include "box.h"
#include "color.h"
#include "point.h"
#include "viewwin.h"

#include <vector>
#include <list>
#include <map>
#include <string>
#include <fstream>

#define LISTEN(w,v) w.set_listener(v);
#define MOVE(w) w.set_moveable(1);
#define HANDLEINPUT(h) if (h.handle_input()) return 1;

#define MILLION 1000000
#define MILLIONF float(MILLION)

extern int lmb;
extern int mousex, mouseyy;

struct ui;
struct widget;

struct voiddata {
  void* data;
};

struct move_listener : voiddata {
	virtual void moved () = 0;
  virtual void movestart () {}
  virtual void endmove () {}
};

struct nullt {
  int null;
  nullt () : null(0) {}
};

#define MAKE_MOVE_LISTENER(name,var) struct name : move_listener { void movestart (); void moved (); void endmove (); } ; name var;
#define MOVED(scope,name) void scope::name::moved ()
#define MOVESTART(scope,name) void scope::name::movestart ()
#define MOVEEND(scope,name) void scope::name::endmove ()

struct mover {
  widget* w; // widget moved
  int* pmb; // mouse button that moves widget
  int mb_clicked; // is that mouse button clicked
  int move; // 1 - widget is moving, 0 - not
  int prevx, prevy; // previous mouse position
  mover (widget* ww, int* pb = &lmb);
  int handle_input ();
};

typedef std::vector<widget *>::size_type size_vw;

struct widget { // ui widget

	int id;

  std::string name; 
	void set_name (const std::string& _name);
	void nametothis ();
	virtual void set_text (const std::string& txt) {set_name (txt);}

	// bounding box
	//
  box<int> extents; // extents of bounding box around this widget
	void set_extents (int l, int b, int r, int t);
	void set_extents (const box<int>& ext) { extents = ext;}
	int size; // for making square widgets
	void set_size (int _sz) {
		size = _sz;
		set_extents (posx, posy, posx + size, posy + size);
	}
	void set_width_height (int w, int h) {
		set_extents (posx, posy, posx + w, posy + h);
	}

	static int bb[]; // bounding box data for OpenGL drawing
	void draw_bbox ();
	void fill_bbox (float a = 0.1f, int g = 1);
	void draw_and_fill_bbox ();

  // color of all widgets (except checkbuttons)
  static float R, G, B;
  color clr;
	virtual void set_color (unsigned char ur, unsigned char ug, unsigned char ub);
	virtual void set_color (float r, float g, float b);
	void set_color (const color& c) {set_color (c.r, c.g, c.b);}

  int posx, posy; // position in screen space
	virtual void set_pos (int x, int y);

	// helper to handle move with mouse
	int moveable; // moveable?
	mover movr; // handles move
	move_listener* movlis;
	virtual void set_moveable (int m, int mc = 0, int* pmb = &lmb); 
	void move (int dx, int dy, int mc = 1);
  void setpos2 (widget& w);

  int visible; // visible?

  int enabled; // enabled?
	void enable ();
	void disable ();

	static int HOVER; // hover over any widget in DIN?

  int hover; // hovering?

  // children
  //
  std::vector<widget*> children;
  int numchil;
	void add_child (widget *w);
	void remove_child (widget* w);
  void swapchild (widget* w1, widget* w2);

#ifdef __WIDGET_MOVE__
  int dg;
	void draw_guides ();
#endif

	static widget *focus, *next_focus;

	enum {all, only_children};
	virtual void show ();
	void hide (int what = all);

  widget (int l = 0, int b = 0, int r = 0, int t = 0);
  virtual ~widget ();

  virtual int handle_input ();
  virtual inline void draw () {
	#ifdef __WIDGET_MOVE__
		if (movr.move || dg) draw_guides ();
	#endif
		glColor3f (clr.r, clr.g, clr.b);
	}

  virtual void update () {}

  static void advance_right (int& x, widget& w, int spc = 10) {
    const box<int>& e = w.extents;
    x += (e.width + spc);
  }

  virtual void load (std::ifstream& file);
  virtual void save (std::ofstream& file);

};

// globals
//

void widget_load (const std::string& name, widget** pw, int n);
void widget_load (const std::string& fname, std::vector<widget*>& vec);
void widget_save (const std::string& name, widget** pw, int n);
void widget_save (const std::string& fname, std::vector<widget*>& vec);

// listeners for widgets
template <typename W> struct change_listener {
  virtual void changed (W& w) = 0;
};

// for tying lmb to a widget
struct is_lmb_t {
	widget* tie;
	is_lmb_t ();
	int operator() (widget* w = 0);
	void clear (widget* _tie = 0);
};

extern is_lmb_t is_lmb;

void set_focus (widget* w);
void defocus ();
void defocus (widget* w);

void makehier (widget** wa);
void makehier (widget** wa, int n);
void makefam (widget** wa);
void makefam (widget* parent, widget** children, int n);
void makefam (widget** mem, int n);

#endif
