#include "FurnaceScreen.h"
#include "crafting/PaneCraftingScreen.h"
#include "../Screen.h"
#include "../components/NinePatch.h"
#include "../../Minecraft.h"
#include "../../player/LocalPlayer.h"
#include "../../renderer/Tesselator.h"
#include "../../renderer/entity/ItemRenderer.h"
#include "../../../world/item/Item.h"
#include "../../../world/item/crafting/Recipes.h"
#include "../../../world/item/ItemCategory.h"
#include "../../../world/entity/player/Inventory.h"
#include "../../../world/entity/item/ItemEntity.h"
#include "../../../world/level/Level.h"
#include "../../../world/item/DyePowderItem.h"
#include "../../../world/item/crafting/FurnaceRecipes.h"
#include "../../../world/level/tile/entity/FurnaceTileEntity.h"
#include "../../../locale/I18n.h"
#include "../../../util/StringUtils.h"
#include "../../../world/inventory/FurnaceMenu.h"
#include "../../../network/packet/ContainerSetSlotPacket.h"
#include "../../../network/RakNetInstance.h"

static int heldMs = -1;
static int percent = -1;
static const float MaxHoldMs = 500.0f;
static const int MinChargeMs = 200;

static void setIfNotSet(bool& ref, bool condition) {
	ref = (ref || condition);
}

const int   descFrameWidth = 100;

const int rgbActive = 0xfff0f0f0;
const int rgbInactive = 0xc0635558;
const int rgbInactiveShadow = 0xc0aaaaaa;

#ifdef __APPLE__
	static const float BorderPixels = 4;
	#ifdef DEMO_MODE
		static const float BlockPixels = 22;
	#else
		static const float BlockPixels = 22;
	#endif
#else
	static const float BorderPixels = 4;
	static const float BlockPixels = 24;
#endif
static const int ItemSize = (int)(BlockPixels + 2*BorderPixels);

static const int Bx = 10; // Border Frame width
static const int By = 6; // Border Frame height


FurnaceScreen::FurnaceScreen(Player* player, FurnaceTileEntity* furnace)
:	super(new FurnaceMenu(furnace)), //@huge @attn
	inventoryPane(NULL),
	btnFuel(FurnaceTileEntity::SLOT_FUEL),
	btnIngredient(FurnaceTileEntity::SLOT_INGREDIENT),
	btnResult(FurnaceTileEntity::SLOT_RESULT),
	btnClose(4, ""),
	bHeader (5, "Furnace"),
	guiBackground(NULL),
	guiSlot(NULL),
	guiSlotMarked(NULL),
	guiSlotMarker(NULL),
	guiPaneFrame(NULL),
	player(player),
	furnace(furnace),
	selectedSlot(FurnaceTileEntity::SLOT_INGREDIENT),
	doRecreatePane(false),
	lastBurnTypeId(0),
	descWidth(90)
	//guiSlotItem(NULL),
	//guiSlotItemSelected(NULL)
{
	//LOGI("Creating FurnaceScreen with %p, %d\n", furnace, furnace->runningId);
}

FurnaceScreen::~FurnaceScreen() {
	clearItems();

	delete inventoryPane;

	delete guiBackground;
	delete guiSlot;
	delete guiSlotMarked;
	delete guiSlotMarker;
	delete guiPaneFrame;

	delete menu;

	if (furnace->clientSideOnly)
		delete furnace;
}

void FurnaceScreen::init() {
	super::init();
	//printf("-> %d\n", width/2);

	ImageDef def;
	def.name = "gui/spritesheet.png";
	def.x = 0;
	def.y = 1;
	def.width = def.height = 18;
	def.setSrc(IntRectangle(60, 0, 18, 18));
	btnClose.setImageDef(def, true);
	btnClose.scaleWhenPressed = false;

	buttons.push_back(&bHeader);
	buttons.push_back(&btnIngredient);
	buttons.push_back(&btnFuel);
	buttons.push_back(&btnResult);
	buttons.push_back(&btnClose);

	// GUI - nine patches
	NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png");

	guiBackground   = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4);
	guiSlot         = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3);
	guiSlotMarked   = builder.createSymmetrical(IntRectangle(0, 44, 8, 8), 3, 3);
	guiSlotMarker   = builder.createSymmetrical(IntRectangle(10, 42, 16, 16), 5, 5);
	guiPaneFrame    = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->setExcluded(1 << 4);

	recheckRecipes();
}

