#include "empire.h"

const int listbox_textheight = 13;
const int listbox_scrollbarwidth = 10;

_listbox::_listbox() :
	up (_button()),
	down (_button()),
	x (0),
	y (0),
	w (0),
	h (0),
	listsize (0),
	sel (-1),
	offset (0),
	delay (0),
	mouse_zval (0),
	list (0)
{}

_listbox::_listbox(const _listbox &l) :
	up (l.up),
	down (l.down),
	x (l.x),
	y (l.y),
	w (l.w),
	h (l.h),
	listsize (l.listsize),
	sel (l.sel),
	offset (l.offset),
	delay (l.delay),
	mouse_zval (l.mouse_zval),
	list (0)
{
	if (listsize > 0)
	{
		list = (char **)malloc(sizeof(char *) * listsize);
		for (int i = 0; i < listsize; i++)
			list[i] = strdup(l.list[i]);
	}
}

_listbox &_listbox::operator=(const _listbox &l)
{
	clear();
	
	up = l.up;
	down = l.down;
	x = l.x;
	y = l.y;
	w = l.w;
	h = l.h;
	listsize = l.listsize;
	sel = l.sel;
	offset = l.offset;
	delay = l.delay;
	mouse_zval = l.mouse_zval;
	list = 0;
	
	if (listsize > 0)
	{
		list = (char **)malloc(sizeof(char *) * listsize);
		for (int i = 0; i < listsize; i++)
			list[i] = strdup(l.list[i]);
	}
	
	return *this;
}

_listbox::~_listbox()
{
	clear();
}

void _listbox::init(int _x, int _y, int _w, int _h)
{
	clear();
	x = _x;
	y = _y;
	w = _w - listbox_scrollbarwidth;
	h = _h;
	listsize = 0;
	sel = -1;
	offset = 0;
	list = NULL;
	up.init(x + w + 1, y - 1, listbox_scrollbarwidth, 10);
	down.init(x + w + 1, y + h - 9, listbox_scrollbarwidth, 10);
	mouse_zval = mouse_z;
}

void _listbox::additem(const string &text, bool textwrapping)
{
	additem(text.c_str(), textwrapping);
}

void _listbox::additem(const char *text, bool textwrapping)
{  //splits the input text into lines, and wraps any text that's too long
	vector<string> lines;
	string alltext;
	string currentline;
	size_t oldpos = 0, pos = 0;

	if (text == NULL) return;
	
	alltext = text;
	
	pos = alltext.find("\n", oldpos);
	while (pos != string::npos)
	{  //separate the text by newlines into a vector of strings
		lines.push_back(alltext.substr(oldpos, pos - oldpos));
		oldpos = pos + 1;
		pos = alltext.find("\n", oldpos);
	}
	lines.push_back(alltext.substr(oldpos));
	
	for (unsigned int i = 0; i < lines.size(); i++)
	{
		string s = lines[i];
		
		if (textwrapping)
		{
			currentline = "";
			oldpos = 0;
			pos = s.find(" ", oldpos);
			
			while (pos != string::npos)  //while there are more words in the line...
			{  //if the text won't fit in the box, wrap it to the next line
				if (normal.Width(currentline + " " + s.substr(oldpos, pos - oldpos)) >= w - 3)
				{  //text would would be too long, add what we have and start a new line
					addline(currentline.c_str());
					currentline = "";
				}
				else if (currentline != "")
				{  //if this word isn't the very first word, add a space to the string we're building
					currentline += " ";
				}
				
				currentline += s.substr(oldpos, pos - oldpos);
				oldpos = pos + 1;
				pos = s.find(" ", oldpos);
			}
			
			//last word before the end of the string
			if (normal.Width(currentline + " " + s.substr(oldpos)) >= w - 3)
			{  //text would would be too long, add what we have and start a new line
				addline(currentline.c_str());
				currentline = "";
			}
			else if (currentline != "")
			{  //if it's not the very first word, add a space
				currentline += " ";
			}
			
			currentline += s.substr(oldpos);
			addline(currentline);
		}
		else
		{  //no text wrapping, just add the line as it is
			addline(s);
		}
	}
}

void _listbox::addline(const string &text)
{
	addline(text.c_str());
}

void _listbox::addline(const char *text)
{
	char *item;
	
	if (text == NULL) return;
	
	item = (char *)malloc(sizeof(char) * (strlen(text) + 1));
	strcpy(item, text);
	
	listsize++;
	if (list != NULL)
	{
		list = (char **)realloc(list, sizeof(char *) * listsize);
		list[listsize - 1] = item;
		return;
	}
	list = (char **)malloc(sizeof(char *));
	list[0] = item;
}

int _listbox::size() const
{
	return listsize;
}

char *_listbox::getitem(int i) const
{
	return list[i];
}

