/*
 *  psychlops_figure_prototype.cpp
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2005/10/05 by Kenchi HOSOKAWA
 *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#include "psychlops_widgets_prototype.h"
#include <stdio.h>
#include <string.h>


namespace Psychlops {
namespace Widgets {
		bool hook(Figure *f, bool on_off, Canvas *cnvs) {
			if(cnvs!=0) {
				if(on_off) {
					for(std::vector<Figure*>::iterator i=cnvs->billboard.begin(); i!=cnvs->billboard.end(); i++) {
						if(*i==f) { return false; }
					}
					cnvs->billboard.push_back(f);
					return true;
				} else {
					for(std::vector<Figure*>::iterator i=cnvs->billboard.begin(); i!=cnvs->billboard.end(); i++) {
						if(*i==f) { cnvs->billboard.erase(i); return true; }
					}
				}
			}
			return false;
		}


		RectWidget::RectWidget() : Rectangle() {}
//		RectWidget::RectWidget(const double width__, const double height__) : left(0), top(0), right(width__), bottom(height__) {}
//		RectWidget::~RectWidget() {}
//		RectWidget& RectWidget::set(const double width__, const double height__) {
	//		static_cast<double>(left) = 0.0;
	//		static_cast<double>(top) = 0.0;
	//		static_cast<double>(right) = width__;
	//		static_cast<double>(bottom) = height__;
//			return *this;
//		}
//		RectWidget& RectWidget::centering(const Point& p) { Rectangle::centering(p); return *this; }
//		RectWidget& RectWidget::centering(const Drawable& target) { Rectangle::centering(target); return *this; }
//		RectWidget& RectWidget::centering(const double x, const double y, const double z) { Rectangle::centering(x,y); return *this; }
//		RectWidget& RectWidget::shift(const double x, const double y, const double z) { Rectangle::shift(x,y); return *this; }


		Console::LetterStyle::LetterStyle() : line_height_(10) {};
		Console::Console() : RectWidget(), changed_(false), buffer(0), last_pos_(0) {
			fill = Color(0.2, 0.2, 0.6, 0.35);
			stroke = Stroke(Color::white, 2, Stroke::SOLID);
			textColor = Color::green;
		}
		Console::Console(const Rectangle &rect) : RectWidget(), changed_(false), buffer(0), last_pos_(0) {
			fill.set(0.2, 0.2, 0.6, 0.35);
			stroke = Stroke(Color::white, 2, Stroke::SOLID);
			textColor = Color::green;
			set(rect);
		}
		Console::Console(const double width, const double height) : RectWidget(), changed_(false), buffer(0), last_pos_(0) {
			fill.set(0.2, 0.2, 0.6, 0.35);
			stroke = Stroke(Color::white, 2, Stroke::SOLID);
			textColor = Color::green;
			set(width, height);
		}
		Console::~Console() {
			if(buffer!=0) delete [] buffer;
			hook(false);
			buffer = 0;
		}
		Console & Console::set(const Rectangle &rect) {
			if(buffer==0) buffer = new char[1024];
			Rectangle::set(rect);
			lines_.push_back(std::string());
			lines_.at(0) = File::decodePath("%TIME_") + "\r";
			view_.push_back(LineView(lines_.back(), 0, lines_.back().length()-1));
			lines_.push_back(std::string());
			view_.push_back(LineView(lines_.back(), 0, 0));
			changed_ = true;
			hook();
			return *this;
		}
		Console & Console::set(const double width, const double height) {
			return set(Rectangle(width, height));
		}
		Console & Console::hook(bool on_off, Canvas *cnvs) {
			Psychlops::Widgets::hook(this, on_off, cnvs);
			return *this;
		}
		Console & Console::draw(Drawable &target) { return draw(left, top, 0, target); }
		Console & Console::draw(const Color &col, Drawable &target) { return draw(left, top, 0, target); }
		Console & Console::draw(const Stroke &strk, Drawable &target) { return draw(left, top, 0, target); }
		Console & Console::draw(const double x, const double y, const double z, Drawable &target) {
			target.rect(*this, fill);
			target.rect(*this, stroke);
			devideStreamIntoLines();
			int line_c=0;
			for(std::deque<LineView>::iterator i=view_.begin(); i!=view_.end(); i++) {
				Display::msg((i->target)->substr(i->begin, i->end - i->begin), x+4, y+12 + line_c*10, textColor, Letters::TEXT_ALIGN_LEFT, getWidth()-18);
				line_c++;
			}
			return *this;
		}
		void Console::devideStreamIntoLines() {
			const int h_limit = ((int)getWidth()-15)/7, v_limit = (int)getHeight()/10-1;

			if(changed_) {
				strings_.flush();
				//while(!strings_.eof()) {
					strings_.getline(buffer, 1024);
					lines_.back() += buffer;
					char last_char = lines_.back()[lines_.back().length()-1];
					while(lines_.back().length()-last_pos_>h_limit) {
						last_pos_ = ((last_pos_/h_limit)+1)*h_limit;
						view_.back().end = last_pos_-1;
						view_.push_back(LineView(lines_.back(), last_pos_, last_pos_));
						if( view_.size()>v_limit ) view_.pop_front();
					}
					view_.back().end = lines_.back().length()-1;
					if(last_char=='\r' || last_char=='\n') {
						lines_.push_back(std::string());
						view_.push_back(LineView(lines_.back(), 0, 0));
						last_pos_ = 0;
						if( view_.size()>v_limit ) view_.pop_front();
					}
				//}
				strings_.clear();

/*				int line_c=0, line_limit = getHeight()/8-2;
				if(last_num_lines_ > line_limit) {
					for(int i=lines_.size()-1; i>=0; i--) {
						line_c += lines_.at(i).lines_in_display_;
						if(line_c>line_limit) {
							last_begin_line_ = i+1;
							break;
						}
					}
				}
 */
			}
			changed_ = false;
		}
		void Console::save(std::string filename) {
			std::ofstream f(File::decodePath(filename).c_str());
			for(int i=0; i<lines_.size(); i++) {
				f << lines_.at(i);
			}
			f.close();
			return;
		}
	Console::LineView::LineView() : target(0), begin(0), end(0) {}
	Console::LineView::LineView(std::string &t, int b, int e) : target(&t), begin(b), end(e) {}




		HotKeyWidget::HotKeyWidget(const Keyboard::Key &init_key) : key(init_key), func(0) {}
		HotKeyWidget::~HotKeyWidget() { hook(this, false); }
		const Point HotKeyWidget::getDatum() const { return Point(0,0,0); }
		HotKeyWidget& HotKeyWidget::setDatum(const Point &p) { return *this; }
		HotKeyWidget& HotKeyWidget::centering(const Point &p) { return *this; }
		HotKeyWidget& HotKeyWidget::draw(Drawable &drawable) { return *this; }

		PauseHotKey::PauseHotKey(const Keyboard::Key &init_key) : HotKeyWidget(init_key) {}
		PauseHotKey& PauseHotKey::draw(Drawable &target) {
			AppState::ThreadPriority current = AppState::getThreadPriority();
			if(Input::get(key, Keyboard::pushed)) {
				while(!Input::get(key, Keyboard::pushed)){
					AppState::setThreadPriority(AppState::IDLE);
				}
				AppState::setThreadPriority(current);
			}
			return *this;
		}

		ExitHotKey::ExitHotKey(const Keyboard::Key &init_key) : HotKeyWidget(init_key) {
		}
		ExitHotKey& ExitHotKey::draw(Drawable &target) {
			if(Input::get(key, Keyboard::pushed)) {
				exit(0);
			}
			return *this;
		}

		TabStopHotKey::TabStopHotKey() : HotKeyWidget(Keyboard::tab) {}
		TabStopHotKey::TabStopHotKey(const Keyboard::Key &init_key)  : HotKeyWidget(init_key) {
			registered = true;
		}
		void TabStopHotKey::set(void* target) {
			if(!registered) {
				hook(this, true);
				registered = true;
			}
			if(Display::the_canvas!=0) Display::the_canvas->eventroot.appendTabStop__(target);
		}
		TabStopHotKey& TabStopHotKey::draw(Drawable &target) {
			//StackPanel::default_stack.set(400, 1024);
			//StackPanel::default_stack.align();
			if(Keyboard::tab.pushed()) {
				if(Display::the_canvas!=0)
				{
					if(Keyboard::shift.pressed() || Keyboard::shift_r.pressed())
					{
						Display::the_canvas->eventroot.prevTabStop();
					} else {
						Display::the_canvas->eventroot.nextTabStop();
					}
				}
			}
			return *this;
		}
		TabStopHotKey TabStopHotKey::tabStopHotKey;


	bool MoviePlayer::auto_play = false;
	MoviePlayer* MoviePlayer::now_playing = 0;
	Prototype::Thread MoviePlayer::thread;
	Canvas *MoviePlayer::cnvs = 0;
	Canvas::CanvasMode MoviePlayer::mode;
	Display *MoviePlayer::disp;

	void MoviePlayer::paraThread()
	{
		while(true)
		{
			if(Keyboard::esc.pressed())
			{
				now_playing->stop();
			}
			Prototype::Thread::sleep(1);
		}
	}

	MoviePlayer::MoviePlayer()
	{
		cnvs = 0;
	}
	MoviePlayer::MoviePlayer(Figures::Movie &movie)
	{
		cnvs = 0;
		appendMovie(movie);
	}
	MoviePlayer::~MoviePlayer() {}

	void MoviePlayer::appendMovie(Figures::Movie &movie)
	{
		movies.push_back(&movie);
	}

	/*
	void MoviePlayer::start(Canvas::CanvasMode mode_, const Display &disp_)
	{
		if(!auto_play)
		{
			mode = mode_;
			disp = (Display*)&disp_;
			now_playing = this;
			auto_play = true;
			thread.create(&play_loop_in_this_thread);
		}
	}
	*/
	void MoviePlayer::start(Canvas& target)
	{
		start(&paraThread, target);
	}
	void MoviePlayer::start(void (*func)(), Canvas& target)
	{
		if(movies.size()!=1) return;
		if(!auto_play)
		{
			cnvs = &target;
			now_playing = this;
			auto_play = true;
			thread.create(func);
			play_loop();
		}
	}
	void MoviePlayer::play_loop_in_this_thread()
	{
		Canvas c;
		c.set(mode, *disp);
		cnvs = &c;
		play_loop();
	}
	void MoviePlayer::play_loop()
	{
		while(true)
		{
			if(!auto_play) break;
			//now_playing->movies[0].current_frame++;
			//now_playing->movies->current_frame %= now_playing->movies[0].frame.size();
			cnvs->clear();
			now_playing->movies[0]->draw(*cnvs);
			cnvs->flip();
		}
	}
	void MoviePlayer::stop()
	{
		auto_play = false;
	}

		SequentialScreenshot::SequentialScreenshot()
		 : max_shot(0), current_shot(0), rec(false), saved(false), directory("%USER_DOCUMENTS%"), filePrefix("%EXPNAME__%TIME_") {
		}
		SequentialScreenshot::SequentialScreenshot(const Rectangle &rect)
		 : max_shot(0), current_shot(0), rec(false), saved(false), directory("%USER_DOCUMENTS%"), filePrefix("%EXPNAME__%TIME_") {
		 	set(rect);
		}
		SequentialScreenshot::~SequentialScreenshot() {
			if(!saved) save();
			clear();
		}
		SequentialScreenshot& SequentialScreenshot::set(const Rectangle &rect) {
			if(movie.size()==0) {
				source.set(rect);
//				hook();
			}
			return *this;
		}
		SequentialScreenshot& SequentialScreenshot::set(const double width, const double height) {
			return set(Rectangle(width, height));
		}
		SequentialScreenshot& SequentialScreenshot::clear() {
			if(movie.size() != 0) {
				for(int i=0; i<movie.size(); i++) {
					delete movie[i];
				}
				movie.clear();
			}
			return *this;
		}
		SequentialScreenshot& SequentialScreenshot::hook(bool on_off, Canvas *cnvs) {
			Psychlops::Widgets::hook(this, on_off, cnvs);
			return *this;
		}
		SequentialScreenshot& SequentialScreenshot::record(int max_num_shots, std::string file_prefix) {
			if(max_num_shots<0) throw new Exception("SequentialScreenshot: max_num_shots should be a positive nunmber.");
			if(!rec) {
				filePrefix = file_prefix;
				max_shot = max_num_shots;
				current_shot = 0;
				if(max_num_shots!=0) {
					movie.reserve(max_shot);
					for(int i=0; i<max_shot; i++) {
						movie.push_back(new Image);
					}
				}
				rec = true;
			}
			return *this;
		}
		const SequentialScreenshot& SequentialScreenshot::stop() {
			if(rec) {
				rec = false;
				if(Drawable::prime_is_a_canvas()) Display::msg("END", Display::getWidth()-5, 10, Color::red, Letters::TEXT_ALIGN_RIGHT);
			}
			return *this;
		}
		const SequentialScreenshot& SequentialScreenshot::save() {
			char buf[256];
			for(int i=0; i<movie.size(); i++) {
				memset(buf, 0, 256);
				sprintf(buf, "%u", i);
				movie[i]->save(directory+'/'+filePrefix+"_"+buf+".png");
			}
			clear();
			saved = true;
			return *this;
		}
		SequentialScreenshot& SequentialScreenshot::draw(Drawable &target) {
			if(rec) {
				if(max_shot==0) {
					movie.push_back(new Image);
					if(Drawable::prime_is_a_canvas()) dynamic_cast<Canvas *>(Drawable::prime)->to(*(movie[current_shot]), source);
				} else if(current_shot<max_shot) {
					if(Drawable::prime_is_a_canvas()) dynamic_cast<Canvas *>(Drawable::prime)->to(*(movie[current_shot]), source);
				} else {
					rec = false;
					return *this;
				}
				current_shot++;
				saved = false;
			}
			Rectangle rect;
			Color mean(0.5,0.4,0.4,0.5);
			Canvas* the_canvas = dynamic_cast<Canvas *>(Drawable::prime);
			if(the_canvas!=0) {
				rect.set(0,0,the_canvas->getWidth(),source.getTop()).draw(mean);
				rect.set(0,source.getTop()+1,source.getLeft(),source.getBottom()-1).draw(mean);
				rect.set(source.getRight(),source.getTop()+1,the_canvas->getWidth(),source.getBottom()-1).draw(mean);
				rect.set(0,source.getBottom(),the_canvas->getWidth(),the_canvas->getHeight()).draw(mean);
			}
			if(rec) {
				if(Drawable::prime_is_a_canvas()) the_canvas->msg("@REC", Display::getWidth()-5, 10, Color::red, Letters::TEXT_ALIGN_RIGHT);
			} else {
				if(Keyboard::shift.pressed() && Mouse::left.pressed()) source.set(Mouse::x, Mouse::y, source.getRight(), source.getBottom());
				if(Keyboard::shift.pressed() && Mouse::right.pressed()) source.set(source.getLeft(), source.getTop(), Mouse::x, Mouse::y);
				if(Drawable::prime_is_a_canvas()) the_canvas->msg("||STOP", Display::getWidth()-5, 10, Color::gray, Letters::TEXT_ALIGN_RIGHT);
			}

			return *this;
		}