void FurnaceScreen::setupPositions() {
	// Left  - Categories
	bHeader.x = bHeader.y = 0;
	bHeader.width = width;// -  bDone.w;

	btnClose.width = btnClose.height = 19;
	btnClose.x = width - btnClose.width;
	btnClose.y = 0;

	// Inventory pane
	const int maxWidth = width/2 - Bx - Bx;
	const int InventoryColumns = maxWidth / ItemSize;
	const int realWidth = InventoryColumns * ItemSize;
	const int paneWidth = realWidth + Bx + Bx;
	const int realBx = (paneWidth - realWidth) / 2;

	inventoryPaneRect = IntRectangle(realBx,
#ifdef __APPLE__
		26 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-28);
#else
		26 + By, realWidth, height-By-By-28);
#endif

	// Right - Slots, description etc
	{
	int cx = (inventoryPaneRect.x + inventoryPaneRect.w);
	int rightWidth = width - cx;

	btnIngredient.width = btnFuel.width = btnResult.width = (int)guiSlot->getWidth();
	btnIngredient.height = btnFuel.height = btnResult.height = (int)guiSlot->getHeight();
	int space = rightWidth - (2 * btnFuel.width + 40);
	int margin = space/2;
	int bx0 = cx + margin;
	int bx1 = width - margin - btnFuel.width;
	btnIngredient.x = btnFuel.x = bx0;
	descWidth = (float)Mth::Min(90, width - bx1 + 24 - 4);

	int by = 36;// + (height-20) / 6;
	btnIngredient.y = by;
	btnFuel.y = by + 20 + btnFuel.height;

	btnResult.x = bx1;
	btnResult.y = (btnIngredient.y + btnFuel.y) / 2;

	guiBackground->setSize((float)width, (float)height);
	guiSlotMarker->setSize((float)btnFuel.width + 4, (float)btnFuel.height + 4);

	recheckRecipes();
	setupInventoryPane();
	}
}

void FurnaceScreen::tick() {
	if (inventoryPane)
		inventoryPane->tick();

	if (doRecreatePane) {
		recheckRecipes();
		setupInventoryPane();
		doRecreatePane = false;
	}
}

void FurnaceScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) {
	if (pane) {
		int ms, id;
		pane->markerIndex = -1;
		if (pane->queryHoldTime(&id, &ms)) {
			heldMs = ms;
			
			const ItemInstance* item = inventoryItems[id];
			int count = (item && !item->isNull())? item->count : 0;
			float maxHoldMs = item? 700 + 10 * item->count: MaxHoldMs;

			if (count > 1 && canMoveToFurnace(id, item)) {
				float share = (heldMs-MinChargeMs) / maxHoldMs;
				pane->markerType = 1;//(heldMs >= MinChargeMs)? 1 : 0;
				pane->markerIndex = id;
				pane->markerShare = Mth::Max(share, 0.0f);

				percent = (int)Mth::clamp(100.0f * share, 0.0f, 100.0f);
				if (percent >= 100) {
					addItem(pane, id);
				}
			}
		}

		pane->render(xm, ym, a);
		guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1));
		//LOGI("query-iv: %d, %d\n", gridId, heldMs);

	}
}

