#include "CtrlLib.h"
#include "CtrlLib.h"

namespace Upp {

CtrlFrame& DropFrame();

void PopUpList::Clear()
{
	items.Clear();
	lineinfo.Clear();
	linedisplay.Clear();
	cursor = -1;
	if(popup)
		popup->ac.Clear();
}

void PopUpList::Remove(int i)
{
	items.Remove(i);
	if(i < lineinfo.GetCount())
		lineinfo.Remove(i);
	if(i < linedisplay.GetCount())
		linedisplay.Remove(i);
	if(popup)
		popup->ac.Remove(i);
	if(cursor > i)
		cursor--;
}

void PopUpList::Insert(int i, const Value& v)
{
	items.Insert(i, v);
	if(i < lineinfo.GetCount())
		lineinfo.Insert(i, 0x7fff);
	if(i < linedisplay.GetCount())
		linedisplay.Insert(i, nullptr);
	if(popup)
		popup->ac.Insert(i, Vector<Value>() << v);
	if(cursor >= i)
		cursor++;
}

void PopUpList::SetCount(int n)
{
	items.SetCount(n);
	if(popup)
		popup->ac.SetCount(n);
}

void PopUpList::AddSeparator()
{
	word& x = lineinfo.At(items.GetCount(), 0x7fff);
	x |= 0x8000;
	items.Add(Null);
	if(popup)
		popup->ac.AddSeparator();
}

void PopUpList::Add(const Value& v)
{
	items.Add(v);
	if(popup)
		popup->ac.Add(v);
}

void PopUpList::SetLineCy(int ii, int cy)
{
	ASSERT(cy >= 0 && cy < 32000);
	word& x = lineinfo.At(ii, 0x7fff);
	x = (x & 0x8000) | cy;
	if(popup)
		popup->ac.SetLineCy(ii, cy);
}

int PopUpList::GetLineCy(int ii) const
{
	if(ii >= 0 && ii < lineinfo.GetCount()) {
		word h = lineinfo[ii] & 0x7fff;
		if(h != 0x7fff)
			return h;
	}
	return linecy;
}

bool PopUpList::IsLineEnabled(int ii) const
{
	return !(ii >= 0 && ii < lineinfo.GetCount() && ((lineinfo[ii] & 0x8000) || (lineinfo[ii] & 0x7fff) == 0));
}

void PopUpList::Set(int i, const Value& v)
{
	items.At(i) = v;
	if(popup)
		popup->ac.Set(i, 0, v);
}

int PopUpList::Find(const Value& v) const
{
	return FindIndex(items, v);
}

void PopUpList::SetScrollBarStyle(const ScrollBar::Style& s)
{
	sb_style = &s;
	if(popup)
		popup->ac.SetScrollBarStyle(*sb_style);
}

void PopUpList::SetLineCy(int cy)
{
	linecy = cy;
	if(popup)
		popup->ac.SetLineCy(linecy);
}

void PopUpList::SetDisplay(const Display& d)
{
	display = &d;
	if(popup)
		popup->ac.ColumnAt(0).SetDisplay(d);
}

void PopUpList::SetDisplay(int i, const Display& d)
{
	linedisplay.At(i, nullptr) = &d;
	if(popup)
		popup->ac.SetDisplay(i, 0, d);
}

const Display& PopUpList::GetDisplay(int i) const
{
	if(i >= 0 && i < linedisplay.GetCount() && linedisplay[i])
		return *linedisplay[i];
	return *display;
}

void PopUpList::SetConvert(const Convert& c)
{
	convert = &c;
	if(popup)
		popup->ac.ColumnAt(0).SetConvert(c);
}

void PopUpList::SetCursor(int i)
{
	cursor = i;
	if(popup)
		popup->ac.SetCursor(i);
}

int PopUpList::GetCursor() const
{
	return popup ? popup->ac.GetCursor() : cursor;
}

bool PopUpList::Key(int c)
{
	int q = GetCursor();
	q = q >= 0 ? q + 1 : 0;
	c = ToUpperAscii(c);
	for(int i = 0; i < GetCount(); i++) {
		int ii = (q + i) % GetCount();
		if(ToUpperAscii(*StdFormat(items[ii]).ToWString()) == c) {
			SetCursor(ii);
			return true;
		}
	}
	return false;
}

void PopUpList::PopupCancelMode() {
	if(popup && !inpopup)
		DoCancel();
}

void PopUpList::DoClose() {
	if(!inpopup && popup) {
		popup->closing = true; // prevent infinite recursion
		cursor = popup->ac.GetCursor();
		if(permanent) {
			popup->Close();
			popup->closing = false;
		}
		else
			popup.Clear();
	}
}

void PopUpList::PopupDeactivate() {
	if(popup) {
		Ctrl::IgnoreMouseClick();
		DoCancel();
	}
}

void PopUpList::PopupArrayCtrl::LeftUp(Point p, dword keyflags)
{
	ArrayCtrl::LeftUp(p, keyflags);
	list->DoSelect();
}

bool PopUpList::PopupArrayCtrl::Key(dword key, int n)
{
	switch(key) {
	case K_ENTER:
	case K_ALT_DOWN:
		list->DoSelect();
		return true;
	case K_ESCAPE:
		list->DoCancel();
		return true;
	case K_UP:
		if(!IsCursor()) {
			SetCursor(GetCount() - 1);
			return true;
		}
		break;
	case K_DOWN:
		if(!IsCursor()) {
			SetCursor(0);
			return true;
		}
		break;
	}
	return list->Key(key) || ArrayCtrl::Key(key, n);
}

PopUpList::Popup::Popup(PopUpList *list)
:	list(list)
{
	ac.list = list;
	ac.SetFrame(DropFrame());
	auto& col = ac.AddColumn().Accel();
	if(list->convert)
		col.SetConvert(*list->convert);
	col.SetDisplay(*list->display);
	ac.NoHeader();
	ac.HeaderTab(0).SetMargin(0);
	ac.MouseMoveCursor();
	ac.NoGrid();
	ac.AutoHideSb();
	ac.SetLineCy(list->GetLineCy());
	for(int i = 0; i < list->items.GetCount(); i++) {
		Value v = list->items[i];
		word w = i < list->lineinfo.GetCount() ? list->lineinfo[i] : 0x7fff;
		if(w & 0x8000)
			ac.AddSeparator();
		else
			ac.Add(v);
		w &= 0x7fff;
		if(w != 0x7fff)
			ac.SetLineCy(i, w);
	}
	ac.SetCursor(list->GetCursor());
	ac.CenterCursor();
}

ArrayCtrl& PopUpList::Permanent()
{
	if(!popup)
		popup.Create(this);
	permanent = true;
	return popup->ac;
}

void PopUpList::PopUp(Ctrl *owner, int x, int top, int bottom, int width) {
	if(inpopup)
		return;
	DoClose();
	inpopup++;
	if(!popup)
		popup.Create(this);
	int h = popup->ac.AddFrameSize(width, min(droplines * popup->ac.GetLineCy(), popup->ac.GetTotalCy())).cy;
	Rect rt = RectC(x, bottom, width, h);
	Rect area = Ctrl::GetWorkArea(Point(x, top));
	bool up = false;
	if(rt.bottom > area.bottom) {
		up = true;
		rt.top = top - h;
		rt.bottom = rt.top + h;
	}

	if(up) {
		popup->SetRect(Rect(rt.left, rt.bottom - 1, rt.right, rt.bottom));
		popup->Add(popup->ac.TopPos(0, rt.Height()).LeftPos(0, rt.Width()));
	}
	else {
		popup->SetRect(Rect(rt.left, rt.top, rt.right, rt.top + 1));
		popup->Add(popup->ac.BottomPos(0, rt.Height()).LeftPos(0, rt.Width()));
	}

	if(GUI_PopUpEffect()) {
		popup->ac.CenterCursor();
		popup->PopUp(owner, true, true, GUI_DropShadows());
		popup->ac.SetFocus();
		Ctrl::ProcessEvents();
		Animate(*popup, rt, GUIEFFECT_SLIDE);
	}
	if(popup && !popup->IsOpen()) {
		popup->SetRect(rt);
		if(!popup->IsOpen())
			popup->PopUp(owner, true, true, GUI_DropShadows());
		popup->ac.CenterCursor();
		popup->ac.SetFocus();
	}

	popup->ac.SizePos(); // the size of popup can be slightly bigger than requested
	inpopup--;
}

void PopUpList::PopUp(Ctrl *owner, int width)
{
	Rect r = owner->GetScreenRect();
	if(width)
		r.right = r.left + width;
	PopUp(owner, r.left, r.top, r.bottom, r.Width());
}

void PopUpList::PopUp(Ctrl *owner)
{
	Rect r = owner->GetScreenRect();
	PopUp(owner, r.left, r.top, r.bottom, r.Width());
}

void PopUpList::DoSelect()
{
	DoClose();
	WhenSelect();
}

void PopUpList::DoCancel()
{
	DoClose();
	WhenCancel();
}

PopUpList::PopUpList() {
	droplines = 16;
	inpopup = 0;
	permanent = false;
	linecy = GetStdFontCyA();
	display = &StdDisplay();
	convert = NULL;
}

PopUpList::~PopUpList() {}

}
