package engine.net.client.handlers;

import engine.Enum;
import engine.Enum.GameObjectType;
import engine.exception.MsgSendException;
import engine.gameManager.BuildingManager;
import engine.gameManager.DbManager;
import engine.gameManager.SessionManager;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.objects.Building;
import engine.objects.PlayerCharacter;
import org.pmw.tinylog.Logger;

import java.time.LocalDateTime;

import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup;

/*
 *
 * @Summary: Processes application protocol message where a
 * client requests that a building be upgraded.
 */
public class UpgradeAssetMsgHandler extends AbstractClientMsgHandler {

	// Constructor
	public UpgradeAssetMsgHandler() {

		super(UpgradeAssetMessage.class);
	}

	@Override
	protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException {

		// Member variable declaration

		UpgradeAssetMessage msg;
		ManageCityAssetsMsg outMsg;
		PlayerCharacter player;
		int buildingUUID;
		Building buildingToRank;
		LocalDateTime dateToUpgrade;
		int nextRank;
		int rankCost;
		Dispatch dispatch;

		// Assign member variables

		msg = (UpgradeAssetMessage) baseMsg;

		// Grab pointer to the requesting player

		player = SessionManager.getPlayerCharacter(origin);

		// Grab pointer to the building from the cache

		buildingUUID = msg.getBuildingUUID();

		buildingToRank = (Building) DbManager.getObject(GameObjectType.Building, buildingUUID);

		// Early exit if building not in cache.

		if (buildingToRank == null) {
			Logger.error("Attempt to upgrade null building by " + player.getName());
			return true;
		}

		// Early exit for building that is already ranking

		if (buildingToRank.isRanking()) {
			Logger.error("Attempt to upgrade a building already ranking by " + player.getName());
			return true;
		}

		// Calculate and set time/cost to upgrade

		nextRank = (buildingToRank.getRank() + 1);

		if (buildingToRank.getBlueprint() == null)
			return true;
		if (buildingToRank.getBlueprint().getMaxRank() < nextRank || nextRank == 8){
			ErrorPopupMsg.sendErrorMsg(player, "Building is already at it's Max rank.");
			return true;
		}

		rankCost = buildingToRank.getBlueprint().getRankCost(nextRank);

		// SEND NOT ENOUGH GOLD ERROR

		if (!buildingToRank.hasFunds(rankCost)){
			ErrorPopupMsg.sendErrorPopup(player, 127); // Not enough gold in strongbox
			return true;
		}

		if (rankCost > buildingToRank.getStrongboxValue()) {
			sendErrorPopup(player, 127);
			return true;
		}

		// Validation appears good.  Let's now process the upgrade
		
		try {
			if (buildingToRank.getCity() != null){
				buildingToRank.getCity().transactionLock.writeLock().lock();
				try{
					if (!buildingToRank.transferGold(-rankCost,false)) {
						sendErrorPopup(player, 127);
						return true;
					}
				}catch(Exception e){
					Logger.error(e);
				}finally{
					buildingToRank.getCity().transactionLock.writeLock().unlock();
				}
			}else
			if (!buildingToRank.transferGold(-rankCost,false)) {
				sendErrorPopup(player, 127);
				return true;
			}

			dateToUpgrade = LocalDateTime.now().plusHours(buildingToRank.getBlueprint().getRankTime(nextRank));

			BuildingManager.setUpgradeDateTime(buildingToRank, dateToUpgrade, 0);

			// Schedule upgrade job

			BuildingManager.submitUpgradeJob(buildingToRank);

			// Refresh the client's manage asset window
			// *** Refactor : We have some of these unknowns

			outMsg = new ManageCityAssetsMsg(player, buildingToRank);

			// Action TYPE
			outMsg.actionType = 3;
			outMsg.setTargetType(buildingToRank.getObjectType().ordinal());
			outMsg.setTargetID(buildingToRank.getObjectUUID());
			outMsg.setTargetType3(buildingToRank.getObjectType().ordinal());
			outMsg.setTargetID3(buildingToRank.getObjectUUID());
			outMsg.setAssetName1(buildingToRank.getName());
			outMsg.setUnknown54(1);

			dispatch = Dispatch.borrow(player, outMsg);
			DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

		} catch (Exception e) {
			PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
		}

		return true;
	}
}