void FurnaceScreen::render(int xm, int ym, float a) {
	const int N = 5;
	static StopwatchNLast r(N);
	//renderBackground();

	updateResult(furnace->getItem(FurnaceTileEntity::SLOT_INGREDIENT));

	Tesselator& t = Tesselator::instance;
	guiBackground->draw(t, 0, 0);
	glEnable2(GL_ALPHA_TEST);

	// Buttons (Left side + crafting)
	super::render(xm, ym, a);

	handleRenderPane(inventoryPane, t, xm, ym, a);

	t.colorABGR(0xffffffff);

	drawSlotItemAt(t, furnace->getItem(btnIngredient.id), btnIngredient.x, btnIngredient.y, btnIngredient.id == selectedSlot);
	drawSlotItemAt(t, furnace->getItem(btnFuel.id), btnFuel.x, btnFuel.y, btnFuel.id == selectedSlot);

	const ItemInstance* resultSlotItem = furnace->getItem(btnResult.id);
	drawSlotItemAt(t, resultSlotItem, btnResult.x, btnResult.y, btnResult.id == selectedSlot);

	if (!burnResult.isNull()) {
		if (!resultSlotItem || resultSlotItem->isNull()) {
			glEnable2(GL_BLEND);
			t.beginOverride();
			t.colorABGR(0x33ffffff);
			t.noColor();
			ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, &burnResult, (float)(btnResult.x + 7), (float)(btnResult.y + 8), true);
			t.endOverrideAndDraw();
			glDisable2(GL_BLEND);
		}
		minecraft->font->drawWordWrap(currentItemDesc, (float)btnResult.x - 24, (float)(btnResult.y + btnResult.height + 6), descWidth, rgbActive);
	}

	minecraft->textures->loadAndBindTexture("gui/spritesheet.png");
	int yy = btnResult.y + 8;
	int fpx = furnace->getLitProgress(14) + 2;
	int xx0 = btnIngredient.x + 8;
	blit(xx0, yy, 80, 40, 16, 16, 32, 32);
	blit(xx0, yy + 16 - fpx, 112, 40 + 32-fpx-fpx, 16, fpx, 32, fpx+fpx);

	int bpx = furnace->getBurnProgress(24);
	int xx1 = btnIngredient.x + 40;
	blit(xx1, yy, 144, 40, 24, 16, 48, 32);
	blit(xx1, yy, 144, 72, bpx, 16, bpx+bpx, 32);
}

void FurnaceScreen::buttonClicked(Button* button) {
	int slot = button->id;

	if (button == &btnClose) {
		minecraft->player->closeContainer();
	}

	if (slot >= FurnaceTileEntity::SLOT_INGREDIENT
	 && slot <= FurnaceTileEntity::SLOT_RESULT) {
		// Can't highlight the Result slot
		int oldSlot = selectedSlot;
		if (slot != FurnaceTileEntity::SLOT_RESULT)
			selectedSlot = slot;

		if (oldSlot == selectedSlot)
			takeAndClearSlot(slot);
	}
}

static bool sortCanCraftPredicate(const CItem* a, const CItem* b) {
	//if (a->maxBuildCount == 0 && b->maxBuildCount > 0) return false;
	//if (b->maxBuildCount == 0 && a->maxBuildCount > 0) return true;
	return a->sortText < b->sortText;
}

void FurnaceScreen::recheckRecipes()
{
	clearItems();

	Stopwatch w;
	w.start();

	const FurnaceRecipes* recipes = FurnaceRecipes::getInstance();
	ItemPack ip;
	// Check for fuel, and items to burn
	if (minecraft->player && minecraft->player->inventory) {
		Inventory* inv = (minecraft->player)->inventory;

		for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) {
			if (ItemInstance* item = inv->getItem(i)) {
				// Fuel material
				if (FurnaceTileEntity::isFuel(*item)) {
					CItem* ci = new CItem(*item, NULL, "");//item->getName());
					//LOGI("Adding fuel: %s\n", item->getName());
					listFuel.push_back(ci);
				}
				// Ingredient/burn material
				if (recipes->isFurnaceItem(item->id)) {
					CItem* ci = new CItem(*item, NULL, "");//item->getName());
					//LOGI("Adding item to burn: %s\n", item->getName());
					listIngredient.push_back(ci);
				}
				//ip.add(ItemPack::getIdForItemInstance(item), item->count);
			}
		}
	}

	ip.print();

	w.stop();
	w.printEvery(1, "> craft ");

	updateItems();
	//std::stable_sort(_categories[c].begin(), _categories[c].end(), sortCanCraftPredicate);
}

