import recti makeScreenCenteredRect(const dim2di &in rectSize) from "gui_lib";

class DockedHull {
	const HullLayout@ layout;
	int docked;
	int undocking;

	DockedHull(const HullLayout@ lay) {
		@layout = lay;
		docked = 1;
		undocking = 0;
	}

	int opCmp(DockedHull@ other) const {
		if (layout.scale < other.layout.scale)
			return -1;
		if (layout.scale > other.layout.scale)
			return 1;
		return 0;
	}
};

class UndockWindow : GuiCallback {
	Object@ obj;
	int id;

	DockedHull@[] hulls;
	DockedHull@[] leftList;
	DockedHull@[] rightList;

	GuiDraggable@ win;
	GuiExtText@ text;
	GuiButton@ ok;
	GuiButton@ close;
	GuiButton@ clear;

	GuiEditBox@ moveLeftCount;
	GuiEditBox@ moveRightCount;

	GuiButton@ MoveRight;
	GuiButton@ MoveLeft;
	GuiButton@ moveAllRight;

	GuiListBox@ store;
	GuiListBox@ chosen;

	UndockWindow(Object@ forObj) {
		@obj = forObj;
		id = reserveGuiID();

		@win = GuiDraggable(getSkinnable("Dialog"), makeScreenCenteredRect(dim2di(500, 355)), true, null);
		@text = GuiExtText(recti(pos2di(14, 24), dim2di(422, 20)), win);
		text.setText(localize("#DM_Undock"));

		@close = GuiButton(getSkinnable("CloseButton"), recti(pos2di(470, 0), dim2di(30, 12)), null, win);

		@store = GuiListBox(recti(pos2di(14, 46), dim2di(190, 265)), true, win);
		@chosen = GuiListBox(recti(pos2di(296, 46), dim2di(190, 265)), true, win);

		@MoveRight = GuiButton(getSkinnable("Button"), recti(pos2di(260, 123), dim2di(30, 21)), ">", win);
		@moveRightCount = GuiEditBox(recti(pos2di(210, 123), dim2di(45, 21)), "1", true, win);

		@MoveLeft = GuiButton(getSkinnable("Button"), recti(pos2di(210, 149), dim2di(30, 21)), "<", win);
		@moveLeftCount = GuiEditBox(recti(pos2di(245, 149), dim2di(45, 21)), "1", true, win);

		@moveAllRight = GuiButton(getSkinnable("Button"), recti(pos2di(114, 320), dim2di(80, 20)), localize("#all")+" >", win);

		@clear = GuiButton(getSkinnable("Button"), recti(pos2di(306, 320), dim2di(80, 20)), "< "+localize("#all"), win);
		@ok = GuiButton(getSkinnable("Button"), recti(pos2di(396, 320), dim2di(80, 20)), localize("#undock"), win);

		ok.setID(id);
		clear.setID(id);
		MoveRight.setID(id);
		MoveLeft.setID(id);
		moveRightCount.setID(id);
		moveLeftCount.setID(id);
		moveAllRight.setID(id);
		close.setID(id);
		store.setID(id);
		chosen.setID(id);
		win.setID(id);

		bindEscapeEvent(win);
		bindGuiCallback(id, this);
		updateDocked();
	}

	void updateDocked() {
		ObjDockedList list;
		list.prepare(obj);

		hulls.resize(0);
		for (uint i = 0; i < list.childCount; ++i) {
			HulledObj@ child = list.getChild(i);
			if (child is null) continue;

			const HullLayout@ lay = child.getHull();
			bool found = false;
			for (uint j = 0; j < hulls.length(); ++j) {
				if (hulls[j].layout is lay) {
					++hulls[j].docked;
					found = true;
					break;
				}
			}

			if (!found) {
				uint n = hulls.length();
				hulls.resize(n+1);
				@hulls[n] = DockedHull(lay);
			}
		}

		store.clear();
		chosen.clear();

		leftList.resize(hulls.length());
		rightList.resize(0);
		for (uint i = 0; i < hulls.length(); ++i)
			@leftList[i] = hulls[i];
		leftList.sort(true);
		refreshItems(null);
	}

	void refreshItems(const HullLayout@ refreshOnly) {
		// Update item counts in left list
		uint leftCnt = leftList.length();
		uint storedCnt = store.getItemCount();

		for (uint i = 0; i < leftCnt; ++i) {
			DockedHull@ hull = leftList[i];
			if (refreshOnly !is null && hull.layout !is refreshOnly)
				continue;

			string@ text = i_to_s(hull.docked)+"x "+hull.layout.getName();
			if (i < storedCnt)
				store.setItem(i, text);
			else
				store.addItem(text);
		}
		for (int i = int(storedCnt) - 1; i >= int(leftCnt); --i)
			store.removeItem(i);
		if (store.getItemCount() > 0 && store.getSelected() < 0)
			store.setSelected(0);

		// Update item counts in right list
		uint rightCnt = rightList.length();
		uint chosenCnt = chosen.getItemCount();
		for (uint i = 0; i < rightCnt; ++i) {
			DockedHull@ hull = rightList[i];
			if (refreshOnly !is null && hull.layout !is refreshOnly)
				continue;

			string@ text = i_to_s(hull.undocking)+"x "+hull.layout.getName();
			if (i < chosenCnt)
				chosen.setItem(i, text);
			else
				chosen.addItem(text);
		}
		for (int i = int(chosenCnt) - 1; i >= int(rightCnt); --i)
			chosen.removeItem(i);
		if (chosen.getItemCount() > 0 && chosen.getSelected() < 0)
			chosen.setSelected(0);
	}

