#include "cachedtexture.hpp"

#include <stdutil.hpp>
#include <gxcanvas.hpp>
#include <gxgraphics.hpp>

int active_texs;

extern gxRuntime*  gx_runtime;
extern gxGraphics* gx_graphics;

std::set<CachedTexture::CTInstance*> CachedTexture::rep_set;

static std::string path;

struct CachedTexture::CTInstance {
	int               ref_cnt;
	std::string       file;
	int               flags, w, h, first;
	std::vector<gxCanvas*> frames;

	CTInstance(int w, int h, int flags, int cnt) : ref_cnt(1), flags(flags), w(w), h(h), first(0)
	{
		++active_texs;
		while (cnt-- > 0) {
			if (gxCanvas* t = gx_graphics->createCanvas(w, h, flags)) {
				frames.push_back(t);
			} else
				break;
		}
	}

	CTInstance(const std::string& f, int flags, int w, int h, int first, int cnt)
		: ref_cnt(1), file(f), flags(flags), w(w), h(h), first(first)
	{
		++active_texs;
		if (!(flags & gxCanvas::CANVAS_TEX_CUBE)) {
			if (w <= 0 || h <= 0 || first < 0 || cnt <= 0) {
				w = h = first = 0;
				if (gxCanvas* t = gx_graphics->loadCanvas(f, flags)) {
					frames.push_back(t);
				}
				return;
			}
		}

		int t_flags = flags
						  & (gxCanvas::CANVAS_TEX_RGB | gxCanvas::CANVAS_TEX_ALPHA | gxCanvas::CANVAS_TEX_MASK
							 | gxCanvas::CANVAS_TEX_HICOLOR)
					  | gxCanvas::CANVAS_NONDISPLAY;

		gxCanvas* t = gx_graphics->loadCanvas(f, t_flags);
		if (!t)
			return;
		if (!t->getDepth()) {
			gx_graphics->freeCanvas(t);
			return;
		}

		if (flags & gxCanvas::CANVAS_TEX_CUBE) {
			int w = t->getWidth() / 6;
			if (w * 6 != t->getWidth())
				return;
			int h = t->getHeight();

			gxCanvas* tex = gx_graphics->createCanvas(w, h, flags);
			if (tex) {
				frames.push_back(tex);

				for (int face = 0; face < 6; ++face) {
					tex->setCubeFace(face);
					gx_graphics->copy(tex, 0, 0, tex->getWidth(), tex->getHeight(), t, face * w, 0, w, h);
				}
				tex->setCubeFace(1);
			}
		} else {
			int x_tiles = t->getWidth() / w;
			int y_tiles = t->getHeight() / h;
			if (first + cnt > x_tiles * y_tiles) {
				gx_graphics->freeCanvas(t);
				return;
			}
			int x = (first % x_tiles) * w;
			int y = (first / x_tiles) * h;
			while (cnt--) {
				gxCanvas* p = gx_graphics->createCanvas(w, h, flags);
				gx_graphics->copy(p, 0, 0, p->getWidth(), p->getHeight(), t, x, y, w, h);
				frames.push_back(p);
				x = x + w;
				if (x + w > t->getWidth()) {
					x = 0;
					y = y + h;
				}
			}
		}
		gx_graphics->freeCanvas(t);
	}

	~CTInstance()
	{
		--active_texs;
		for (int k = 0; k < frames.size(); ++k)
			gx_graphics->freeCanvas(frames[k]);
	}
};

CachedTexture::CTInstance* CachedTexture::findRep(const std::string& f, int flags, int w, int h, int first, int cnt)
{
	std::set<CTInstance*>::const_iterator it;
	for (it = rep_set.begin(); it != rep_set.end(); ++it) {
		CTInstance* rep = *it;
		if (rep->file == f && rep->flags == flags && rep->w == w && rep->h == h && rep->first == first
			&& rep->frames.size() == cnt) {
			++rep->ref_cnt;
			return rep;
		}
	}
	return 0;
}

CachedTexture::CachedTexture(int w, int h, int flags, int cnt) : rep(new CTInstance(w, h, flags, cnt)) {}

CachedTexture::CachedTexture(const std::string& f_, int flags, int w, int h, int first, int cnt)
{
	std::string f = f_;
	if (f.substr(0, 2) == ".\\")
		f = f.substr(2);
	if (path.size()) {
		std::string t = path + tolower(filenamefile(f));
		if (rep = findRep(t, flags, w, h, first, cnt))
			return;
		rep = new CTInstance(t, flags, w, h, first, cnt);
		if (rep->frames.size()) {
			rep_set.insert(rep);
			return;
		}
		delete rep;
	}
	std::string t = tolower(fullfilename(f));
	if (rep = findRep(t, flags, w, h, first, cnt))
		return;
	rep = new CTInstance(t, flags, w, h, first, cnt);
	rep_set.insert(rep);
}

CachedTexture::CachedTexture(const CachedTexture& t) : rep(t.rep)
{
	++rep->ref_cnt;
}

CachedTexture::~CachedTexture()
{
	if (!--rep->ref_cnt) {
		rep_set.erase(rep);
		delete rep;
	}
}

CachedTexture& CachedTexture::operator=(const CachedTexture& t)
{
	++t.rep->ref_cnt;
	if (!--rep->ref_cnt) {
		rep_set.erase(rep);
		delete rep;
	}
	rep = t.rep;
	return *this;
}

std::string CachedTexture::getName() const
{
	return rep->file;
}

const std::vector<gxCanvas*>& CachedTexture::getFrames() const
{
	return rep->frames;
}

void CachedTexture::setPath(const std::string& t)
{
	path = tolower(t);
	if (int sz = path.size()) {
		if (path[sz - 1] != '/' && path[sz - 1] != '\\')
			path += '\\';
	}
}