void _listbox::clear()
{
	int i = 0;
	
	listsize = 0;
	sel = -1;
	offset = 0;
	delay = 0;

	if (list == NULL) return;	
	while (i < listsize)
	{
		free(list[i]);
		list[i] = NULL;
		i++;
	}
	free(list);
	list = NULL;
}

void _listbox::check(bool selectable)
{
	if (delay <= 0)
	{
		if (up.mousedown())
		{
			scroll(-1);
			delay = 5;
		}
		else if (down.mousedown())
		{
			scroll(1);
			delay = 5;
		}
		else if ((sel != -1) && (key[KEY_UP]))
		{
			if ((mouse_x > x) && (mouse_x < x + w - 2))
			{
				if ((mouse_y > y) && (mouse_y < y + h))
				{
					if (sel > 0) sel--;
					if (sel < offset) scroll(-1);
					delay = 5;
				}
			}
		}
		else if ((sel != -1) && (key[KEY_DOWN]))
		{
			if ((mouse_x > x) && (mouse_x < x + w - 2))
			{
				if ((mouse_y > y) && (mouse_y < y + h))
				{
					if (sel < listsize - 1) sel++;
					if (sel > offset + (h / listbox_textheight) - 2) scroll(1);
					delay = 5;
				}
			}
		}
		else if (mouse_zval != mouse_z)
		{
			if ((mouse_x > x) && (mouse_x < x + w) && (mouse_y > y) && (mouse_y < y + h))
			{  //if the mouse is over the box
				if (mouse_z > mouse_zval)
				{
					scroll(-1);
				}
				else
				{
					scroll(1);
				}
			}
			mouse_zval = mouse_z;
		}
	}
	else
	{
		delay--;
	}
	
	if (mouse_clicked() && selectable)
	{
		if ((mouse_x > x) && (mouse_x < x + w - 2))
		{
			if ((mouse_y > y) && (mouse_y < y + h))
			{
				sel = ((mouse_y - y) / listbox_textheight) + offset;
				if (sel >= listsize) sel = -1;
			}
		}
	}
	
	draw();
}

void _listbox::draw(float alpha, bool textonly, bool reverseorder)
{
	int i = 0;
	float bgalpha = alpha - 0.35;
	if (bgalpha < 0) bgalpha = 0;
	
	buffer_rectfill(x, y, x + w + listbox_scrollbarwidth, y + h, BLACK, bgalpha);
	
	if (!textonly)
	{
		buffer_rect(x, y, x + w, y + h, GREY);
	}
	
	
	while ((i + offset < listsize) && ((i + 1) * listbox_textheight <= h))
	{
		if ((!textonly) && (i + offset == sel))
		{
			buffer_rectfill(x, y + listbox_textheight * i, x + w, y + listbox_textheight * (i + 1), GREY, 0.5);
		}
		if (reverseorder)
		{
			clipped_textout_ex(normal, list[listsize - 1 - i], x + 2, y + h - listbox_textheight * (i + 1), w - 3, WHITE, alpha);
		}
		else
		{
			clipped_textout_ex(normal, list[i + offset], x + 2, y + listbox_textheight * i, w - 3, WHITE, alpha);
		}
		i++;
	}
	
	if (!textonly)
	{
		buffer_vline(x + w + listbox_scrollbarwidth, y, y + h, GREY);
		up.check();
		down.check();
	}
}

void _listbox::scroll(int n)
{
	int itemslisted = h / listbox_textheight;
	
	offset += n;
	if (offset > listsize - itemslisted) offset = listsize - itemslisted;
	if (offset < 0) offset = 0;
}

void _listbox::scroll_to_end()
{
	int itemslisted = h / listbox_textheight;
	
	offset = listsize - itemslisted;
	if (offset < 0) offset = 0;
}

void _listbox::sort()
{
	char **temp = (char**)malloc(sizeof(char *) * listsize);
	char *atemp;
	int i = 0, j, min;
	
	while (i < listsize)
	{
		temp[i] = list[i];
		i++;
	}
	
	i = 0;
  while (i < listsize - 1)
  {  //  arrange filenames (the sort is kind of slow, but shouldn't take too long)
    min = i;
    j = i;
    while (j < listsize)
    {
      if (strcmp(temp[j], temp[min]) < 0)
      {
        min = j;
      }
      j++;
    }
    if (min != i)
    {
      atemp = temp[i];
      temp[i] = temp[min];
      temp[min] = atemp;
    }
    i++;
  }
	free(list);
	list = temp;
	
	counter = 0;  //but in case it does take a while, don't let it affect the program too badly
}

void _listbox::setsel(int i)
{
	sel = i;
}

char *_listbox::selected() const
{
	if (sel == -1) return NULL;
	return list[sel];
}

int _listbox::getsel() const
{
	return sel;
}

void _listbox::unselect()
{
	sel = -1;
}

int _listbox::get_x() const
{
	return x;
}

int _listbox::get_y() const
{
	return y;
}

int _listbox::get_w() const
{
	return w;
}

int _listbox::get_h() const
{
	return h;
}
