#include "Minecraft.hpp"
#include "client/Options.hpp"
#include "client/gamemode/GameMode.hpp"
#include "client/gui/screens/ChatScreen.hpp"
#include "client/gui/screens/ConsoleScreen.hpp"
#include "client/gui/screens/DeathScreen.hpp"
#include "client/gui/screens/UsernameScreen.hpp"
#include "client/player/LocalPlayer.hpp"
#include "client/player/input/IBuildInput.hpp"
#include "client/player/input/IInputHolder.hpp"
#include "client/player/input/KeyboardInput.hpp"
#include "client/player/input/MouseBuildInput.hpp"
#include "client/player/input/MouseTurnInput.hpp"
#include "client/player/input/touchscreen/TouchscreenInput.hpp"
#include "client/player/input/touchscreen/TouchInputHolder.hpp"
#include "client/renderer/Chunk.hpp"
#include "client/renderer/EntityTileRenderer.hpp"
#include "client/renderer/ItemInHandRenderer.hpp"
#include "client/renderer/Tesselator.hpp"
#include "client/renderer/entity/EntityRenderDispatcher.hpp"
#include "client/renderer/entity/ItemRenderer.hpp"
#include "client/renderer/ptexture/DynamicTexture.hpp"
#include "network/RakNetInstance.hpp"
#include "network/ServerSideNetworkHandler.hpp"
#include "network/packet/InteractPacket.hpp"
#include "network/packet/RespawnPacket.hpp"
#include "platform/CThread.hpp"
#include "platform/input/Multitouch.hpp"
#include "util/PerfRenderer.hpp"
#include "util/PerfTimer.hpp"
#include "world/level/Level.hpp"
#include "world/level/chunk/ChunkSource.hpp"
#include <MinecraftClient.hpp>

static void checkGlError(const char* tag) {
#ifdef GLDEBUG
	while (1) {
        const int errCode = glGetError();
        if (errCode == GL_NO_ERROR) break;

		LOGE("################\nOpenGL-error @ %s : #%d\n", tag, errCode);
	}
#endif /*GLDEBUG*/
}

MinecraftClient::~MinecraftClient() {
    delete levelRenderer;
	delete gameRenderer;
	delete particleEngine;

	delete soundEngine;

	delete inputHolder;
	delete _perfRenderer;

    delete font;
	delete textures;

	if (getScreen != NULL) {
		delete getScreen;
		getScreen = NULL;
	}

    EntityRenderDispatcher::destroy();

#ifdef PLATFORM_DESKTOP
	ItemRenderer::teardown_static();
	if (EntityTileRenderer::instance != NULL) {
		delete EntityTileRenderer::instance;
		EntityTileRenderer::instance = NULL;
	}
	TileEntityRenderDispatcher::destroy();
#endif
}

void MinecraftClient::init() {
	Minecraft::init();

	setSize(m_platform->getScreenWidth(), m_platform->getScreenHeight());

	initGLStates();
	Tesselator::instance.init();
	I18n::loadLanguage(m_platform, "en_US");

	checkGlError("Init enter");

	_supportsNonTouchscreen = !m_platform->supportsTouchscreen();

	LOGI("IS TOUCHSCREEN? %d\n", options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN));

	textures = new Textures(&options, m_platform);
	textures->addDynamicTexture(new WaterTexture());
	textures->addDynamicTexture(new WaterSideTexture());
	gui.texturesLoaded(textures);

	levelRenderer = new LevelRenderer(this);
	gameRenderer = new GameRenderer(this);
	particleEngine = new ParticleEngine(level, textures);

	// Platform specific initialization here
	font = new Font(&options, "font/default8.png", textures);

	_perfRenderer = new PerfRenderer(this, font);

	checkGlError("Init complete");

	screenChooser.setScreen(SCREEN_STARTMENU);

	if (options.getBooleanValue(OPTIONS_FIRST_LAUNCH)) {
		options.toggle(OPTIONS_FIRST_LAUNCH);
		setScreen(new UsernameScreen());
	}

	reloadOptions();
}

void MinecraftClient::leaveGame(bool renameLevel /*=false*/) {
    if (isGeneratingLevel || !_hasSignaledGeneratingLevelFinished)
        return;
    
	isGeneratingLevel = false;
	bool saveLevel = level && (!level->isClientSide || renameLevel);

	raknetInstance->disconnect();
	if (saveLevel) {
		// If server or wanting to save level as client, save all unsaved chunks!
		level->getChunkSource()->saveAll(true);
	}

	LOGI("Clearing levels\n");

	cameraTargetPlayer = NULL;
	LOGI("Erasing callback\n");
	delete netCallback;
	netCallback = NULL;

	LOGI("Erasing level\n");
	if (level != NULL) {
		delete level->getLevelStorage();
		delete level;
		level = NULL;
	}
	//delete player;
	player = NULL;
	cameraTargetPlayer = NULL;

	_running = false;
}