bool FurnaceScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) {
	//LOGI("items.size, index: %d, %d\n", inventoryItems.size(), itemIndex);
	const ItemInstance* item = inventoryItems[itemIndex];
	if (!item || item->isNull()) return false;

	setIfNotSet(doRecreatePane, handleAddItem(selectedSlot, item));

	if (doRecreatePane) {
		int slot = inventorySlots[itemIndex];
		if (player->inventory->getItem(slot)) {
			// fix: if we deplete a slot we didn't click on (but had same type),
			// we need to NULLify the slot that actually was depleted
			for (unsigned int i = 0; i < inventorySlots.size(); ++i) {
				slot = inventorySlots[i];
				if (!player->inventory->getItem(slot)) {
					LOGI("Changed! removing slot %d (was: %d)\n", i, itemIndex);
					itemIndex = i;
					break;
				}
			}
		}
		inventoryItems[itemIndex] = NULL;
	}
	//LOGI("Pressed button: %d\n", itemIndexInCurrentCategory);
	return true;
}

bool FurnaceScreen::isAllowed( int slot )
{	//LOGI("items.size, index: %d, %d\n", inventoryItems.size(), slot);
	if (slot >= (int)inventoryItems.size()) return false;
    if (!inventoryItems[slot]) return false;
	const ItemInstance& item = *inventoryItems[slot];

	if (selectedSlot == btnFuel.id)
		return (FurnaceTileEntity::getBurnDuration(item) > 0);
	else
    if (selectedSlot == btnIngredient.id)
		return !FurnaceRecipes::getInstance()->getResult(item.id).isNull();
	return false;
}

bool FurnaceScreen::renderGameBehind()
{
	return false;
}

std::vector<const ItemInstance*> FurnaceScreen::getItems( const Touch::InventoryPane* forPane )
{
	return inventoryItems;
}

void FurnaceScreen::clearItems()
{
	for (unsigned int i = 0; i < listFuel.size(); ++i) delete listFuel[i];
	for (unsigned int i = 0; i < listIngredient.size(); ++i) delete listIngredient[i];
	listFuel.clear();
	listIngredient.clear();
}

void FurnaceScreen::updateItems() {
	inventoryItems.clear();
	inventorySlots.clear();

	ItemList all(listFuel.begin(), listFuel.end());
	all.insert(all.end(), listIngredient.begin(), listIngredient.end());
	for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) {
		ItemInstance* item = minecraft->player->inventory->getItem(i);
		if (!item) continue;
		//LOGI("ItemInstance (%p) Id/aux/count: %d, %d, %d\n", item, item->id, item->getAuxValue(), item->count);
		bool added = false;
		for (unsigned int j = 0; j < listFuel.size(); ++j) {
			if (ItemInstance::matches(item, &listFuel[j]->item)) {
				inventorySlots.push_back(i);
				inventoryItems.push_back(item);
				added = true;
				break;
			}
		}
		if (added) continue;
		for (unsigned int j = 0; j < listIngredient.size(); ++j) {
			if (ItemInstance::matches(item, &listIngredient[j]->item)) {
				inventorySlots.push_back(i);
				inventoryItems.push_back(item);
				added = true;
				break;
			}
		}
	}
}

bool FurnaceScreen::canMoveToFurnace(int inventorySlot, const ItemInstance* item) {
	if (!isAllowed(inventorySlot)) return false;
	ItemInstance* jitem = furnace->getItem(selectedSlot);
	if (!jitem || jitem->isNull()) return true;
	if (ItemInstance::isStackable(item, jitem) && jitem->count < jitem->getMaxStackSize())
		return true;

	return false;
}

void FurnaceScreen::updateResult( const ItemInstance* item )
{
	const ItemInstance* result = furnace->getItem(FurnaceTileEntity::SLOT_RESULT);
	if (!result->isNull()) {
		int id = result->id;
		if (id == lastBurnTypeId) return;
		currentItemDesc = I18n::getDescriptionString(*result);
		lastBurnTypeId = id;
		this->burnResult = *result;
	} else {
		int id = (item? item->id : 0);
		if (id == lastBurnTypeId) return;

		ItemInstance burnResult = FurnaceRecipes::getInstance()->getResult(id);
		if (!burnResult.isNull())
			currentItemDesc = I18n::getDescriptionString(burnResult);
		else
			currentItemDesc = "";
		lastBurnTypeId = id;
		this->burnResult = burnResult;
	}
}

