#pragma once
#include "Common.h"
#include "Widget.h"
#include "Property.h"
BEGIN_NAMESPACE


// Generally this doesn't end up being too useful right now
// What does it buy you?
// The property is the thing being interacted with, not the widget class
template <typename T>
class AbstractValueWidget : public Widget
{
// TODO, define like this instead:
//   template <typename T>
//   class AbstractValueWidget : public Inherit<Widget, AbstractValueWidget<T> >
// problem is that it forces a type of constructor we can't support as we need a parameter to set the value reference with
// we can only have constructors with the same parameters as the parent type supports.
public:
	AbstractValueWidget(Widget* a_parent, AbstractProperty<T>& a_value)
		: Widget(a_parent, 0, LP_Vertical, 0, 0, 10, 10), value(a_value)
	{
		connect(value.valueChanged, this, &AbstractValueWidget<T>::refresh);
	}
	void refresh()
	{
		repaint();
	}
	// In this case, we store and expose an AbstractProperty since the value held here is used
	// to reflect some state from somewhere else in the program
	AbstractProperty<T>& value;
};


namespace details
{
	extern Property<bool>   g_unassignedBool;
	extern Property<String> g_unassignedString;
}


// checked and value are now synonym for the same value
// A bit of overhead in having the extra reference
class CheckableWidget : public AbstractValueWidget<bool>
{
public:
	CheckableWidget(Object* a_parent, const char* a_name);
	CheckableWidget(Widget* a_parent, AbstractProperty<bool>& a_value);
  // = details::g_unassignedBool);
	//void setProperty(AbstractProperty<bool>& a_value);
	AbstractProperty<bool>& checked;
};


// If use this, it breaks various code-completion / tagging systems
#define CLASS(T, ParentT) \
    class T : public Inherit<ParentT, T>


class HBox : public Inherit<Widget, HBox>
{
public:
	HBox(Object* a_parent, const char* a_name) : HBox(ObjectPtrCast<Widget>(a_parent)) {}
  HBox(Widget* a_parent);

  // works with code-completion
  bool blah(int a, int b) { return true; }
  int test;

  // doesn't work with code-completion
  START_MEMBERS
    REFLECT_MEMBER_FUNCTION(blah,  "Test member function reflected")
    REFLECT_MEMBER(test,           "Test int reflected")
  END_MEMBERS
};


class VBox : public Inherit<Widget, VBox>
{
public:
	VBox(Object* a_parent, const char* a_name) : VBox(ObjectPtrCast<Widget>(a_parent)) {}
	VBox(Widget* a_parent);

  int test2;
  START_MEMBERS
    ADD_MEMBER(int, test,    "Test int member")
  END_MEMBERS
};


// Very simple helper class to create a simple grid
class Grid : public HBox
{
public:
  Grid(Widget* parent, int columns) : HBox(parent)
  {
    v = new VBox*[columns];
    for (int i = 0; i < columns; i++)
      v[i] = new VBox(this);
  }
  ~Grid()
  {
    delete[] v;
  }
  VBox **v;
};


class GroupBoxSpacer : public Widget
{
public:
	GroupBoxSpacer(Widget* a_parent);
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
};


class GroupBox : public AbstractValueWidget<String>
{
public:
	GroupBox(Widget* a_parent, AbstractProperty<String>& a_title);
	AbstractProperty<String>& title;
	Property<bool> disabled;
protected:
	void paintEvent(PaintEvent& a_event);
private:
	GroupBoxSpacer m_titleSpace;
};


class HSpace : public Widget
{
public:
	HSpace(Widget* a_parent);
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
};


class VSpace : public Widget
{
public:
	VSpace(Widget* a_parent);
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
};


class Label : public AbstractValueWidget<String>
{
public:
	Label(Widget* a_parent, AbstractProperty<String>& a_text);
	Property<bool> disabled;

	// In this case, we store and expose an AbstractProperty since the value held here is used
	// to reflect some state from somewhere else in the program - eg we can bind the text to display from somewhere
	AbstractProperty<String>&  text;
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
};


class Button : public Widget
{
public:
	Button(Widget* a_parent, const char* a_text = "");
	
	Action<bool> activated;

	Property<String> text;

  // TODO: Perhaps these should move to a base class - they seem pretty common to most widgets
  Property<bool> disabled;
	Property<bool> hasFocus;
	Property<bool> isDefault;

protected:
	void refresh();
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);

private:
	bool m_mouseDownInButton;
	bool m_mouseOverButton;
};


class CheckBox : public CheckableWidget
{
public:
	CheckBox(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text = "");
	virtual void toggle();
	Property<String> text;
	Property<bool> disabled;
	Property<bool> hasFocus;
	Property<bool> isDefault;

  void onTextChanged(String a_text);
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
	bool m_mouseDownInButton;
	bool m_mouseOverButton;
};


class RadioButton : public CheckableWidget
{
public:
	RadioButton(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text = "");
	virtual void toggle();
	Property<String> text;
	Property<bool> disabled;
	Property<bool> hasFocus;
	Property<bool> isDefault;
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
	bool m_mouseDownInButton;
	bool m_mouseOverButton;
};


class LineEdit : public AbstractValueWidget<String>
{
public:
	LineEdit(Widget* a_parent, AbstractProperty<String>& a_text);
	AbstractProperty<String>& text;
	Property<bool> disabled;
	Property<bool> hasFocus;
protected:
  void refresh2() { repaint(); }
	void sizeOptions(SizeOptions& a_sizeOptions);
	void timerEvent(TimerEvent& a_event);
	void keyEvent(KeyEvent& a_event);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
	bool	m_mouseOver;
	TimerId m_timerId;
	bool    m_carrotOn;
	size_t  m_carrotPosition;
};