void MinecraftClient::update() {
    m_frames++;

    // Generate Multitouch active pointer list
	Multitouch::commit();

    if (Options::debugGl)
		LOGI(">>>>>>>>>>\n");

	TIMER_PUSH("root");

    // If we're paused (local world / invisible server), freeze gameplay and
	// networking and only keep UI responsive.
	bool freezeGame = pause;

    if (!freezeGame) {
        Minecraft::update();
    }

    if (gameMode != NULL) {
		if (gamemode.destroyProgress <= 0) {
			m_gui.progress = 0;
			levelRenderer->destroyProgress = 0;
		} else {
			float dp = oDestroyProgress + (destroyProgress - oDestroyProgress) * a;
			m_gui.progress = dp;
			levelRenderer->destroyProgress = dp;
		}
	}

    TIMER_PUSH("sound");
    soundEngine->update(player, timer.a);
    TIMER_POP_PUSH("render");
    gameRenderer->render(timer.a);
    TIMER_POP();

    Multitouch::resetThisUpdate();

    checkGlError("Update finished");

	if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) {
#ifndef PLATFORM_DESKTOP
		if (!PerfTimer::enabled) {
			PerfTimer::reset();
			PerfTimer::enabled = true;
		}
		_perfRenderer->renderFpsMeter(1);
		checkGlError("render debug");
#endif
	} else {
		PerfTimer::enabled = false;
	}

    TIMER_POP();

    swapBuffers();
	Mouse::reset2();

#ifndef WIN32
	updateStats();
#endif
}

void MinecraftClient::updateStats() {
	if (Options::debugGl)
		LOGI("--------------------------------------------\n");

	//*
	int now = getTimeMs();
	//int since = now - _lastTickMs;

	if (now >= lastTime + 1000)
	{
		if (player) {
			LOGI("%d fps   \t%3d chunk updates.   (%.2f, %.2f, %.2f)\n",
				m_frames, Chunk::updates, player->x, player->y, player->z);
			Chunk::resetUpdates();

			//static int _n = 0;
			//if (++_n % 5 == -1) { // @note: -1
			//	static char filename[256];
			//	sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", externalStoragePath.c_str(), _n/5);
			//	_context.platform->saveScreenshot(filename, width, height);
			//}

			LOGI("%s", levelRenderer->gatherStats1().c_str());
			//printf("Texture swaps (this frame): %d\n", Textures::textureChanges);
		} else {
			LOGI("%d fps\n", m_frames);
		}

		//const int* pointerIds;
		//int pointerCount = Multitouch::getActivePointerIds(&pointerIds);
		//if (pointerCount) {
		//	std::string s = "Pointers (";
		//	s += (char)(48 + pointerCount);
		//	s += ": ";
		//	for (int i = 0; i < pointerCount; ++i) {
		//		s += (char)(48 + pointerIds[i]);
		//		s += ", ";
		//	}
		//	LOGI("%s\n", s.c_str());
		//}

		lastTime = now;
		m_frames = 0;
#ifdef GLDEBUG
		while (1) {
			int error = glGetError();
			if (error == GL_NO_ERROR) break;

			LOGI("#################### GL-ERROR: %d\t#####################\n", error);
			LOGI("#################### GL-ERROR: %d\t#####################\n", error);
			LOGI("#################### GL-ERROR: %d\t#####################\n", error);
		}
#endif
	}

	Textures::textureChanges = 0;
}

void MinecraftClient::restartServer() {
	Minecraft::restartServer();

	gui.addMessage("This server has restarted!");

	// @fix maybe code below should run also on server

	for (int i = level->players.size()-1; i >= 0; --i) {
        Player* p = level->players[i];
        if (p != player)
            level->removeEntity(p);
    }
}


bool MinecraftClient::handleBack(bool isDown) {
	if (isGeneratingLevel)
	{
		return true;
	}
	if (level)
	{
		if (!isDown)
		{
			if (getScreen)
			{
				if (!getScreen->handleBackEvent(isDown))
				{
					if (player->containerMenu) player->closeContainer();
					setScreen(NULL);
				}
				return true;
			} else {
				pauseGame(true);
			}
			//leaveGame();
			return false;
		}
		return true;
	}
	else if (getScreen)
	{
		return getScreen->handleBackEvent(isDown);
	}
	return false;
}

void MinecraftClient::onGraphicsReset() {
	initGLStates();
	Tesselator::instance.init();

	m_textures.clear();
	
	font->onGraphicsReset();
	gui.onGraphicsReset();

	if (levelRenderer) levelRenderer->onGraphicsReset();
	if (gameRenderer) gameRenderer->onGraphicsReset();

	EntityRenderDispatcher::getInstance()->onGraphicsReset();
	TileEntityRenderDispatcher::getInstance()->onGraphicsReset();
}