void FurnaceScreen::setupInventoryPane()
{
	// IntRectangle(0, 0, 100, 100)
	if (inventoryPane) delete inventoryPane;
	inventoryPane = new Touch::InventoryPane(this, minecraft, inventoryPaneRect, inventoryPaneRect.w, BorderPixels, inventoryItems.size(), ItemSize, (int)BorderPixels);
	inventoryPane->fillMarginX = 0;
	inventoryPane->fillMarginY = 0;
	guiPaneFrame->setSize((float)inventoryPaneRect.w + 2, (float)inventoryPaneRect.h + 2);
	//LOGI("Creating new pane: %d %p\n", inventoryItems.size(), inventoryPane);
}

void FurnaceScreen::drawSlotItemAt( Tesselator& t, const ItemInstance* item, int x, int y, bool selected)
{
	float xx = (float)x;
	float yy = (float)y;

	(selected? guiSlot/*Marked*/ : guiSlot)->draw(t, xx, yy);

	if (selected)
		guiSlotMarker->draw(t, xx - 2, yy - 2);

	if (item && !item->isNull()) {
		ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 7, yy + 8, true);
		minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true);
	}
}

ItemInstance FurnaceScreen::moveOver(const ItemInstance* item, int maxCount) {
	int wantedCount = item->count * percent / 100;
	if (!wantedCount || heldMs < MinChargeMs)
		wantedCount = 1;

	wantedCount = Mth::Min(wantedCount, maxCount);

	ItemInstance removed(item->id, wantedCount, item->getAuxValue());
	int oldSize = minecraft->player->inventory->getNumEmptySlots();
	if (minecraft->player->inventory->removeResource(removed)) {
		int newSize = minecraft->player->inventory->getNumEmptySlots();
		setIfNotSet(doRecreatePane, newSize != oldSize);
		return removed;
	}
	

	return ItemInstance();
}

void FurnaceScreen::takeAndClearSlot( int slot )
{
	//if (selectedSlot == btnBurn.id && !furnace->isSlotEmpty(btnBurn.id))
	ItemInstance oldItem = *furnace->getItem(slot);
	ItemInstance blank;

	furnace->setItem(slot, &blank);
	if (minecraft->level->isClientSide) {
		ContainerSetSlotPacket p(menu->containerId, slot, blank);
		minecraft->raknetInstance->send(p);
	}

	int oldSize = minecraft->player->inventory->getNumEmptySlots();

	if (!minecraft->player->inventory->add(&oldItem))
		minecraft->player->drop(new ItemInstance(oldItem), false);

	int newSize = minecraft->player->inventory->getNumEmptySlots();
	setIfNotSet(doRecreatePane, newSize != oldSize);
}

bool FurnaceScreen::handleAddItem( int slot, const ItemInstance* item )
{
	ItemInstance* furnaceItem = furnace->getItem(slot);
	int oldSize = minecraft->player->inventory->getNumEmptySlots();

	if (item->id == furnaceItem->id) {
		// If stackable, stack them! Else deny the addition
		const int maxMovedCount = furnaceItem->getMaxStackSize() - furnaceItem->count;
		if (maxMovedCount <= 0)
			return false;

		ItemInstance added = moveOver(item, maxMovedCount);
		furnaceItem->count += added.count;
	} else {
		if (!furnace->isSlotEmpty(slot))
			return false;//takeAndClearSlot(slot);

		ItemInstance moved = moveOver(item, item->getMaxStackSize());
		player->containerMenu->setSlot(slot, &moved);
	}
	 
	if (minecraft->level->isClientSide) {
		ContainerSetSlotPacket p(menu->containerId, slot, *furnaceItem);
		minecraft->raknetInstance->send(p);
	}

	int newSize = minecraft->player->inventory->getNumEmptySlots();
	return (newSize != oldSize);
}
