#include <CtrlCore/CtrlCore.h>

#ifdef GUI_GTK

namespace Upp {

#define LLOG(x)  // DLOG(x)

void TopWindow::SyncSizeHints()
{
	GuiLock __;
	if(!top)
		return;
	Size sz0 = GetRect().GetSize();
	LLOG("SyncSizeHints sz0: " << sz0 << ", sizeable: " << sizeable << ", min: " << GetMinSize() << ", max: " << GetMaxSize());
	Top *top = GetTop();
	if(top) {
		int mcx = 0;
		int mcy = 0;

		if(top->csd) {
			mcx += csd_border.left + csd_border.right;
			mcy += csd_border.top + csd_border.bottom;
			if(custom_bar_frame)
				mcy += GetCustomTitleBarMetrics().height;
			else
				mcy += csd_std_header_cy;
		}

		GdkGeometry m;

		m.base_width = sz0.cx;
		m.base_height = sz0.cy;

		Size minsz = sizeable ? GetMinSize() : sz0;
		m.min_width = LSCH(minsz.cx + mcx);
		m.min_height = LSCH(minsz.cy + mcy);

		Size maxsz = sizeable ? GetMaxSize() : sz0;
		m.max_width = LSCH(maxsz.cx + mcx);
		m.max_height = LSCH(maxsz.cy + mcy);

		gtk_window_set_resizable(gtk(), sizeable);
		gtk_window_set_geometry_hints(gtk(), NULL, &m,
		                              GdkWindowHints(GDK_HINT_MIN_SIZE|GDK_HINT_MAX_SIZE|GDK_HINT_BASE_SIZE));
		gtk_widget_set_size_request(top->window, m.min_width, m.min_height);
	}

	SyncCustomBar();
}

void TopWindow::SyncTitle()
{
	GuiLock __;
	if(GetTop())
		gtk_window_set_title(gtk(), FromUnicode(title, CHARSET_UTF8));
}

void TopWindow::SyncCaption()
{
	GuiLock __;
	SyncTitle();
	if(top) {
		GList *icons = NULL;
		if(gdk_icon.Set(icon))
			icons = g_list_append(icons, gdk_icon);
		if(gdk_largeicon.Set(largeicon))
			icons = g_list_append(icons, gdk_largeicon);
		if(icons != NULL) {
			gtk_window_set_icon_list(gtk(), icons);
			g_list_free(icons);
		}
		gtk_window_set_decorated(gtk(), !frameless);
		gtk_window_set_urgency_hint(gtk(), urgent);
	}
}

void TopWindow::CenterRect(Ctrl *owner)
{
	GuiLock __;
	SetupRect(owner);
	if(owner && center == 1 || center == 2) {
		Size sz = GetRect().Size();
		Rect wr = owner ? owner->GetWorkArea() : Ctrl::GetPrimaryWorkArea();
		Rect fm = frameMargins;
		Rect r = (center == 1 && owner ? owner->GetRect() : wr)
		         .CenterRect(sz);
		wr.left += fm.left;
		wr.right -= fm.right;
		wr.top += fm.top;
		wr.bottom -= fm.bottom;
		if(r.top < wr.top) {
			r.top = wr.top;
			r.bottom = r.top + sz.cy;
		}
		if(r.left < wr.left) {
			r.left = wr.left;
			r.right = r.left + sz.cx;
		}
		if(r.bottom > wr.bottom) {
			r.bottom = wr.bottom;
			r.top = r.bottom - sz.cy;
		}
		if(r.right > wr.right) {
			r.right = wr.right;
			r.left = r.right - sz.cx;
		}
		minsize.cx = min(minsize.cx, r.GetWidth());
		minsize.cy = min(minsize.cy, r.GetHeight());
		SetRect(r);
	}
}

gboolean TopWindow::StateEvent(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
{
	TopWindow *w = (TopWindow *)user_data;
	dword h = event->new_window_state;
	int prev = w->state;
	if(h & GDK_WINDOW_STATE_FULLSCREEN)
		w->state = FULLSCREEN;
	else
	if(h & GDK_WINDOW_STATE_ICONIFIED)
		w->state = MINIMIZED;
	else
	if(h & GDK_WINDOW_STATE_MAXIMIZED)
		w->state = MAXIMIZED;
	else {
		w->state = OVERLAPPED;
		w->overlapped = w->GetRect();
	}
	LLOG("StateEvent " << prev << " -> " << (int)w->state);
	w->topmost = h & GDK_WINDOW_STATE_ABOVE;
	w->Layout();
	if(prev == MINIMIZED && w->state != MINIMIZED) {
		prev_mouse_pos = CurrentMousePos = Null; // we lost the track of mouse, otherwise "minimize" button would render highlighted
		if(w->custom_bar_icons)
			w->custom_bar_icons->RefreshFrame();
	}
	return FALSE;
}

void TopWindow::Open(Ctrl *owner)
{
	GuiLock __;
	LLOG("OPEN " << Name() << " owner: " << UPP::Name(owner));
	if(dokeys && (!GUI_AKD_Conservative() || GetAccessKeysDeep() <= 1))
		DistributeAccessKeys();
	if(fullscreen)
		SetRect(GetPrimaryScreenArea());
	else
		CenterRect(owner);
	IgnoreMouseUp();
	Create(owner, false);
	Top *top = GetTop();
	if(top)
		g_signal_connect(top->window, "window-state-event", G_CALLBACK(StateEvent), this);
	SyncSizeHints();
	SyncCaption();
	PlaceFocus();
	int q = state;
	state = OVERLAPPED;
	SetMode(q);
	SyncTopMost();
}

void TopWindow::Open()
{
	Open(GetActiveWindow());
}

void TopWindow::OpenMain()
{
	Open(NULL);
}

void TopWindow::SyncTopMost()
{
	GuiLock __;
	if(top)
		gtk_window_set_keep_above(gtk(), topmost);
}

void TopWindow::SetMode(int mode)
{
	GuiLock __;
	GtkWindow *w = gtk();
	if(w)
		switch(state) {
		case MINIMIZED:
			fullscreen = false;
			gtk_window_iconify(w);
			break;
		case MAXIMIZED:
			fullscreen = false;
			gtk_window_deiconify(w);
			gtk_window_maximize(w);
			break;
		case OVERLAPPED:
			fullscreen = false;
			gtk_window_deiconify(w);
			gtk_window_unmaximize(w);
			break;
		case FULLSCREEN:
			gtk_window_fullscreen(w);
			fullscreen = true;
			break;
		}
}

void TopWindow::Minimize(bool effect)
{
	SetMode(MINIMIZED);
}

TopWindow& TopWindow::FullScreen(bool b)
{
	SetMode(b ? (int)FULLSCREEN : (int)OVERLAPPED);
	return *this;
}

void TopWindow::Maximize(bool effect)
{
	SetMode(MAXIMIZED);
}

void TopWindow::Overlap(bool effect)
{
	SetMode(OVERLAPPED);
}

TopWindow& TopWindow::TopMost(bool b, bool)
{
	GuiLock __;
	topmost = b;
	SyncTopMost();
	return *this;
}

bool TopWindow::IsTopMost() const
{
	GuiLock __;
	return topmost;
}

void TopWindow::GuiPlatformConstruct()
{
	topmost = false;
}

void TopWindow::GuiPlatformDestruct()
{
}

void TopWindow::SerializePlacement(Stream& s, bool reminimize)
{
	GuiLock __;
	int version = 0;
	s / version;
	Rect rect = GetRect();
	s % overlapped % rect;
	bool mn = state == MINIMIZED;
	bool mx = state == MAXIMIZED;
	s.Pack(mn, mx);
	LLOG("TopWindow::SerializePlacement / " << (s.IsStoring() ? "write" : "read"));
	LLOG("minimized = " << mn << ", maximized = " << mx);
	LLOG("rect = " << rect << ", overlapped = " << overlapped);
	if(s.IsLoading()) {
		if(mn) rect = overlapped;
		Rect limit = GetVirtualWorkArea();
		Rect fm = frameMargins;
		limit.left += fm.left;
		limit.right -= fm.right;
		limit.top += fm.top;
		limit.bottom -= fm.bottom;
		Size sz = min(rect.Size(), limit.Size());
		rect = RectC(
			minmax(rect.left, limit.left, limit.right - sz.cx),
			minmax(rect.top,  limit.top,  limit.bottom - sz.cy),
			sz.cx, sz.cy);
		state = OVERLAPPED;
		if(mn && reminimize)
			state = MINIMIZED;
		if(mx)
			state = MAXIMIZED;
		if(state == OVERLAPPED)
			SetRect(rect);
		if(IsOpen()) {
			if(state == MINIMIZED)
				Minimize(false);
			if(state == MAXIMIZED)
				Maximize(false);
		}
	}
}

}

#endif