void MinecraftClient::tick(int nTick, int maxTick) {
	if (!pause) Minecraft::tick(nTick, maxTick);

	if (!getScreen && player) {
		if (player->health <= 0) {
			setScreen(new DeathScreen());
		}
	}

	TIMER_PUSH("input");
	tickInput();

	TIMER_POP_PUSH("gui");
	gui.tick();

	//
	// Ongoing level generation in a (perhaps) different thread. When it's
	// ready, _levelGenerated() is called once and any threads are deleted.
	//
	if (isGeneratingLevel) {
		return;
	} else if (!_hasSignaledGeneratingLevelFinished) {
		if (generateLevelThread) {
			delete generateLevelThread;
			generateLevelThread = NULL;
		}
		_levelGenerated();
	}

	//
	// Normal game loop, run before or efter level generation
	//
	if (level != NULL && !pause) {
		TIMER_POP_PUSH("gameRenderer");
		gameRenderer->tick(nTick, maxTick);

		TIMER_POP_PUSH("levelRenderer");
		levelRenderer->tick();

		TIMER_POP_PUSH("animateTick");
		if (player) {
			level->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
		}
	}

	m_textures.loadAndBindTexture("terrain.png");
	
	if (!pause && !(getScreen && !getScreen->renderGameBehind())) {
		#if !defined(RPI)
			#ifdef __APPLE__
			if (isSuperFast())
			#endif
			{
				if (nTick == maxTick) {
					TIMER_POP_PUSH("textures");
					m_textures.tick(true);
				}
			}
		#endif
	}
	TIMER_POP_PUSH("particles");

	if (!pause) {
		particleEngine->tick();
	}
	
	if (getScreen) {
		screenMutex = true;
		getScreen->tick();
		screenMutex = false;
	}

	// @note: fix to keep "isPressed" and "isReleased" as long as necessary.
	//      Most likely keyboard and mouse could/should be reset here as well.
	Multitouch::reset();
	TIMER_POP();
}


class InputRAII {
public:
	~InputRAII() {
		Mouse::reset();
		Keyboard::reset();
	}
};

