




#include <allegro5/allegro_primitives.h>

#include <allegro_flare/allegro_flare.h>
#include <allegro_flare/color.h>
#include <allegro_flare/useful.h>
#include <allegro_flare/clipboard_win.h> // eventually should be replaced with a generic clipboard

#include <flare_gui/text_area.h>
#include <flare_gui/collision_box.h>







// FGUITextArea::Cursor


FGUITextArea::Cursor::Cursor(int head_pos, int anchor_pos)
	: head_pos(head_pos)
	, _anchor_pos(anchor_pos)
	, anchor_down(false)
{}

void FGUITextArea::Cursor::move(int delta)
{
	head_pos += delta;
	if (!anchor_down) _anchor_pos = head_pos;
}
void FGUITextArea::Cursor::move(int delta, int min, int max)
{
	head_pos += delta;
	if (head_pos >= max) head_pos = max;
	if (head_pos <= min) head_pos = min;

	if (!anchor_down) _anchor_pos = head_pos;
}
void FGUITextArea::Cursor::move_to(int pos)
{
	head_pos = pos;
	if (!anchor_down) _anchor_pos = head_pos;
}
void FGUITextArea::Cursor::move_to(int pos, int min, int max)
{
	head_pos = pos;
	if (head_pos >= max) head_pos = max;
	if (head_pos <= min) head_pos = min;

	//head_pos = pos;
	if (!anchor_down) _anchor_pos = head_pos;
}
void FGUITextArea::Cursor::move_anchor_to_cursor() { _anchor_pos = head_pos; }

int FGUITextArea::Cursor::pos() { return head_pos; }
int FGUITextArea::Cursor::anchor_pos() { return _anchor_pos; }
int FGUITextArea::Cursor::get_start() { return min(head_pos, _anchor_pos); }
int FGUITextArea::Cursor::get_end() { return max(head_pos, _anchor_pos); }
int FGUITextArea::Cursor::get_selection_length() { return get_end() - get_start(); }

void FGUITextArea::Cursor::set_anchor_down() { anchor_down = true; }
void FGUITextArea::Cursor::set_anchor_up() { anchor_down = false; }
bool FGUITextArea::Cursor::is_anchor_down() { return anchor_down; }
bool FGUITextArea::Cursor::selection_active() { return head_pos != _anchor_pos; }











// FGUITextArea



FGUITextArea::FGUITextArea(FGUIParent *parent, ALLEGRO_FONT *font, std::string text, float x, float y, float w, float h)
	: FGUIWidget(parent, new FGUICollisionBox(x, y, w, h))
	, cursor(0, 0)
	, full_text(text)
	, font(font)
	, cursor_blink_counter(1)
{}

void FGUITextArea::move_cursor(int delta)
{
	cursor.move(delta, 0, (int)full_text.length());
	cursor_blink_counter = 1;
}

void FGUITextArea::move_cursor_to(int pos)
{
	cursor.move_to(pos, 0, (int)full_text.length());
	cursor_blink_counter = 1;
}

void FGUITextArea::move_cursor_to_next_of(std::string chars)
{
	int cursor_pos = full_text.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", cursor.pos());
	if (cursor_pos == std::string::npos)
	{
		cursor_pos = full_text.length();
	}
	move_cursor_to(cursor_pos);
	move_cursor(1);
}

void FGUITextArea::move_cursor_to_previous_not_of(std::string chars)
{
	int cursor_pos = full_text.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\"", max(0, cursor.pos()-1));
	if (cursor_pos == std::string::npos) { cursor_pos = 0; }
	cursor.move_to(cursor_pos);
}

void FGUITextArea::erase_selection()
{
	full_text.erase(cursor.get_start(), cursor.get_selection_length());
	cursor.move_to(cursor.get_start());
	cursor.move_anchor_to_cursor();
}

void FGUITextArea::set_text(std::string text)
{
	this->full_text = text;
	cursor.move_to(0);
	cursor.move_anchor_to_cursor();
}

void FGUITextArea::insert_text(std::string text)
{
	full_text.insert(cursor.pos(), text);
	//cursor.move_to(cursor.pos()+text.length(), 0, full_text.length());
	cursor.move(text.length());
	cursor.move_anchor_to_cursor();
}

