module gedo.gui.component;



public import gedo.math.vector;
private import gedo.video.ggl;



alias void delegate() OnClick;



public class Component
{
private:
	enum Action
	{
		NONE,
		VIBRATION,
		BLINK,
		SLIDE_IN,
		SLIDE_OUT,
	}


public:
	Vector2D position;
	Vector2D size;


	this()
	{
		position[] = 0.0;
		size[]     = 0.0;
	}


	void update()
	{
		foreach(child; this)
		{
			child.update();
		}

		updateTime();

		onUpdate();
	}


	void draw()
	{
		if(_action == Action.BLINK && _time & 32)
		{
			return;
		}

		GGL_pushMatrix();

		transform();

		onDraw();

		GGL_popMatrix();
	}


	void drawRecursive()
	{
		draw();

		foreach(child; this)
		{
			child.drawRecursive();
		}
	}


	void drawReverse()
	{
		if(_parent)
		{
			_parent.drawReverse();

			foreach(c; _parent)
			{
				c.draw();
			}
		}
		else
		{
			draw();
		}
	}


	int opApply(int delegate(ref Component) dg)
	{
		int result = 0;

		for(auto child = _child; child !is null; child = child._brother)
		{
			result = dg(child);
			if(result)
			{
				break;
			}
		}

		return result;
	}


	void scan(void delegate(Component) dg)
	{
		foreach(child; this)
		{
			child.scan(dg);

			dg(child);
		}
	}


	void add(Component child)
	{
		assert(!child._brother);

		if(!_child)
		{
			_child = child;
		}
		else
		{
			foreach(c; this)
			{
				if(!c._brother)
				{
					c._brother = child;
					break;
				}
			}
		}

		child._parent = this;
	}


	void remove(Component child)
	{
		if(child is _child)
		{
			_child = child._brother;
		}
		else
		{
			foreach(c; this)
			{
				if(child is c._brother)
				{
					c._brother = child._brother;
					break;
				}
			}
		}

		child._parent  = null;
		child._brother = null;
	}


	Component parent()
	{
		return _parent;
	}


	Component root()
	{
		auto r = this;

		while(r._parent)
		{
			r = r._parent;
		}

		return r;
	}


	Component previous()
	{
		if(_parent)
		{
			Component p = null;
			foreach(c; _parent)
			{
				if(c is this)
				{
					break;
				}
				else
				{
					p = c;
				}
			}
			if(!p)
			{
				foreach(c; _parent)
				{
					p = c;
				}
			}

			return p;
		}
		else
		{
			return this;
		}
	}


	Component next()
	{
		if(_parent)
		{
			if(_brother)
			{
				return _brother;
			}
			else
			{
				return _parent._child;
			}
		}
		else
		{
			return this;
		}
	}


	bool isAnimating()
	{
		return (_action != Action.NONE);
	}


	void stop()
	{
		_action = Action.NONE;
		_time   = 0;
	}


	void vibrate()
	{
		_action = Action.VIBRATION;
		_time   = 16;
	}


	void blink()
	{
		_action = Action.BLINK;
		_time   = 0;
	}


	void slideIn()
	{
		_action = Action.SLIDE_IN;
		_time   = 16;
	}


	void slideOut()
	{
		_action = Action.SLIDE_OUT;
		_time   = 16;
	}


	void onUpdate()
	{
	}


	void onDraw()
	{
	}


	static void alignVertical(Component[] components, Vector2D center, float interval = 0.0)
	{
		if(interval <= 0.0)
		{
			interval = components[0].size[1] + 4.0;
		}

		Vector2D p = center[];

		p[1] += (components.length - 1)*0.5*interval;

		foreach(c; components)
		{
			c.position[] = p[];

			p[1] -= interval;
		}
	}


	static void alignHorizontal(Component[] components, Vector2D center)
	{
		float w = 0.0f;

		foreach(c; components)
		{
			w += c.size[0] + 4.0;
		}

		Vector2D p = center[];

		p[0] -= w * 0.5;

		foreach(c; components)
		{
			p[0] += (c.size[0] + 4.0)*0.5;

			c.position[] = p[];

			p[0] += (c.size[0] + 4.0)*0.5;
		}
	}


	bool onAxisMove(int axis)
	{
		return false;
	}


	bool click()
	{
		auto result = onClick();

		if(_onClick)
		{
			_onClick();
		}

		return result;
	}


	void cbClick(OnClick dg)
	{
		_onClick = dg;
	}


	bool onClick()
	{
		return false;
	}


	bool cancel()
	{
		auto result = onCancel();

		if(_onCancel)
		{
			_onCancel();

			result = true;
		}

		return result;
	}


	bool onCancel()
	{
		return false;
	}


	void cbCancel(OnClick dg)
	{
		_onCancel = dg;
	}


private:
	Component _parent;
	Component _child;
	Component _brother;
	OnClick   _onClick;
	OnClick   _onCancel;
	Action    _action;
	uint      _time;


	void updateTime()
	{
		switch(_action)
		{
		case Action.NONE:
			break;

		case Action.VIBRATION:
		case Action.SLIDE_IN:
		case Action.SLIDE_OUT:
			if(_time > 0)
			{
				--_time;
			}
			else
			{
				_action = Action.NONE;
			}
			break;

		case Action.BLINK:
			++_time;
			break;
		}
	}


	void transform()
	{
		if(_parent)
		{
			_parent.transform();
		}

		switch(_action)
		{
		case Action.NONE:
			translate(position);
			break;

		case Action.VIBRATION:
			{
				Vector2D p = position[];
				auto     t = _time >> 1;

				if(t & 1)
				{
					p[1] += t;
				}
				else
				{
					p[1] -= t;
				}

				translate(p);
			}
			break;

		case Action.SLIDE_IN:
			{
				float xs = 320.0 + size[0] * 0.5;
				float  k = _time / 16.0;

				float  x = k*xs + (1.0 - k)*position[0];

				translate([x, position[1]]);
			}
			break;

		case Action.SLIDE_OUT:
			{
				float xe = -320.0 - size[0] * 0.5;
				float  k = _time / 16.0;

				float  x = (1.0 - k)*xe + k*position[0];

				translate([x, position[1]]);

				if(_time == 0)
				{
					position[0] = x;
				}
			}
			break;

		case Action.BLINK:
			translate(position);
			break;
		}
	}
}

