#include "lc_global.h"
#include "image.h"
#include "lc_file.h"

static void CopyFromQImage(const QImage& Src, Image& Dest)
{
	const bool Alpha = Src.hasAlphaChannel();
	Dest.Allocate(Src.width(), Src.height(), Alpha ? lcPixelFormat::R8G8B8A8 : lcPixelFormat::R8G8B8);

	quint8* Bytes = (quint8*)Dest.mData;

	for (int y = 0; y < Dest.mHeight; y++)
	{
		for (int x = 0; x < Dest.mWidth; x++)
		{
			QRgb Pixel = Src.pixel(x, y);

			*Bytes++ = qRed(Pixel);
			*Bytes++ = qGreen(Pixel);
			*Bytes++ = qBlue(Pixel);

			if (Alpha)
				*Bytes++ = qAlpha(Pixel);
		}
	}
}

Image::Image()
{
	mData = nullptr;
	mWidth = 0;
	mHeight = 0;
	mFormat = lcPixelFormat::Invalid;
}

Image::Image(Image&& Other)
{
	mData = Other.mData;
	mWidth = Other.mWidth;
	mHeight = Other.mHeight;
	mFormat = Other.mFormat;

	Other.mData = nullptr;
	Other.mWidth = 0;
	Other.mHeight = 0;
	Other.mFormat = lcPixelFormat::Invalid;
}

Image::~Image()
{
	FreeData();
}

int Image::GetBPP() const
{
	switch (mFormat)
	{
	case lcPixelFormat::Invalid:
		return 0;
	case lcPixelFormat::A8:
		return 1;
	case lcPixelFormat::L8A8:
		return 2;
	case lcPixelFormat::R8G8B8:
		return 3;
	case lcPixelFormat::R8G8B8A8:
		return 4;
	}

	return 0;
}

bool Image::HasAlpha() const
{
	switch (mFormat)
	{
	case lcPixelFormat::Invalid:
		return false;
	case lcPixelFormat::A8:
		return true;
	case lcPixelFormat::L8A8:
		return true;
	case lcPixelFormat::R8G8B8:
		return false;
	case lcPixelFormat::R8G8B8A8:
		return true;
	}

	return 0;
}

void Image::FreeData()
{
	free(mData);
	mData = nullptr;
	mWidth = 0;
	mHeight = 0;
	mFormat = lcPixelFormat::Invalid;
}

void Image::Allocate(int Width, int Height, lcPixelFormat Format)
{
	FreeData();

	mWidth = Width;
	mHeight = Height;
	mFormat = Format;
	mData = (unsigned char*)malloc(mWidth * mHeight * GetBPP());
}

void Image::ResizePow2()
{
	int i, shifted_x, shifted_y;

	shifted_x = mWidth;
	for (i = 0; ((i < 16) && (shifted_x != 0)); i++)
		shifted_x = shifted_x >> 1;
	shifted_x = (i != 0) ? 1 << (i-1) : 1;

	shifted_y = mHeight;
	for (i = 0; ((i < 16) && (shifted_y != 0)); i++)
		shifted_y = shifted_y >> 1;
	shifted_y = (i != 0) ? 1 << (i-1) : 1;

	if ((shifted_x != mWidth) || (shifted_y != mHeight))
		Resize (shifted_x, shifted_y);
}

void Image::Resize(int width, int height)
{
	int i, j, k, components, stx, sty;
	float accumx, accumy;
	unsigned char* bits = nullptr;

	components = GetBPP();
	const int BufferSize = width * height * components;

	if (BufferSize)
	{
		bits = (unsigned char*)malloc(BufferSize);

		if (bits)
		{
			for (j = 0; j < mHeight; j++)
			{
				accumy = (float)height*j / (float)mHeight;
				sty = (int)floor(accumy);

				for (i = 0; i < mWidth; i++)
				{
					accumx = (float)width*i / (float)mWidth;
					stx = (int)floor(accumx);

					for (k = 0; k < components; k++)
						bits[(stx + sty*width)*components + k] = mData[(i + j * mWidth) * components + k];
				}
			}
		}
	}

	free(mData);
	mData = bits;
	mWidth = width;
	mHeight = height;
}

bool Image::FileLoad(lcMemFile& File)
{
	QImage Image;

	const unsigned char* Buffer = File.mBuffer + File.mPosition;
	const size_t BufferLength = File.mFileSize - File.mPosition;

	if (!Image.loadFromData(Buffer, (int)BufferLength))
		return false;

	CopyFromQImage(Image, *this);

	return true;
}

bool Image::FileLoad(const QString& FileName)
{
	QImage Image;

	if (!Image.load(FileName))
		return false;

	CopyFromQImage(Image, *this);

	return true;
}