void MinecraftClient::tickInput() {
	InputRAII raiiInput;

	// input handling code here...
	if (getScreen && !getScreen->passEvents) {
		screenMutex = true;
		getScreen->updateEvents();
		//screen->updateSetScreen();
		screenMutex = false;
		if (hasScheduledScreen) {
			setScreen(scheduledScreen);
			scheduledScreen = NULL;
			hasScheduledScreen = false;
		}
		return;
	}

	if (!player) {
		return;
	}

#ifdef RPI
	bool mouseDiggable = true;
	bool allowGuiClicks = !mouseGrabbed;
#else
	bool mouseDiggable = !gui.isInside(Mouse::getX(), Mouse::getY());
	bool allowGuiClicks = true;
#endif

	TIMER_PUSH("mouse");
	while (Mouse::next()) {
		//if (Mouse::getButtonState(MouseAction::ACTION_LEFT))
		//	LOGI("mouse-down-at: %d, %d\n", Mouse::getX(), Mouse::getY());
        	int passedTime = getTimeMs() - lastTickTime;
        	if (passedTime > 200) continue; // @note: As long Mouse::clear CLEARS the whole buffer, it's safe to break here
		// But since it might be rewritten anyway (and hopefully there aren't a lot of messages, we just continue.

		const MouseAction& e = Mouse::getEvent();

		if (!useTouchscreen() && !mouseGrabbed) {
			if (!getScreen && e.data == MouseAction::DATA_DOWN) {
				grabMouse();
			}
		}

		if (allowGuiClicks && e.action == MouseAction::ACTION_LEFT && e.data == MouseAction::DATA_DOWN) {
			gui.handleClick(MouseAction::ACTION_LEFT, Mouse::getX(), Mouse::getY());
		}

		if (e.action == MouseAction::ACTION_WHEEL) {
			// If chat/console is open, use the wheel to scroll through chat history.
			if (getScreen && (dynamic_cast<ChatScreen*>(getScreen) || dynamic_cast<ConsoleScreen*>(getScreen))) {
				gui.scrollChat(e.dy);
			} else {
				Inventory* v = player->inventory;

				int numSlots = gui.getNumSlots();
				if (!useTouchscreen()) numSlots--;

				int slot = (v->selected - e.dy + numSlots) % numSlots;
				v->selectSlot(slot);
			}
		}
		/*
		if (mouseDiggable && options.useMouseForDigging) {
			if (Mouse::getEventButton() == MouseAction::ACTION_LEFT && Mouse::getEventButtonState()) {
				handleMouseClick(MouseAction::ACTION_LEFT);
				lastClickTick = ticks;
			}
			if (Mouse::getEventButton() == MouseAction::ACTION_RIGHT && Mouse::getEventButtonState()) {
				handleMouseClick(MouseAction::ACTION_RIGHT);
				lastClickTick = ticks;
			}
		}
		*/
	}

	TIMER_POP_PUSH("keyboard");
	while (Keyboard::next()) {
		int key = Keyboard::getEventKey();
		bool isPressed = (Keyboard::getEventKeyState() == KeyboardAction::KEYDOWN);
		player->setKey(key, isPressed);

		if (isPressed) {
			gui.handleKeyPressed(key);

			if (key >= '0' && key <= '9') {
				int digit = key - '0';
				int slot = digit - 1;

				if (slot >= 0 && slot < gui.getNumSlots())
					player->inventory->selectSlot(slot);

				#if defined(WIN32)
					if (digit >= 1 && GetAsyncKeyState(VK_CONTROL) < 0) {
						// Set adventure settings here!
						AdventureSettingsPacket p(level->adventureSettings);
						p.toggle((AdventureSettingsPacket::Flags)(1 << slot));
						p.fillIn(level->adventureSettings);
						raknetInstance->send(p);
					}
					if (digit == 0) {
						Pos pos((int)player->x, (int)player->y-1, (int)player->z);
						SetSpawnPositionPacket p(pos);
						raknetInstance->send(p);
					}
				#endif
			}

			if (key == Keyboard::KEY_LEFT_CTRL) {
				player->setSprinting(true);
			}

			if (key == Keyboard::KEY_E) {
				screenChooser.setScreen(SCREEN_BLOCKSELECTION);
			}

			if (!getScreen && key == Keyboard::KEY_T && level) {
				setScreen(new ConsoleScreen());
			}

			if (key == Keyboard::KEY_F3) {
				options.toggle(OPTIONS_RENDER_DEBUG);
			}

			if (key == Keyboard::KEY_F5) {
				options.toggle(OPTIONS_THIRD_PERSON_VIEW);
				/*
				ImprovedNoise noise;
				for (int i = 0; i < 16; ++i)
					printf("%d\t%f\n", i, noise.grad2(i, 3, 8));
				*/
			}

			if (!getScreen && key == Keyboard::KEY_O || key == 250) {
				releaseMouse();
			}

			if (key == Keyboard::KEY_F) {
				int dst = options.getIntValue(OPTIONS_VIEW_DISTANCE);
				options.set(OPTIONS_VIEW_DISTANCE, (dst + 1) % 4);
			}
			#ifdef CHEATS
				if (key == Keyboard::KEY_U) {
					onGraphicsReset();
					player->heal(100);
				}

				if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode
					setIsCreativeMode(!isCreativeMode());

				if (key == Keyboard::KEY_P) // Step forward in time
					level->setTime( level->getTime() + 1000);

				if (key == Keyboard::KEY_G) {
					setScreen(new ArmorScreen());
					/*
					std::vector<AABB>& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f));
					LOGI("boxes: %d\n", (int)boxs.size());
					*/
				}

				if (key == Keyboard::KEY_Y) {
					textures->reloadAll();
					player->hurtTo(2);
				}
				if (key == Keyboard::KEY_Z || key == 108) {
					for (int i = 0; i < 1; ++i) {
						Mob* mob = NULL;
						int forceId = 0;//MobTypes::Sheep;

						int types[] = {
							MobTypes::Sheep,
							MobTypes::Pig,
							MobTypes::Chicken,
							MobTypes::Cow,
						};

						int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))];
						mob = MobFactory::CreateMob(mobType, level);

						//((Animal*)mob)->setAge(-1000);
						float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot);
						float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot);
						if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true))
							delete mob;
					}
				}

				if (key == Keyboard::KEY_X) {
					const EntityList& entities = level->getAllEntities();
					for (int i = entities.size()-1; i >= 0; --i) {
						Entity* e = entities[i];
						if (!e->isPlayer())
							level->removeEntity(e);
					}
				}

				if (key == Keyboard::KEY_C /*|| key == 4*/) {
					player->inventory->clearInventoryWithDefault();
					// @todo: Add saving here for benchmarking
				}
				if (key == Keyboard::KEY_H) {
					setScreen( new PrerenderTilesScreen() );
				}

				if (key == Keyboard::KEY_O) {
					for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i)
						if (player->inventory->getItem(i))
							player->inventory->dropSlot(i, false);
				}
				if (key == Keyboard::KEY_M) {
					Difficulty difficulty = (Difficulty)options.getIntValue(OPTIONS_DIFFICULTY);
					options.set(OPTIONS_DIFFICULTY, (difficulty == Difficulty::PEACEFUL)?
						Difficulty::NORMAL : Difficulty::PEACEFUL);
					//setIsCreativeMode( !isCreativeMode() );
				}

				if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) {
					if (key >= '0' && key <= '9') {
						_perfRenderer->debugFpsMeterKeyPress(key - '0');
					}
				}
			#endif

			if (key == Keyboard::KEY_ESCAPE)
				pauseGame(false);

			#ifndef OPENGL_ES
				if (key == Keyboard::KEY_P) {
					static bool isWireFrame = false;
					isWireFrame = !isWireFrame;
					glPolygonMode(GL_FRONT, isWireFrame? GL_LINE : GL_FILL);
					//glPolygonMode(GL_BACK, isWireFrame? GL_LINE : GL_FILL);
				}
			#endif
		}
		#ifdef WIN32
			if (key == Keyboard::KEY_M) {
				for (int i = 0; i < 5 * SharedConstants::TicksPerSecond; ++i)
					level->tick();
			}
		#endif
	}

	TIMER_POP_PUSH("handlemouse");
	
	static bool prevMouseDownLeft = false;

	if (Mouse::getButtonState(MouseAction::ACTION_LEFT) == 0) {
		gameMode->stopDestroyBlock();
	}

	if (!Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
		gameMode->releaseUsingItem(player);
	}

	if (useTouchscreen()) {
		// Touch: gesture recognizer classifies the action type (turn/destroy/build)
		BuildActionIntention bai;

		if (inputHolder && inputHolder->getBuildInput()->tickBuild(player, &bai)) {
			handleBuildAction(&bai);
		} else {
			gameMode->stopDestroyBlock();
		}
	} else {
		// Desktop: left mouse = destroy/attack
		if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) {
			auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK;

			if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE;

			BuildActionIntention bai(baiFlags);
			handleBuildAction(&bai);
		}

		prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT);

		// Build and use/interact is on same button
		// USPESHNO spizheno
		static int buildHoldTicks = 0;
		if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
			if (buildHoldTicks >= 5) buildHoldTicks = 0;

			if (++buildHoldTicks == 1) {
				BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT);
				handleBuildAction(&bai);
			}
		} else {
			buildHoldTicks = 0;
			gameMode->releaseUsingItem(player);
		}
	}
	// after input handling

	lastTickTime = getTimeMs();

	// we have (hopefully) handled the keyboard & mouse queue and it
	// can now be emptied. If wanted, the reset could be changed to:
	// index -= numRead; // then this code doesn't have to be placed here
	// + it prepares for tick not handling all or any events.
	// update: RAII'ing instead, see above
	//Keyboard::reset();
	//Mouse::reset();

	TIMER_POP();
}

