/*
 *  psychlops_figure_prototype.cpp
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2009/02/15 by Kenchi HOSOKAWA
 *  (C) 2009 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#include "psychlops_widget.h"
#include "psychlops_widgets.h"
#include "psychlops_widgets_prototype.h"
#include "../../../core/ApplicationInterfaces/psychlops_code_snippets.h"

namespace Psychlops {


namespace Widgets {





	// TextBlock
	void TextBlock::initialize()
	{
		if(!initialized)
		{
			area.fill = Color::gray;
			area.stroke = Stroke::hair_line;
			area.set( content.length()*label.font.size, label.font.size + 4 );
			area.shift( -area.getWidth()/2, -label.font.size/2 - 2 );

			label.fill = Color::white;
			label.stroke = Stroke::hair_line;
			label.shift(0, label.font.size/4 - 1 );
			label.align = Letters::TEXT_ALIGN_CENTER;

			append(&area);
			append(&label);
		}
	}
	TextBlock::TextBlock()
	{
		initialized = false;
	}
	TextBlock::TextBlock(std::wstring s, double hei)
	{
		initialized = false;
		setText(s);
		initialize();
	}

	Point TextBlock::getHitDatum()
	{
		return getDatum();
	}
	TextBlock& TextBlock::set(const std::wstring s, double hei)
	{
		setText(content);
		initialize();
		return *this;
	}
	TextBlock& TextBlock::set(double w, double h)
	{
		initialized = false;
		area.set(w,h);
		initialize();
		return *this;
	}
	void TextBlock::setText(const std::wstring &str)
	{
		content = str;
		label.setString(content);
		initialize();
	}
	std::wstring& TextBlock::getText()
	{
		return content;
	}




	void Button_::evokeP(Events::Evoke &ev)
	{
		if(onClick!=0) (*onClick)();
	}
	void Button_::evokeMouse(Events::MouseLeftUp &ev)
	{
		Events::Evoke e;
		getSlots().emit(e);
	}
	Button_::Button_()
	{
		onClick = 0;
		initialized = false;
		getSlots().connectEventDelegate(this, &Button_::evokeMouse);
		getSlots().connectEventDelegate(this, &Button_::evokeP);
	}
	Button_::Button_(std::wstring s, double hei)
	{
		onClick = 0;
		initialized = false;
		setText(s);
		initialize();
		getSlots().connectEventDelegate(this, &Button_::evokeMouse);
		getSlots().connectEventDelegate(this, &Button_::evokeP);
	}
	void Button_::onEvoked(void (*f)())
	{
		getSlots().connectActionFunction<Events::Evoke>(f);
	}
	//Button_& Button_::set(double w, double h)
	//{
	//	TextBlock::set(w,h);
	//	return *this;
	//}





	// Deplecated Widgets


	void TextBox::setBase()
	{
		cache.fill = Color::white;
		area.fill = Color::null_color;
		area.stroke = Stroke::hair_line;
	}
	TextBox::TextBox()
	{
		setBase();
	}
	TextBox::TextBox(std::wstring s)
	{
		setBase();
	}
	TextBox& TextBox::set(std::wstring s)
	{
		setBase();
		return *this;
	}
	TextBox& TextBox::draw(Drawable &target)
	{
//		if(!tabStopRegistered) { TabStopHotKey::tabStopHotKey.set(this); tabStopRegistered=true; }
		if(getLeft()<1 && getTop()<1) WidgetRect::__autoAlign(*this);

		area.draw(target);
		cache.setString(content);
		cache.draw(target);
		return *this;
	}
	void TextBox::setText(const std::wstring &str)
	{
		content = str;
	}
	std::wstring& TextBox::getText()
	{
		return content;
	}


	namespace Internal
	{
		void doNothing() {}
		void doNothingP(void *p) {}
	}

	Button::Button()
	{
		setBase();
	}
	Button::Button(std::wstring s)
	{
		set(s);
	}
	Button::Button(std::wstring s, double size)
	{
		set(s, size);
	}
	void Button::setBase() {
		onClick = 0;
		onClickP = 0;
		arg = 0;
	}
	Button& Button::set(std::wstring s, double size) {
		setBase();
		WidgetRect::set(s, size);
		return *this;
	}
	Button& Button::set(double w, double h)
	{
		setBase();
		WidgetRect::set(w, h);
		return *this;
	}
	Button& Button::draw(Drawable &target) {
//		if(!tabStopRegistered) { TabStopHotKey::tabStopHotKey.set(this); tabStopRegistered=true; }
		if(getLeft()<1 && getTop()<1) WidgetRect::__autoAlign(*this);

		Point mouse = drawableMouse(target);
		if(area.include(mouse)) {
			area.fill = theme->over_background[theme_type];
			if(mleft.pushed()) {
				pushThis();
				if(onClick!=0) onClick();
				if(onClickP!=0) onClickP(arg);
				label.fill = theme->active_foreground[theme_type];
				area.stroke = theme->active_stroke[theme_type];
			}
		} else {
			label.fill = theme->normal_foreground[theme_type];
			area.fill = theme->normal_background[theme_type];
			area.stroke = theme->normal_stroke[theme_type];
		}

		area.draw(target);
		Canvas* tag = dynamic_cast<Canvas*>(&target);
		if(tag!=0) tag->drawImage(*(theme->button_back), area);
		//label.align = Letters::TEXT_ALIGN_CENTER;
		label.centering(area.getCenter().x, area.getBottom()-2);
		label.draw(label.fill, target);
		return *this;
	}

	ToggleButton::ToggleButton()
	: Button(), checked_(false) {
	}
	ToggleButton::ToggleButton(std::wstring s)
	: Button(s), checked_(false) {
	}
	ToggleButton::ToggleButton(std::wstring s, double size)
	: Button(s, size), checked_(false) {
	}
	bool ToggleButton::toggle()
	{
		pushed_ = true;
		checked_ = !checked_;
		return checked_;
	}
	bool ToggleButton::toggle(bool on_off)
	{
		pushed_ = true;
		checked_ = on_off;
		return checked_;
	}
	bool ToggleButton::isChecked()
	{
		return checked_;
	}
	ToggleButton& ToggleButton::draw(Drawable &target)
	{
//		if(!tabStopRegistered) { TabStopHotKey::tabStopHotKey.set(this); tabStopRegistered=true; }
		if(getLeft()<1 && getTop()<1) WidgetRect::__autoAlign(*this);

		Point mouse = drawableMouse(target);
		if(area.include(mouse)) {
			area.fill = isChecked() ? theme->active_background[theme_type] : theme->over_background[theme_type];
			if(mleft.pushed()) {
				toggle();
				pushed_ = true;
				if(onClick!=0) onClick();
				if(onClickP!=0) onClickP(arg);
				label.fill = theme->active_foreground[theme_type];
				area.stroke = theme->active_stroke[theme_type];
			}
		} else {
			if(isChecked()){
				label.fill = theme->active_foreground[theme_type];
				area.fill = theme->active_background[theme_type];
				area.stroke = theme->active_stroke[theme_type];
			} else {
				label.fill = theme->normal_foreground[theme_type];
				area.fill = theme->normal_background[theme_type];
				area.stroke = theme->normal_stroke[theme_type];
			}
		}

		area.draw(target);
		Canvas* tag = dynamic_cast<Canvas*>(&target);
		if(tag!=0) tag->drawImage(*(theme->button_back), area);
		label.centering(area.getCenter().x, area.getBottom()-2);
		label.draw(label.fill, target);
		return *this;
	}



	Image ScreenShotButton::buffer;
	void ScreenShotButton::shoot(void *ffname)
	{
		Psychlops::Rectangle rect(Display::the_canvas->getWidth(), Display::the_canvas->getHeight());
		Display::the_canvas->to(buffer, rect);
		std::string fname("psychlops_");
		fname += (char*)ffname;
		fname += ".png";
		buffer.save(fname);
	}
	ScreenShotButton::ScreenShotButton(char *name__)
	: Button(L"Capture")
	{
		name = name__;
		arg = (void*)name.c_str();
		onClickP = &shoot;
	}


	const Interval Slider::def_itvl(0, Interval::CLOSE, 1, Interval::CLOSE);
	Slider::~Slider()
	{
		if(isLocal_ && var!=0) delete var;
	}
	Slider::Slider()
	: WidgetRect()
	{
		var = 0;
		scale_mode = LINEAR;
		setBase();
		//StackPanel::default_stack.append(this);
	}
	Slider::Slider(ExperimentalMethods::Variable &v)
	: WidgetRect()
	{
		var = 0;
		scale_mode = LINEAR;
		setBase();
		set(v);
		//StackPanel::default_stack.append(this);
	}
	Slider::Slider(double wid, double hei)
	: WidgetRect()
	{
		var = 0;
		scale_mode = LINEAR;
		WidgetRect::set(wid, hei);
		setBase();
		Interval rng;
		link(local, 0<=rng<=1, 0.125, 10.0);
		//StackPanel::default_stack.append(this);
	}
	Slider::Slider(std::string s, Interval rng, double d_step, double e_step)
	: WidgetRect()
	{
		var = 0;
		scale_mode = LINEAR;
		setBase();
		link(local, s, rng, d_step, e_step);
		setLabel(s);
		//StackPanel::default_stack.append(this);
	}
	void Slider::setBase() {
		show_value = true;
		isLocal_ = false;
		changed_ = true;
		area.fill = Color(0.3);
		internal.fill = Color::blue;
		dragged = false;
		local = 0;
		setSize(150, Font::default_font.size - 4);
	}
	Slider& Slider::set(ExperimentalMethods::Variable &v)
	{
		set(v.label);
		return linkTo(&v);
	}
	Slider& Slider::setSize(double wid, double hei)
	{
		WidgetRect::set(wid, hei);
		label.fill = Color::white;
		label.align = Letters::TEXT_ALIGN_LEFT;
		show_value = label.getString().length()*area.getHeight()*2/3 < area.getWidth();
		return *this;
	}
	Slider& Slider::set(std::string s, Interval rng, double d_step, double e_step)
	{
		if(var==0) {
			link(local, s, rng, d_step, e_step);
		} else {
			var->setInterval(rng);
			//var->set(s, rng, d_step, e_step);
		}
		return *this;
	}
	/*
	Slider& Slider::set(std::string s, double hei) { return set(StringToWString(s), hei); }
	Slider& Slider::set(std::wstring s, double hei)
	{
		WidgetRect::set(s, hei);
		label.fill = Color::white;
		label.align = Letters::TEXT_ALIGN_LEFT;
		show_value = label.getString().length()*area.getHeight()*2/3 < area.getWidth();
		return *this;
	}
	*/
	Slider& Slider::setLabel(std::string s) { return setLabel(StringToWString(s)); }
	Slider& Slider::setLabel(std::wstring s)
	{
		WidgetRect::setLabel(s);
		label.fill = Color::white;
		label.align = Letters::TEXT_ALIGN_LEFT;
		show_value = label.getString().length()*area.getHeight()*2/3 < area.getWidth();
		return *this;
	}
	Slider& Slider::draw(Drawable &target)
	{
		if(!tabStopRegistered) { TabStopHotKey::tabStopHotKey.set(this); tabStopRegistered=true; }
		if(getLeft()<1 && getTop()<1) WidgetRect::__autoAlign(*this);

		Point mouse = drawableMouse(target);
		bool mouse_over = area.include(mouse), focused = true;
		if(Display::the_canvas!=0) focused = Display::the_canvas->eventroot.isFocused__(this);
		Canvas* cnvs = dynamic_cast<Canvas*>(&target);

		if(var!=0) {
			area.draw(target);

			if(mleft.pushed() && mouse_over) { dragged = true; pushThis(); }
			if(mleft.pressed() && dragged) {
				if(area.getWidth()>area.getHeight()) { var->setByRatioInStep((mouse.x-area.getLeft())/area.getWidth()); changed_=true; }
				else { var->setByRatioInStep((area.getBottom()-mouse.y)/area.getHeight()); changed_=true; }
			}
			if(focused) {
				if(Keyboard::left.pressed()) { if(pressFrame<=0) { var->decrement(Keyboard::shift.pressed()); changed_=true; pressFrame = pressFrame==0 ? HumanInterfaceDevice::pushRepeatInterval : HumanInterfaceDevice::pushRepeatFrames; } }
				else if(Keyboard::right.pressed()) { if(pressFrame<=0) { var->increment(Keyboard::shift.pressed()); changed_=true; pressFrame = pressFrame==0 ? HumanInterfaceDevice::pushRepeatInterval : HumanInterfaceDevice::pushRepeatFrames; } }
				else { pressFrame = -1; }
				if(pressFrame>0) pressFrame--;
			}
			if(!mleft.pressed()) dragged = false;

			if(area.getWidth()>area.getHeight()) {
				internal.set(area.getLeft()+1, area.getTop()+1, area.getLeft()+area.getWidth()*(var->getRatio()), area.getBottom()-1).draw(target);
			} else {
				internal.set(area.getLeft()+1, area.getBottom()-area.getHeight()*(var->getRatio()), area.getRight()-1, area.getBottom()-1).draw(target);
			}
			area.draw(mouse_over ? Stroke::hair_line : Stroke::null_line);
			label.locate(area.getLeft()+5, area.getBottom()-2);
			if(focused) {
				label.draw(Color::white, target);
				if(cnvs!=0 && show_value) cnvs->msg(var->to_str(), area.getRight()-2, area.getBottom()-5, Color::white, Letters::TEXT_ALIGN_RIGHT);
			} else {
				label.draw(Color::gray, target);
				if(cnvs!=0 && show_value) cnvs->msg(var->to_str(), area.getRight()-2, area.getBottom()-5, Color::gray, Letters::TEXT_ALIGN_RIGHT);
			}
		}

		return *this;
	}
	Slider& Slider::linkTo(ExperimentalMethods::Variable *v)
	{
		var = v;
		return *this;
	}
	Slider& Slider::operator =(ExperimentalMethods::Variable &v)
	{
		return set(v);
	}
	bool Slider::changed()
	{
		bool tmp = changed_;
		changed_ = false;
		return tmp;
	}
	Slider::operator double()
	{
		return local;
	}
	double Slider::operator =(double v)
	{
		return local = v;
	}
	void Slider::setByRatio(double ratio) { var->setByRatio(ratio); }
	double Slider::getRatio() const { return var->getRatio(); }
	Interval Slider::getInterval() const { return var->getInterval(); }
	Interval Slider::setInterval(const Interval &itvl) { return var->setInterval(itvl); }
	void Slider::increment(int modulation) { var->increment(modulation); }
	void Slider::decrement(int modulation) { var->decrement(modulation); }


	Dial::Dial() {
		set(50);
		setBase();
	}
	Dial::Dial(double r) {
		set(r);
		setBase();
	}
	void Dial::setBase() {
		local = 0;
		link(local);
		changed_ = true;
		current_theta = factor = 0;
		pressed = dragged = false;
		ext.fill = Color(0.3);
		internal.fill = Color::black;
	}
	Dial& Dial::set(double r) {
		ext.set(r,r);
		internal.set(r/5, r/5);
		return *this;
	}
	Dial& Dial::link(double &item, double fac)
	{
		var = &item;
		factor = fac;
		let.align = Letters::TEXT_ALIGN_CENTER;
		return *this;
	}
	Dial& Dial::draw(Drawable &target) {
		Point mouse = drawableMouse(target);
		ext.centering(getDatum());
		bool mouse_over = ext.include(mouse);
		target.ellipse(ext, ext.fill);
		if(!pressed && Mouse::left.pressed() && mouse_over) {
			dragged = true;
			former_theta = atan2(mouse.y-ext.getCenter().y, mouse.x-ext.getCenter().x);
		}
		if(mouse_over || dragged) {
			target.ellipse(ext, Stroke::hair_line);
			if(Keyboard::left.pressed()) { current_theta-=2*PI/36; (*var) = *var-factor/36; changed_=true; }
			if(Keyboard::right.pressed()) { current_theta+=2*PI/36; (*var) = *var+factor/36; changed_=true; }
		}
		pressed = Mouse::left.pressed();
		if(Mouse::left.pressed() && dragged) {
			current_theta = atan2(mouse.y-ext.getCenter().y, mouse.x-ext.getCenter().x);
			double delta_theta = Math::mod(current_theta-former_theta, 2*PI);
			if(delta_theta>PI) delta_theta-=2*PI;
			(*var) = *var+delta_theta/2/PI*factor;
			changed_ = true;
			former_theta = current_theta;
		} else {
			dragged = false;
		}
		internal.centering(ext).shift(ext.getWidth()/2.2*cos(current_theta), ext.getWidth()/2.2*sin(current_theta));
		target.ellipse(internal, internal.fill);
		//let.centering(ext.getCenter().x, ext.getBottom());
		//let.draw(let.fill, target);
		return *this;
	}
	bool Dial::changed() {
		bool tmp = changed_;
		changed_ = false;
		return tmp;
	}
	Dial::operator double() {
		return local;
	}
	double Dial::operator =(double v) {
		return local = v;
	}


	SelectBox::SelectBox()
	: WidgetRect()
	{
		set(Font::default_font.size);
	}
	SelectBox::SelectBox(double size)
	: WidgetRect()
	{
		set(size);
	}
	SelectBox& SelectBox::set(double hei)
	{
		return set(0, hei);
	}
	SelectBox& SelectBox::set(double wid, double hei)
	{
		selected_ = 0;
		vertical_ = false;
		area.resize(wid, hei);
		return *this;
	}
	void SelectBox::makeVertical()
	{
		area.resize(area.getHeight(), area.getWidth());
		vertical_ = true;
	}
	void SelectBox::appendL(Letters &let)
	{
		area.resize(Math::max(area.getWidth(), let.getString().length()*Font::default_font.size), area.getHeight());
		let.fill = theme->normal_foreground[theme_type];
		let.font.size = area.getHeight() - 4;;
		item.push_back(let);
	}
	int SelectBox::getSelected() { return selected_; }
	void SelectBox::setSelected(int dd) { selected_ = Math::min( Math::max(dd, 0), item.size() ); }
	void SelectBox::next() { if(selected_<item.size()-1) { ++selected_; } else { selected_ = 0; }  }
	void SelectBox::retreat() { if(selected_>0) { --selected_; } else { selected_ = item.size()-1; } }
	void SelectBox::hid()
	{
		int wheel = Mouse::getWheelDelta().y;
		if(wheel<0) {
			pushThis();
			area.stroke = theme->active_stroke[theme_type];
			retreat();
		}
		if(wheel>0 || mleft.pushed()) {
			pushThis();
			area.stroke = theme->active_stroke[theme_type];
			next();
		}
	}
	SelectBox& SelectBox::draw(Drawable &target)
	{
		Point mouse = drawableMouse(target);
		if(area.include(mouse)) {
			area.stroke = theme->normal_stroke[theme_type];
			hid();
			area.fill = theme->over_background[theme_type];
		} else {
			area.stroke = theme->normal_stroke[theme_type];
			area.fill = theme->normal_background[theme_type];
		}

		area.draw(target);
		Canvas* tag = dynamic_cast<Canvas*>(&target);
		if(tag!=0) tag->drawImage(*(theme->button_back), area);
		if(!vertical_) {
			if(!item.empty()) {
				item[selected_].locate(area.getLeft()+5, area.getBottom()-2);
				item[selected_].draw(item[selected_].fill, target);
			}
		} else {
			holder_.contents.clear();
			if(!item.empty()) {
				holder_.setDatum(Point(area.getLeft()+2, area.getTop()-5));
				holder_.append(item[selected_]);
				item[selected_].setDatum(Point(0,0)).shift(5, item[selected_].getFont().size);
				holder_.rotation = PI/2;
				holder_.draw(target);
			}
		}
		return *this;
	}


	TitleBar *TitleBar::default_titlebar;





}	/*	<- namespace Widgets 	*/
}	/*	<- namespace Psycholops 	*/