	void moveRight(int pos, int num) {
		DockedHull@ item = leftList[pos];

		num = min(num, item.docked);
		if (num == 0) return;

		item.docked -= num;
		item.undocking += num;

		bool fullRefresh = false;
		if (item.undocking == num) {
			uint n = rightList.length();
			rightList.resize(n+1);
			@rightList[n] = item;
			rightList.sort(true);
			fullRefresh = true;
		}
		if (item.docked == 0) {
			fullRefresh = true;
			leftList.erase(pos);

			if (store.getSelected() == pos)
				store.setSelected(min(store.getItemCount() - 2, pos + 1));
		}

		if (fullRefresh)
			refreshItems(null);
		else
			refreshItems(item.layout);
	}

	void moveLeft(int pos, int num) {
		DockedHull@ item = rightList[pos];

		num = min(num, item.undocking);
		if (num == 0) return;

		item.undocking -= num;
		item.docked += num;

		bool fullRefresh = false;
		if (item.docked == num) {
			uint n = leftList.length();
			leftList.resize(n+1);
			@leftList[n] = item;
			leftList.sort(true);
			fullRefresh = true;
		}
		if (item.undocking == 0) {
			fullRefresh = true;
			rightList.erase(pos);

			if (chosen.getSelected() == pos)
				chosen.setSelected(min(chosen.getItemCount() - 2, pos + 1));
		}

		if (fullRefresh)
			refreshItems(null);
		else
			refreshItems(item.layout);
	}

	void doUndock() {
		ObjDockedList list;
		list.prepare(obj);

		for (uint i = 0; i < list.childCount; ++i) {
			HulledObj@ child = list.getChild(i);
			if (child is null) continue;

			const HullLayout@ lay = child.getHull();

			for (uint j = 0; j < hulls.length(); ++j) {
				if (hulls[j].layout is lay) {
					if (hulls[j].undocking > 0) {
						--hulls[j].undocking;
						--i;
						obj.undock(child.toObject());
					}
					break;
				}
			}
		}
	}

	void update(float time) {
	}

	void remove() {
		clearGuiCallback(id);
		clearEscapeEvent(win);
		win.remove();
	}

	bool OnEvent(const GUIEvent& evt) {
		switch (evt.EventType) {
			case GEVT_Focus_Gained:
				bindEscapeEvent(win);
			break;
			case GEVT_Closed:
				removeWin(this);
			break;
			case GEVT_Clicked:
			case GEVT_EditBox_Enter_Pressed:
				if (evt.Caller is MoveRight || evt.Caller is moveRightCount) {
					int pos = store.getSelected();
					if (pos >= 0 && pos < int(leftList.length()))
						moveRight(pos, s_to_i(moveRightCount.getText()));
				}
				else if (evt.Caller is MoveLeft || evt.Caller is moveLeftCount) {
					int pos = chosen.getSelected();
					if (pos >= 0 && pos < int(rightList.length()))
						moveLeft(pos, s_to_i(moveLeftCount.getText()));
				}
				else if (evt.Caller is ok) {
					doUndock();
					removeWin(this);
				}
				else if (evt.Caller is close) {
					removeWin(this);
				}
				else if (evt.Caller is clear) {
					while(rightList.length() > 0)
						moveLeft(0, rightList[0].undocking);
				}
				else if (evt.Caller is moveAllRight) {
					while(leftList.length() > 0)
						moveRight(0, leftList[0].docked);
				}
			break;
			case GEVT_Listbox_Selected_Again:
				if (evt.Caller is store) {
					int pos = store.getSelected();
					if (pos >= 0 && pos < int(leftList.length()))
						moveRight(pos, shiftKey ? leftList[pos].docked : 1);
				}
				else if (evt.Caller is chosen) {
					int pos = chosen.getSelected();
					if (pos >= 0 && pos < int(rightList.length()))
						moveLeft(pos, shiftKey ? rightList[pos].undocking : 1);
				}
			break;
		}
		return false;
	}
};

UndockWindow@[] wins;
void triggerUndockWin(Object@ obj) {
	uint n = wins.length();
	wins.resize(n+1);

	@wins[n] = UndockWindow(obj);
}

void removeWin(UndockWindow@ win) {
	uint cnt = wins.length();
	for (uint i = 0; i < cnt; ++i) {
		if (wins[i] is win) {
			wins[i].remove();
			wins.erase(i);
			return;
		}
	}
}

void tick(float time) {
	uint cnt = wins.length();
	for (uint i = 0; i < cnt; ++i)
		wins[i].update(time);
}