void MinecraftClient::handleBuildAction(BuildActionIntention* action) {
	if (action->isRemove()) {
		if (missTime > 0) return;
		player->swing();
	}
	if(player->isUsingItem())
		return;
    bool mayUse = true;

	if (!hitResult.isHit()) {
		if (action->isRemove() && !gameMode->isCreativeType()) {
			missTime = 10;
		}
    } else if (hitResult.type == ENTITY) {
        if (action->isAttack()) {
			player->swing();
			//LOGI("attacking!\n");
			InteractPacket packet(InteractPacket::Attack, player->entityId, hitResult.entity->entityId);
			raknetInstance->send(packet);
            gameMode->attack(player, hitResult.entity);
		} else if (action->isInteract()) {
			if (hitResult.entity->interactPreventDefault())
				mayUse = false;
			//LOGI("interacting!\n");
			InteractPacket packet(InteractPacket::Interact, player->entityId, hitResult.entity->entityId);
			raknetInstance->send(packet);
            gameMode->interact(player, hitResult.entity);
        }
    } else if (hitResult.type == TILE) {
		int x = hitResult.x;
        int y = hitResult.y;
        int z = hitResult.z;
        int face = hitResult.f;

		int oldTileId = level->getTile(x, y, z);
        Tile* oldTile = Tile::tiles[oldTileId];

		//bool tryDestroyBlock = false;

		if (action->isRemove()) {
			if (!oldTile)
				return;

			//LOGI("tile: %s - %d, %d, %d. b: %f - %f\n", oldTile->getDescriptionId().c_str(), x, y, z, oldTile->getBrightness(level, x, y, z), oldTile->getBrightness(level, x, y+1, z));
            level->extinguishFire(x, y, z, hitResult.f);
			
			if (action->isFirstRemove()) {
				gameMode->startDestroyBlock(x, y, z, hitResult.f);
			} else {
				gameMode->continueDestroyBlock(x, y, z, hitResult.f);
			}

			particleEngine->crack(x, y, z, hitResult.f);
			player->swing();
        }
		else {
			ItemInstance* item = player->inventory->getSelected();
 			if (gameMode->useItemOn(player, level, item, x, y, z, face, hitResult.pos)) {
			    mayUse = false;
			    player->swing();
			#ifdef RPI
				} else if (item && item->id == ((Item*)Item::sword_iron)->id) {
					player->swing();
			#endif
			}
			if (item && item->count <= 0) {
				player->inventory->clearSlot(player->inventory->selected);
			}
			//} else if (item && item->count != oldCount) {
			//	  gameRenderer->itemInHandRenderer->itemPlaced();
			//}
		}
	}

	if (mayUse && action->isInteract()) {
		ItemInstance* item = player->inventory->getSelected();
		if (item && !player->isUsingItem()) {
			if (gameMode->useItem(player, level, item)) {
				gameRenderer->itemInHandRenderer->itemUsed();
			}

			if (item && item->count <= 0) {
				player->inventory->clearSlot(player->inventory->selected);
			}
		}
	}
}