std::string FGUITextArea::get_text()
{
	return this->full_text;
}

void FGUITextArea::on_draw()
{
	placement2d &place = (*gimmie_placement());
	//if (focused) al_draw_rectangle(0, 0, place.w, place.h, color::red, 2.0);
	//if (focused) al_draw_text(font, color::yellow, -10, -10, NULL, tostring(cursor).c_str());

	if ((cursor_blink_counter -= 0.025) < 0) cursor_blink_counter = 1;

	float draw_cursor_x = 0;
	float draw_cursor_y = 0;
	std::string word;
	std::size_t pos = 0;
	std::size_t found_pos = 0;
	int _number_of_lines = 0;
		
	bool selection_active = cursor.selection_active();
	int selection_line_start = 0;
	int selection_line_end = 0;
	int selection_x_start = 0;
	int selection_x_end = 0;
	int line_height = al_get_font_line_height(font);

	unsigned i=0;
	for (i=0; i<full_text.length(); i++)
	{
		if (full_text[i] == '\n')
		{
			// move the drawing cursor down on newlines
			draw_cursor_x = 0;
			draw_cursor_y += al_get_font_line_height(font);
			_number_of_lines++;
		}
		else
		{
			// proceed with the drawing

			std::string text_packet_to_draw = tostring(full_text[i]).c_str();
			float text_width = al_get_text_width(font, text_packet_to_draw.c_str()); // < this is the process that should be cached

			if (draw_cursor_x + text_width > gimmie_placement()->w)
			{
				// wrap if the text width is beyond the edge
				draw_cursor_x = 0;
				draw_cursor_y += al_get_font_line_height(font);
				_number_of_lines++;
			}

			if (selection_active && cursor.anchor_pos() == i)
			{
				// draw the anchor
				if (focused) al_draw_line(draw_cursor_x, draw_cursor_y, draw_cursor_x, draw_cursor_y+line_height, color::color(color::aliceblue, 0.5), 2.0);
				selection_x_start = draw_cursor_x;
				selection_line_start = _number_of_lines;
			}

			if (cursor.pos() == i)
			{
				// draw the cursor
				if (focused) al_draw_line(draw_cursor_x, draw_cursor_y, draw_cursor_x, draw_cursor_y+line_height, color::color(color::aliceblue, cursor_blink_counter), 2.0);
				selection_x_end = draw_cursor_x;
				selection_line_end = _number_of_lines;
			}

			al_draw_text(font, color::black, draw_cursor_x, draw_cursor_y, NULL, text_packet_to_draw.c_str());
			draw_cursor_x += text_width;
		}
	}


	// just the one exception where the cursor is in the last place
	if (cursor.pos() == full_text.length())
	{
		if (focused) al_draw_line(draw_cursor_x, draw_cursor_y, draw_cursor_x, draw_cursor_y+line_height, color::color(color::aliceblue, cursor_blink_counter), 2.0);
		selection_x_end = draw_cursor_x;
		selection_line_end = _number_of_lines;
	}
	if (selection_active && cursor.anchor_pos() == full_text.length())
	{
		// draw the anchor
		if (focused) al_draw_line(draw_cursor_x, draw_cursor_y, draw_cursor_x, draw_cursor_y+line_height, color::color(color::aliceblue, 0.5), 2.0);
		selection_x_start = draw_cursor_x;
		selection_line_start = _number_of_lines;
	}



	// swap the selection_x_start and selection_x_end if the anchor is *after* the cursor

	if (selection_active && cursor.pos() < cursor.anchor_pos())
	{
		selection_x_start ^= selection_x_end;
		selection_x_end ^= selection_x_start;
		selection_x_start ^= selection_x_end;			

		selection_line_start ^= selection_line_end;
		selection_line_end ^= selection_line_start;
		selection_line_start ^= selection_line_end;			
	}


	// draw the selection box

	if (selection_active)
	{
		float line_height = al_get_font_line_height(font);

		ALLEGRO_COLOR selection_color = color::color(color::aliceblue, 0.2);
		if (selection_line_start == selection_line_end)
		{
			al_draw_filled_rectangle(selection_x_start, selection_line_start*line_height, selection_x_end, (selection_line_end+1)*line_height, selection_color);
		}
		else
		{
			// draw the first selection line
			al_draw_filled_rectangle(selection_x_start, selection_line_start*line_height, gimmie_placement()->w, (selection_line_start+1)*line_height, selection_color);

			// draw the last selection line
			al_draw_filled_rectangle(0, selection_line_end*line_height, selection_x_end, (selection_line_end+1)*line_height, selection_color);

			// if the selection box is larger than 2 lines
			if (abs(selection_line_start - selection_line_end) > 1)
			{
				al_draw_filled_rectangle(0, (selection_line_start+1)*line_height, gimmie_placement()->w, (selection_line_end)*line_height, selection_color);
			}
		}
	}
}