// At the moment, it is a value from 0 to 65535 which can be returned as a float from 0.0 to 1.0
// Perhaps could become something like:  num / denom,  or  a min / max thing so has a bias but
// can be returned as a 0->1 value. Idea could be pixels with a minX and maxX of the slider bar
// and a value which is the pixel position along it.
class Range
{
public:
  // These are all as if in pixel values
  void setMin(uint32_t m) { min = m; }
  void setMax(uint32_t m) { max = m; }
  void setValue(uint32_t v) { val = v; }
  uint32_t getValue() { return val; }
  
  // Return the value as if it was between another range, a_min -> a_max
  uint32_t getTranslatedValue(uint32_t a_min, uint32_t a_max)
  {
    int x = int(*this);
    x = (x * (a_max - a_min)) >> 16;
    return x + a_min;
  }

  // Normalized values from 0 -> 65535  and  0.0 -> 1.0 respectively
	operator int() const   { return ((val - min) << 16) / (max - min); }
	operator float() const { int x = int(*this); return float(x) / 65535.0f; }

private:
  uint32_t val = 0;
  uint32_t min = 0;
  uint32_t max = 1UL<<31UL;
};


class SliderR : public AbstractValueWidget<Range>
{
public:
	SliderR(Widget* a_parent, AbstractProperty<Range>& a_sliderValue);

  // TODO: min/max value the slider value is scaled to

	Property<bool> disabled;
	Property<bool> hasFocus;
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
	bool  m_mouseOverSliderHandle;
	bool  m_mouseDownInSliderHandle;
	int sliderWidth();
	Rectangle sliderHandleRectangle();
};


class Slider : public AbstractValueWidget<int>
{
public:
	Slider(Widget* a_parent, AbstractProperty<int>& a_sliderValue);

  // TODO: min/max value the slider value is scaled to

	Property<bool> disabled;
	Property<bool> hasFocus;
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
	bool  m_mouseOverSliderHandle;
	bool  m_mouseDownInSliderHandle;
	int sliderWidth();
	Rectangle sliderHandleRectangle();
};


class GridSlider
{
public:
  GridSlider(Grid* g, AbstractProperty<int>& value, const char* name)
  {
    label = name;
    new Label(g->v[0], label);
    new Slider(g->v[1], value);
  }
  Property<String> label;
};


// TODO: inherit from a 'RangeControl' base class which is a value between a given min/max values
class ScrollBar : public AbstractValueWidget<int>
{
public:
	ScrollBar(Widget* a_parent, AbstractProperty<int>& a_scrollValue);

  Property<int> minValue; // when the scrollbar is at the top, value will be minValue
	Property<int> maxValue; // when the scrollbar is at the bottom, value will be maxValue

  Property<bool> proportional; // if true, the scrollbar handle is sized according to pageSize 
	Property<int> pageSize; // amount to change value by when clicking in gutter.
	Property<int> lineSize; // amount to inc/dec value by when pressing the up/down buttons

	Property<bool> disabled;
	Property<bool> hasFocus;
protected:
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
  int getScrollBarHandleHeight();
	Rectangle getUpHandleRectangle();
	Rectangle getDownHandleRectangle();
	Rectangle getScrollHandleRectangle();
  bool m_onHandle = false;
  //int m_moveStartY = -1;
  int m_moveStartValue = -1;
};


// If MVC, this is the M
class ItemList
{
public:
  ItemList(AbstractProperty<int>& a_value)
    : currentIndex(a_value)
  {
  }

  AbstractProperty<int>& currentIndex;
	Property<String> text;
	Action<int> changed;

	void addItem(String a_itemText)
  {
    int value = currentIndex.value();
    m_items.push_back(a_itemText);
    if (m_items.size() >= (size_t)value)
      text = m_items[value];
  }

	void removeItem(String a_itemText)
  {
    // TODO
  }

//private:
	Vector<String> m_items;
};


// If MVC, this is the VC
class ListView : public Widget
{
public:
	ListView(Widget* a_parent, ItemList& a_list);
protected:
	void refresh()
	{
		repaint();
	}
	void keyEvent(KeyEvent& a_event);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
private:
  ItemList& m_list;
	bool	m_mouseOver;
  Point m_mousePos;
};


// TODO: possibly could inherit from Button too
//       and button could be a toggle/checkable button, so could
//       inherit from AbstractValueWidget<bool> / CheckableWidget
//       One solution is to make a base button class that various
//       button related classes inherit from.
class ComboBox : public AbstractValueWidget<int>
{
public:
	ComboBox(Widget* a_parent, AbstractProperty<int>& a_value);

  Signal activated;
  Signal deactivated;

	//Property<String> text;
	Property<bool> disabled;
	Property<bool> hasFocus;
	Property<bool> isDefault;
	
  //AbstractProperty<int>& currentIndex;
	void addItem(String a_itemText);
  void listChanged();
protected:
	void refresh2();
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
	void mouseEnterEvent(MouseEvent& a_event);
	void mouseLeaveEvent(MouseEvent& a_event);
	void mouseEvent(MouseEvent& a_event);
	void keyEvent(KeyEvent& a_event);
private:
	bool m_mouseDownInButton;
	bool m_mouseOverButton;
  bool m_active = false;

  // Option List should be a property - a property list
	//Vector<String> m_items;
  Widget* m_dropDownMenu = nullptr;
  ItemList m_list;
};


class ProgressBar : public AbstractValueWidget<int>
{
public:
	ProgressBar(Widget* a_parent, AbstractProperty<int>& a_value);
	Property<bool> disabled;
	AbstractProperty<int>& progress;
protected:
	void refresh2();
	void sizeOptions(SizeOptions& a_sizeOptions);
	void paintEvent(PaintEvent& a_event);
};


END_NAMESPACE