bool MinecraftClient::isOnlineClient() {
	return (level && level->isClientSide);
}

void MinecraftClient::pauseGame(bool isBackPaused) {
	// Only freeze gameplay when running a local server and it is not accepting
	// incoming connections (invisible server), which includes typical single-
	// player/lobby mode. If the server is visible, the game should keep ticking.
	bool canFreeze = false;
	if (raknetInstance && raknetInstance->isServer() && netCallback) {
		ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback;
		if (!ss->allowsIncomingConnections())
			canFreeze = true;
	}
	pause = canFreeze;

	if (getScreen != NULL) return;
	screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE);
}

void MinecraftClient::gameLostFocus() {
	if(getScreen != NULL) {
		getScreen->lostFocus();
	}
}

void MinecraftClient::setScreen( Screen* screen ) {
	Mouse::reset();
	Multitouch::reset();
	Multitouch::resetThisUpdate();

	if (screenMutex) {
		hasScheduledScreen = true;
		scheduledScreen = screen;
		return;
	}

	if (screen != NULL && screen->isErrorScreen())
		return;
	if (screen == NULL && level == NULL)
		screen = screenChooser.createScreen(SCREEN_STARTMENU);

	if (this->getScreen != NULL) {
		this->getScreen->removed();
		delete this->getScreen;
	}

	this->getScreen = screen;
	if (screen != NULL) {
		releaseMouse();
		//ScreenSizeCalculator ssc = new ScreenSizeCalculator(options, width, height);
		int screenWidth = (int)(width * Gui::InvGuiScale); //ssc.getWidth();
		int screenHeight = (int)(height * Gui::InvGuiScale); //ssc.getHeight();
		screen->init(this, screenWidth, screenHeight);

		if (screen->isInGameScreen() && level) {
			level->saveLevelData();
            level->saveGame();
        }

		//noRender = false;
	} else {
		// Closing a screen and returning to the game should unpause.
		pause = false;
		grabMouse();
	}
}

void MinecraftClient::grabMouse() {
	if (mouseGrabbed) return;
	mouseGrabbed = true;
	mouseHandler.grab();
	//setScreen(NULL);
}

void MinecraftClient::releaseMouse() {
	if (!mouseGrabbed) {
		return;
	}
	if (player) {
		player->releaseAllKeys();
	}
	mouseGrabbed = false;
	mouseHandler.release();
}

bool MinecraftClient::useTouchscreen() {
#ifdef RPI
	return false;
#endif
	return options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN) && !_supportsNonTouchscreen;
}

bool MinecraftClient::supportNonTouchScreen() {
	return _supportsNonTouchscreen;
}

void MinecraftClient::setSize(int w, int h) {
    transformResolution(&w, &h);

	width  = w;
	height = h;

	int guiScale = options.getIntValue(OPTIONS_GUI_SCALE);

	// determine gui scale, optionally overriding auto
	if (guiScale != 0) {
		// manual selection: 1->small, 2->medium, 3->large, 4->larger, 5->largest
		switch (guiScale) {
		case 1: Gui::GuiScale = 2.0f; break;
		case 2: Gui::GuiScale = 3.0f; break;
		case 3: Gui::GuiScale = 4.0f; break;
		case 4: Gui::GuiScale = 5.0f; break;
		case 5: Gui::GuiScale = 6.0f; break;
		default: Gui::GuiScale = 1.0f; break; // auto
		}
	} else {
		// auto compute from resolution
		if (width >= 1000) {
        #ifdef __APPLE__
            Gui::GuiScale = (width > 2000)? 8.0f : 4.0f;
        #else
            Gui::GuiScale = 4.0f;
        #endif
    }
		else if (width >= 800) {
#ifdef __APPLE__
        Gui::GuiScale = 4.0f;
#else
		Gui::GuiScale = 3.0f;
#endif
    }
		else if (width >= 400)
			Gui::GuiScale = 2.0f;
		else
			Gui::GuiScale = 1.0f;
	}

	Gui::InvGuiScale = 1.0f / Gui::GuiScale;
	int screenWidth  = (int)(width  * Gui::InvGuiScale);
	int screenHeight = (int)(height * Gui::InvGuiScale);

	// if (platform()) {
	// 	float pixelsPerMillimeter = options.getProgressValue(&Options::Option::PIXELS_PER_MILLIMETER);
	// 	pixelCalc.setPixelsPerMillimeter(pixelsPerMillimeter);
	// 	pixelCalcUi.setPixelsPerMillimeter(pixelsPerMillimeter * Gui::InvGuiScale);
	// }

	Config config = createConfig(this);
	gui.onConfigChanged(config);
    
	if (getScreen)
		getScreen->setSize(screenWidth, screenHeight);

	if (inputHolder)
		inputHolder->onConfigChanged(config);
	//LOGI("Setting size: %d, %d: %f\n", width, height, Gui::InvGuiScale);
}