void FGUITextArea::on_key_down()
{
	int keycode = af::current_event->keyboard.keycode;
	if (keycode == ALLEGRO_KEY_RSHIFT || keycode == ALLEGRO_KEY_LSHIFT)
	{
		// note: this should still activate even if the textarea widget is not focused
		cursor.set_anchor_down();
	}

	if (!focused) return;
}

void FGUITextArea::on_key_up()
{
	int keycode = af::current_event->keyboard.keycode;
	if (keycode == ALLEGRO_KEY_RSHIFT || keycode == ALLEGRO_KEY_LSHIFT)
	{
		// note: this should still activate even if the textarea widget is not focused
		cursor.set_anchor_up();
	}

	if (!focused) return;
}

std::string FGUITextArea::get_selection()
{
	return full_text.substr(cursor.get_start(), cursor.get_selection_length());
}

void FGUITextArea::on_key_char()
{
	if (!focused) return;

	cursor_blink_counter = 1;

	int unichar = af::current_event->keyboard.unichar;
	int keycode = af::current_event->keyboard.keycode;
	int modifier = af::current_event->keyboard.modifiers;

	// test cut-copy-paste
	if ((keycode == ALLEGRO_KEY_C
		|| keycode == ALLEGRO_KEY_X
		|| keycode == ALLEGRO_KEY_V
		|| keycode == ALLEGRO_KEY_A)
		&& af::current_event->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL)
	{
		//std::cout << "cut-copy-pate" << std::endl;
		if (keycode == ALLEGRO_KEY_A)
		{
			cursor.move_to(0);
			cursor.move_anchor_to_cursor();
			cursor.set_anchor_down();
			cursor.move_to(full_text.length());
			cursor.set_anchor_up();
		}
		else if (keycode == ALLEGRO_KEY_X)
		{
			WinClipboard::set(get_selection());
			erase_selection();
		}
		else if (keycode == ALLEGRO_KEY_C)
		{
			WinClipboard::set(get_selection());
		}
		else if (keycode == ALLEGRO_KEY_V)
		{
			erase_selection();
			insert_text(WinClipboard::get());
		}

		return;
	}
	else if ((unichar >= ' ') && (unichar <= '~'))
	{
		if (cursor.selection_active()) erase_selection();
		full_text.insert(cursor.pos(), tostring((char)unichar));
		move_cursor(1);
		cursor.move_anchor_to_cursor();
	}
	else if (keycode == ALLEGRO_KEY_ENTER)
	{
		full_text.insert(cursor.pos(), "\n");
		move_cursor(1);
	}
	else if (keycode == ALLEGRO_KEY_RIGHT)
	{
		if (af::current_event->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL)
		{
			move_cursor_to_next_of("null");
		}
		else
		{
			move_cursor(1);
		}
	}
	else if (keycode == ALLEGRO_KEY_LEFT)
	{
		if (af::current_event->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL)
		{
			move_cursor_to_previous_not_of("null");
		}
		else
		{
			move_cursor(-1);
		}
	}
	else if (keycode == ALLEGRO_KEY_BACKSPACE)
	{
		if (cursor.selection_active())
		{
			erase_selection();
		}
		else
		{
			move_cursor(-1);
			full_text.erase(cursor.pos(), 1);
		}
	}
	else if (keycode == ALLEGRO_KEY_DELETE)
	{
		if (cursor.selection_active())
		{
			erase_selection();
		}
		else
		{
			full_text.erase(cursor.pos(), 1);
		}
	}
}



