/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "ui/ui_window.h"
		#include "ui/widget_text.h"
		#include "core/renderer_toolbox.h"
		#include "core/render_resource_factory.h"

		using namespace GS;
		using namespace GS::S2D;


//------------------------------------------------------------------------------
iRect				Window::GetRect() const
{
	if	(!window_flags.IsSet(Window::FlagNoDecoration))
		if	(skin.IsValid())
			return iRect(0, 0, (int)size.x, (int)skin->top->GetHeight());
	return iRect(0, 0, (int)size.x, (int)size.y);
}
iRect				Window::GetClientRect() const
{
	if	(!window_flags.IsSet(Window::FlagNoDecoration))
		if	(skin.IsValid())
			return iRect(skin->left->GetWidth(), skin->top_left->GetHeight(), int(size.x - skin->right->GetWidth()), int(size.y - skin->bottom_right->GetHeight()));
	return iRect(0, 0, (int)size.x, (int)size.y);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::SetSize(float _w, float _h)
{
	uint w = (uint)_w, h = (uint)_h;

	if	(cache.IsValid())
	{
		if	(w > cache->GetWidth() || h > cache->GetHeight() || w < (cache->GetWidth() / 2) || h < (cache->GetHeight() / 2))
		{
			cache->AllocAs(w, h);
			if	(render_data && render_data->texture)
				render_data->texture->Resize(w, h);
		}
	}
	Invalidate();

	Sprite::SetSize(_w, _h);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Widget				*Window::GetTitleWidget() const
{	return title_widget;	}
void				Window::SetTitleWidget(Widget *w)
{
	title_widget = w;
	Invalidate();
}
Widget				*Window::GetBaseWidget() const
{	return base_widget;	}
void				Window::SetBaseWidget(Widget *w)
{
	base_widget = w;
	Invalidate();
}
Widget				*Window::GetWidget(uint id)
{	return base_widget ? base_widget->GetChild(id) : NULL;	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::ComposeSkin()
{
	if	(skin.IsNull())
		return;

	cache->Fill(0, 0, 0, 0);

	// Fill main area.
	Rect <int> rect(GetRect());
	rect.sx = skin->top_left->GetWidth();
	rect.sy = skin->top_left->GetHeight();
	rect.ex -= skin->bottom_right->GetWidth();
	rect.ey -= skin->bottom_right->GetHeight();
	Color _skin_color(skin->color);
	cache->Fill(_skin_color.x, _skin_color.y, _skin_color.z, _skin_color.w, &rect);

	// Blit corners.
	Picture::Blit(*skin->top_left, *cache);

	rect = GetRect();
	rect.sx = rect.ex - skin->top_right->GetWidth();
	Picture::Blit(*skin->top_right, *cache, NULL, &rect);

	rect = GetRect();
	rect.sy = rect.ey - skin->bottom_left->GetHeight();
	Picture::Blit(*skin->bottom_left, *cache, NULL, &rect);

	rect = GetRect();
	rect.sx = rect.ex - skin->bottom_right->GetWidth();
	rect.sy = rect.ey - skin->bottom_right->GetHeight();
	Picture::Blit(*skin->bottom_right, *cache, NULL, &rect);

	// Window left side.
	{
		int height = GetRect().GetHeight() - skin->top_left->GetHeight() - skin->bottom_left->GetHeight();

		int *dst = (int *)cache->GetData();
		dst += skin->top_left->GetHeight() * cache->GetWidth();

		int *src = 0;
		for	(int s = 0, _s = 0; s < height; ++s)
		{
			if	(!src || (++_s == (int)skin->left->GetHeight()))
			{
				src = (int *)skin->left->GetData();
				_s = 0;
			}
			for	(uint x = 0; x < skin->left->GetWidth(); ++x)
				dst[x] = src[x];
			src += skin->left->GetWidth();
			dst += cache->GetWidth();
		}
	}

	// Window right side.
	{
		int height = GetRect().GetHeight() - skin->top_right->GetHeight() - skin->bottom_right->GetHeight();

		int *dst = (int *)cache->GetData();
		dst += skin->top_right->GetHeight() * cache->GetWidth() + GetRect().ex - skin->right->GetWidth();

		int *src = 0;
		for	(int s = 0, _s = 0; s < height; ++s)
		{
			if	(!src || (++_s == (int)skin->right->GetHeight()))
			{
				src = (int *)skin->right->GetData();
				_s = 0;
			}
			for	(uint x = 0; x < skin->right->GetWidth(); ++x)
				dst[x] = src[x];
			src += skin->right->GetWidth();
			dst += cache->GetWidth();
		}
	}

	// Window top side.
	{
		int block_width = skin->top->GetWidth();
		int width = GetRect().GetWidth() - skin->top_left->GetWidth() - skin->top_right->GetWidth(), height = skin->top->GetHeight();

		int *src = (int *)skin->top->GetData(), *dst = (int *)cache->GetData();
		dst += skin->top_left->GetWidth();

		for	(int y = 0; y < height; ++y)
		{
			for	(int x = 0, _x = 0; x < width; ++x)
			{
				dst[x] = src[_x++];
				if	(_x == block_width)
					_x = 0;
			}
			src += skin->top->GetWidth();
			dst += cache->GetWidth();
		}
	}

	// Window bottom side.
	{
		int block_width = skin->bottom->GetWidth();
		int width = GetRect().GetWidth() - skin->bottom_left->GetWidth() - skin->bottom_right->GetWidth(), height = skin->bottom->GetHeight();

		int *src = (int *)skin->bottom->GetData(), *dst = (int *)cache->GetData();
		dst += skin->bottom_left->GetWidth() + (GetRect().ey - height) * cache->GetWidth();

		for	(int y = 0; y < height; ++y)
		{
			for	(int x = 0, _x = 0; x < width; ++x)
			{
				dst[x] = src[_x++];
				if	(_x == block_width)
					_x = 0;
			}
			src += skin->bottom->GetWidth();
			dst += cache->GetWidth();
		}
	}

	//
	{
		int height = GetRect().ey - skin->bottom_right->GetHeight() - skin->top_left->GetHeight(), width = GetRect().GetWidth();

		//
		uint *dst = (uint *)cache->GetData();
		dst += cache->GetWidth() * skin->top_left->GetHeight();// + system.skin_left->GetWidth();
		float alpha = 1.f;

		int segment_height = (height * 70) / 100;
		float alpha_step = (0.85f - alpha) / (float)segment_height;

		for	(int y = 0; y < segment_height; ++y)
		{
			uint int_alpha = (uint(alpha * 255.f)) & 0xff;
		//	uint scan_color = nColor::ARGBtoRGBA((system.skin_color & 0xffffff00) + int_alpha);
			for	(int x = 0; x < width; ++x)
				dst[x] = (((((dst[x] >> 24) & 0xff) * int_alpha) >> 8) << 24) + (dst[x] & 0x00ffffff);//scan_color;
			dst += cache->GetWidth();
			alpha += alpha_step;
		}

		segment_height = height - segment_height;
		alpha_step = (1.f - alpha) / (float)segment_height;

		for	(int y = 0; y < segment_height; ++y)
		{
			uint int_alpha = (uint(alpha * 255.f)) & 0xff;
		//	uint scan_color = nColor::ARGBtoRGBA((system.skin_color & 0xffffff00) + int_alpha);
			for	(int x = 0; x < width; ++x)
				dst[x] = (((((dst[x] >> 24) & 0xff) * int_alpha) >> 8) << 24) + (dst[x] & 0x00ffffff);//scan_color;
			dst += cache->GetWidth();
			alpha += alpha_step;
		}
	}
}
void				Window::Compose()
{
	if	(cache.IsNull())
		return;

	if	(background_picture)
	{
		cache->Fill(0, 0, 0, 0);
		Picture::Blit(*background_picture, *cache);
	}
	else
	{
		bool skinned = false;
		if	(!window_flags.IsSet(Window::FlagNoDecoration))
			if	(skin.IsValid())
				skinned = true;

		if	(skinned)
			ComposeSkin();

		cache->Fill(background_color.x, background_color.y, background_color.z, background_color.w);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::Invalidate()
{
	if	(base_widget)
		base_widget->Invalidate();
	if	(title_widget)
		title_widget->Invalidate();

	window_flags.Raise(FlagCacheDirty | FlagRenderDirty);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::RenderSetup(Core::ResourceFactories *f)
{
	if	((render_data = new Sprite::RenderData))
		if	(f && f->render)
		{
			render_data->texture = f->render->NewTexture();
			render_data->texture->Create(NULL, (int)size.x, (int)size.y, Render::Texture::FormatRGBA8, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));

			window_flags.Raise(FlagRenderDirty);
		}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::Render(Renderer &renderer, GPU::TriangleBatch *batch)
{
	// Compose.
	bool has_title = !(window_flags.IsSet(Window::FlagNoTitleBar) || background_picture);

	if	(base_widget)
	{
		base_widget->Refresh();

		if	(base_widget->IsDirty())
		{
			Invalidate();

			// Compose window skin.
			Compose();

			// Compose window content.
			Rect <int> compose_rect(GetRect());

			if	(has_title && skin)
			{
				compose_rect.sx = skin->top_left->GetWidth();
				compose_rect.sy = skin->title_bottom + 4;
				compose_rect.ex -= skin->bottom_right->GetWidth();
				compose_rect.ey -= skin->bottom_right->GetHeight();
			}

			if	(cache.IsValid())
				base_widget->Compose(*cache, compose_rect);
		}
	}
	else
		if	(window_flags.IsSet(FlagCacheDirty))
			Compose();

	// Compose the window title.
	if	(has_title && skin)
	{
		iRect title_rect(skin->top_left->GetWidth(), skin->title_top, GetRect().GetWidth() - skin->top_right->GetWidth(), skin->title_bottom);
		if	(cache.IsValid())
			title_widget->Compose(*cache, title_rect);
	}

	if	(window_flags.IsSet(FlagRenderDirty))
		if	(render_data.IsValid() && render_data->texture.IsValid())
		{
			render_data->texture->Blit((const char *)cache->GetData(), cache->GetWidth(), cache->GetHeight());
			window_flags.Remove(FlagRenderDirty);
		}

	Sprite::Render(renderer, batch);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Window::SetSkin(WindowSkin *s)
{
	if	((skin = s))
		if	(title_widget->GetType() == WidgetTypeText)
			if	(TextWidget *t = (TextWidget *)title_widget.c_ptr())
			{
				Color skin_color(skin->title_color);

				t->SetFont(s->title_font);
				t->SetFontColor(skin_color.x, skin_color.y, skin_color.z);
				t->SetFontSize(s->title_size);
				t->SetTextAlignment(TextState::Center);
			}

	Invalidate();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Window::Window()
{
	window_flags.Set(FlagCacheDirty);

	type = Type_Window;
	cache = new Picture;

	background_color.Set(0.f, 0.f, 0.f, 0.f);
	title_widget = new TextWidget(-1, "Window Title");

	SetSize(320, 200);
}
//------------------------------------------------------------------------------