void MinecraftClient::reloadOptions() {
	options.save();
	bool wasTouchscreen = options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN);
	options.set(OPTIONS_USE_TOUCHSCREEN, useTouchscreen());
	options.save();

	if ((wasTouchscreen != useTouchscreen()) || (inputHolder == 0))
		_reloadInput();

	// user->name = options.username;

	LOGI("Reloading-options\n");

    // @todo @fix Android and iOS behaves a bit differently when leaving
    //            an options screen (Android recreates OpenGL surface)
    setSize(width, height);
}

void MinecraftClient::_reloadInput() {
	delete inputHolder;

	const bool useTouchHolder = useTouchscreen();
	if (useTouchHolder) {
		inputHolder = new TouchInputHolder(this, &options);
	} else {
		#if defined(ANDROID) || defined(__APPLE__) 
			inputHolder = new CustomInputHolder(
				new XperiaPlayInput(&options),
				new ControllerTurnInput(2, ControllerTurnInput::MODE_DELTA),
				new IBuildInput());
		#else
			inputHolder = new CustomInputHolder(
				new KeyboardInput(&options),
				new MouseTurnInput(MouseTurnInput::MODE_DELTA, width/2, height/2),
				new MouseBuildInput());
		#endif
	}

	mouseHandler.setTurnInput(inputHolder->getTurnInput());
	if (level && player) {
		player->input = inputHolder->getMoveInput();
	}
}

