#include "lgi/common/Lgi.h"
#include "lgi/common/EmojiFont.h"
#include "lgi/common/Emoji.h"
#include "lgi/common/GdcTools.h"
#include "FontPriv.h"

#define FILE_NAME "EmojiMap.png"
// 200,960-219,979

struct LEmojiFontPriv
{
	LString Fn;
	LAutoPtr<LSurface> Img, Scaled;
	LArray<bool> Resampled;
	int Cell;

	LEmojiFontPriv()
	{
		Cell = EMOJI_CELL_SIZE;
	}
};

LEmojiFont::LEmojiFont()
{
	priv = new LEmojiFontPriv;
}

LEmojiFont::~LEmojiFont()
{
	delete priv;
}

#define ConvertUtf32(s) \
	uint32_t u32 = 0; \
	if (sizeof(*Str) == 1) \
		u32 = LgiUtf8To32( (uint8_t*&)s, Bytes); \
	else if (sizeof(*Str) == 2) \
		u32 = LgiUtf16To32( (const uint16_t*&)s, Bytes); \
	else if (sizeof(*Str) == 4) \
		u32 = *((uint32_t*&)s)++; \
	else \
		LAssert(!"Invalid OsChar type.");

void LEmojiFont::_Measure(int &x, int &y, OsChar *Str, int len)
{
	ssize_t Bytes = len * sizeof(*Str);

	x = 0;
	y = priv->Cell;
	for (void *s = Str; Bytes > 0; )
	{
		ConvertUtf32(s);
		if (u32)
			x += priv->Cell;
	}
}

int LEmojiFont::_CharAt(int xPos, OsChar *Str, int len, LPxToIndexType Type)
{
	ssize_t Bytes = len * sizeof(*Str);

	int x = 0;
	int Char = 0;
	for (void *s = Str; Bytes > 0; )
	{
		ConvertUtf32(s);
		if (u32)
		{
			if (xPos >= x && xPos < x + priv->Cell)
				return Char;
			x += priv->Cell;
			Char++;
		}
		else break;
	}

	return -1;
}

void LEmojiFont::_Draw(LSurface *pDC, int x, int y, OsChar *Str, int len, LRect *r, LColour &fore)
{
	if (!priv->Img)
		return;

	ssize_t Bytes = len * sizeof(*Str);

	pDC->Op(GDC_ALPHA);
	pDC->Colour(Back());

	for (void *s = Str; Bytes > 0; )
	{
		ConvertUtf32(s);
		if (!u32)
			break;

		LSurface *Src = priv->Scaled ? priv->Scaled : priv->Img;

		auto Emoji = EmojiToIconIndex(&u32, 1);
		if (Emoji.Index >= 0)
		{
			int Cx = Emoji.Index % EMOJI_GROUP_X;
			int Cy = Emoji.Index / EMOJI_GROUP_X;

			LRect Icon(0, 0, priv->Cell-1, priv->Cell-1);
			Icon.Offset(Cx * priv->Cell, Cy * priv->Cell);

			if (!Transparent())
			{
				if (r)
					pDC->Rectangle(r);
				else
					pDC->Rectangle(x, y, x + priv->Cell - 1, y + priv->Cell - 1);
			}

			if (priv->Scaled && !priv->Resampled[Emoji.Index])
			{
				priv->Resampled[Emoji.Index] = true;
				LAutoPtr<LSurface> s(priv->Scaled->SubImage(Icon));
				LRect Src(0, 0, EMOJI_CELL_SIZE-1, EMOJI_CELL_SIZE-1);
				Src.Offset(Cx * EMOJI_CELL_SIZE, Cy * EMOJI_CELL_SIZE);
				ResampleDC(s, priv->Img.Get(), &Src);
			}

			pDC->Blt(x, y, Src, &Icon);

			x += priv->Cell;
		}
		else
		{
			LgiTrace("%s:%i - Invalid char: %i 0x%x\n", _FL, u32, u32);
			break;
		}
	}
}

int LEmojiFont::GetHeight()
{
	return priv->Cell;
}

bool LEmojiFont::Create(const char *Face, LCss::Len Sz, LSurface *pSurface)
{
	if (Sz.IsValid())
		Size(Sz);

	if (!priv->Fn)
	{
		priv->Fn = LFindFile(FILE_NAME);
		if (!priv->Fn)
		{
			LFile::Path p(LSP_APP_INSTALL);
			p += "..\\Lgi\\trunk\\src\\common\\Text\\Emoji\\EmojiMap.png";
			if (p.Exists())
				priv->Fn = p.GetFull();
		}
	}

	if (priv->Fn && !priv->Img)
	{
		if (priv->Img.Reset(GdcD->Load(priv->Fn)))
		{
			// This assert happens when the libpng library is not available and
			// the fallback windows loader is used.
			LAssert(priv->Img->GetBits() == 32);
		}
		else return false;
	}

	if (!d->GlyphMap)
	{
		uint Bytes = (MAX_UNICODE + 1) >> 3;
		d->GlyphMap = new uchar[Bytes];
		if (d->GlyphMap)
		{
			memset(d->GlyphMap, 0, Bytes);
			
			for (int r=0; r<CountOf(EmojiRanges); r++)
			{
				auto &rng = EmojiRanges[r];
				for (uint32_t u=rng.Start; u<rng.End(); u++)
				{
					if (EmojiToIconIndex(&u, 1).Index >= 0)
						d->GlyphMap[u>>3] |= 1 << (u & 7);
				}
			}
		}
	}

	auto Dpi = LScreenDpi();
	Sz = Size();
	if (Sz.IsValid() && priv->Img)
	{
		int NewCell = (int) (Sz.Value * Dpi / 50);
		int Cx = priv->Img->X() / EMOJI_CELL_SIZE;
		int Cy = priv->Img->Y() / EMOJI_CELL_SIZE;
		int Nx = Cx * NewCell;
		int Ny = Cy * NewCell;

		if
		(
			NewCell != EMOJI_CELL_SIZE &&
			!priv->Scaled &&
			priv->Scaled.Reset(new LMemDC(Nx, Ny, priv->Img->GetColourSpace()))
		)
		{
			priv->Scaled->Colour(0, 32);
			priv->Scaled->Rectangle();
			priv->Cell = NewCell;
		}
	}

	return priv->Img != NULL;
}