/*
		Movie::Movie(const Rectangle &rect)
		 : max_shot(0), current_shot(0), rec(false), saved(false), directory("%USER_DOCUMENTS%"), filePrefix("%EXPNAME__%TIME_") {
		}
		Movie::Movie(const Rectangle &rect)
		 : max_shot(0), current_shot(0), rec(false), saved(false), directory("%USER_DOCUMENTS%"), filePrefix("%EXPNAME__%TIME_") {
		 	set(rect);
		}
		Movie::~Movie() {
			//if(!saved) save();
			clear();
		}
		Movie& Movie::set(const Rectangle &rect);
		Movie& Movie::set(const double width, const double height);
		Movie& Movie::load(size_t begin, size_t end) {
			saved = true;
			return *this;
		}
		Movie& Movie::load(const char* format, size_t begin, size_t end) {
			saved = true;
			return *this;
		}
		Movie& Movie::clear() {
			if(movie.size() != 0) {
				for(int i=0; i<movie.size(); i++) {
					delete movie[i];
				}
				movie.clear();
			}
			return *this;
		}
		Movie& Movie::hook(bool on_off, Canvas *cnvs);
		Movie& Movie::record(int max_num_shots, std::string file_prefix);
		Movie& Movie::seek(size_t frame);
		Movie& Movie::stop();
		Movie& const Movie::save(size_t begin, size_t end) {
			char buf[256];
			for(int i=0; i<movie.size(); i++) {
				memset(buf, 0, 256);
				sprintf(buf, "%u", i);
				movie[i]->save(directory+'/'+filePrefix+"_"+buf+".png");
			}
			clear();
			saved = true;
			return *this;
		}
		Movie& const Movie::save(const char* format, size_t begin, size_t end) {
			char buf[256];
			for(int i=0; i<movie.size(); i++) {
				memset(buf, 0, 256);
				sprintf(buf, "%u", i);
				movie[i]->save(directory+'/'+filePrefix+"_"+buf+".png");
			}
			clear();
			saved = true;
			return *this;
		}

		Movie& draw(Drawable &target);
		Movie& play(Drawable &target);
*/



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