void MinecraftClient::initGLStates() {
	//glShadeModel2(GL_SMOOTH);
	//glClearDepthf(1.0f);
	glEnable2(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glDepthRangef(0, 1);
	glEnable2(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.1f);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glEnable2(GL_TEXTURE_2D);
#ifndef _EMSCRIPTEN_
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
#endif
	// Both updates isPowerVR flag in java and returns if the graphics chip is PowerVR SGX or not
	_powerVr = platform()->isPowerVR();
#ifdef __APPLE__
    _isSuperFast = platform()->isSuperFast();
#endif
}

void MinecraftClient::_levelGenerated() {
	Minecraft::_levelGenerated();

	level->loadPlayer(player, true);
	// if we are client side, we trust the server to have given us a correct position
	if (player && !level->isClientSide) {
		player->resetPos(false);
	}

	this->cameraTargetPlayer = player;

	if (player == NULL) {
		player = new LocalPlayer(minecraft, level, minecraft.options.getStringValue(OPTIONS_USERNAME), level->dimension->id, isCreativeType());
		gameMode->initPlayer(player);
	}

	if (player) {
		player->input = inputHolder->getMoveInput();
	}

	if (levelRenderer != NULL) levelRenderer->setLevel(level);
	if (particleEngine != NULL) particleEngine->setLevel(level);

	gameMode->adjustPlayer(player);
	gui.onLevelGenerated();

#if defined(WIN32) || defined(RPI)
	if (_commandServer) {
		delete _commandServer;
	}
	_commandServer = new CommandServer(this);
	_commandServer->init(commandPort);
#endif
}

void MinecraftClient::respawnPlayer() {
	// RESET THE FRACKING PLAYER HERE
	//bool slowCheck = false;
	//for (int i = 0; i < level->entities.size(); ++i)
	//	if (level->entities[i] == player) slowCheck = true;
	//
	//LOGI("Has entity? %d, %d\n", level->getEntity(player->entityId), slowCheck);

	resetPlayer(player);

	// tell server (or other client) that we re-spawned
	RespawnPacket packet(player);
	raknetInstance->send(packet);
}

void MinecraftClient::audioEngineOn() {
    soundEngine->enable(true);
}

void MinecraftClient::audioEngineOff() {
    soundEngine->enable(false);
}

void MinecraftClient::setIsCreativeMode(bool isCreative) {
	Minecraft::setIsCreativeMode(isCreative);

	if (player)
		gameMode->initAbilities(player->abilities);
}

void MinecraftClient::optionUpdated(OptionId option, bool value ) {
	Minecraft::optionUpdated(option, value);

	if (option == OPTIONS_USE_TOUCHSCREEN) {
		_reloadInput();
	} else if (option == OPTIONS_VSYNC) {
		m_platform->setVSync(value);
	}
}

void MinecraftClient::optionUpdated(OptionId option, float value ) {
	Minecraft::optionUpdated(option, value);
// #ifndef STANDALONE_SERVER
// 	if(option == OPTIONS_PIXELS_PER_MILLIMETER) {
// 		pixelCalcUi.setPixelsPerMillimeter(value * Gui::InvGuiScale);
// 		pixelCalc.setPixelsPerMillimeter(value);
// 	}
// #endif
}

void MinecraftClient::optionUpdated(OptionId option, int value ) {
	Minecraft::optionUpdated(option, value);

    if(option == OPTIONS_GUI_SCALE) {
        // reapply screen scaling using current window size
        setSize(width, height);
    }

	if (option == OPTIONS_DIFFICULTY && level) {
		level->difficulty = options.getIntValue(OPTIONS_DIFFICULTY);
	}
}

// @attn shittttt
void MinecraftClient::setLevel(Level* level, const std::string& message /* ="" */, Player* forceInsertPlayer /* = NULL */) {
	Minecraft::setLevel(level, message, forceInsertPlayer);
	cameraTargetPlayer = NULL;
	
	if (level != NULL) {
		if (!player && forceInsertPlayer)
		{
			player = forceInsertPlayer;
			player->resetPos(false);
			//level->addEntity(forceInsertPlayer);
		}
		else if (player != NULL) {
			player->resetPos(false);
			if (level != NULL) {
				level->addEntity(player);
			}
		}
    } else {
        player = NULL;
    }
}

void MinecraftClient::hostMultiplayer(int port) {
	Minecraft::hostMultiplayer(port);
	raknetInstance->host(options.getStringValue(OPTIONS_USERNAME), port);
}

std::string MinecraftClient::getServerName() {
	return options.getStringValue(OPTIONS_USERNAME);
}

void MinecraftClient::locateMultiplayer() {
	isLookingForMultiplayer = true;

	raknetInstance->pingForHosts(19132);
	netCallback = new ClientSideNetworkHandler(this, raknetInstance);
}

void MinecraftClient::cancelLocateMultiplayer() {
	isLookingForMultiplayer = false;

	raknetInstance->stopPingForHosts();

	delete netCallback;
	netCallback = NULL;
}

bool MinecraftClient::joinMultiplayer( const PingedCompatibleServer& server ) {
	if (isLookingForMultiplayer && netCallback) {
		isLookingForMultiplayer = false;
		return raknetInstance->connect(server.address.ToString(false), server.address.GetPort());
	}
	return false;
}

bool MinecraftClient::joinMultiplayerFromString( const std::string& server ) {	
	std::string ip = "";
	std::string port = "19132";
	
	size_t pos = server.find(":");

	if (pos != std::string::npos) {
		ip = server.substr(0, pos);
		port = server.substr(pos + 1);
	} else {
		ip = server;
	}

	printf("%s \n", port.c_str());
	
	if (isLookingForMultiplayer && netCallback) {
		isLookingForMultiplayer = false;
		printf("test");
		int portNum = atoi(port.c_str());
		return raknetInstance->connect(ip.c_str(), portNum);
	}
	return false;
}

bool MinecraftClient::isKindleFire(int kindleVersion) {
	// if (kindleVersion != 1)
	// 	return false;

	// std::string model = platform()->getPlatformStringVar(PlatformStringVars::DEVICE_BUILD_MODEL);
	// std::string modelLower(model);
	// std::transform(modelLower.begin(), modelLower.end(), modelLower.begin(), tolower);

	// return (modelLower.find("kindle") != std::string::npos) && (modelLower.find("fire") != std::string::npos);

	return false;
}

bool MinecraftClient::transformResolution(int* w, int* h)
{
	bool changed = false;

	// Kindle Fire 1: reporting wrong height in
	// certain cases (e.g. after screen lock)
	if (isKindleFire(1) && *h >= 560 && *h <= 620) {
		*h = 580;
		changed = true;
	}

	return changed;
}

void MinecraftClient::onBlockDestroyed(Player* player, int x, int y, int z, int face) {
	particleEngine->destroy(x, y, z);
	soundEngine->play(oldTile->soundType->getBreakSound(), x + 0.5f, y + 0.5f, z + 0.5f, (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f);

	if (player == this->player) {
		if (options.getBooleanValue(OPTIONS_DESTROY_VIBRATION)) minecraft.platform()->vibrate(24);

		RemoveBlockPacket packet(player, x, y, z);
		minecraft.raknetInstance->send(packet);
	}
}