/*
 * Copyright (c) 2013 L2jMobius
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.l2jmobius.gameserver.managers;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * What a bot is doing right now and what recently happened to it - the facts
 * that feed the seam rule lottery (plan=trade/raid/migrate, event=ganked/
 * levelup/raid_finished/opened_store). Updated by one-line hooks at the
 * places that already emit the corresponding chat events. In-memory only:
 * after a restart bots simply have an empty short-term memory, which is fine.
 * @author Mobius SPP
 */
public class FakePlayerBotContext
{
	private static final long EVENT_TTL = 10 * 60 * 1000;
	
	private static class Context
	{
		volatile String plan = "farm";
		volatile long planUntil;
		volatile String event = "";
		volatile long eventUntil;
		volatile String boss = "";
		final Map<String, String> grounding = new ConcurrentHashMap<>();
	}
	
	private static final Map<Integer, Context> CONTEXTS = new ConcurrentHashMap<>();
	
	private FakePlayerBotContext()
	{
	}
	
	private static Context contextOf(int botId)
	{
		return CONTEXTS.computeIfAbsent(botId, k -> new Context());
	}
	
	/**
	 * @param botId bot character id.
	 * @param plan farm / trade / raid / migrate / pvp / idle.
	 * @param durationMs how long the plan holds (afterwards it decays to "farm").
	 */
	public static void setPlan(int botId, String plan, long durationMs)
	{
		if (botId <= 0)
		{
			return;
		}
		final Context context = contextOf(botId);
		context.plan = plan;
		context.planUntil = System.currentTimeMillis() + durationMs;
	}
	
	public static void setEvent(int botId, String event)
	{
		if (botId <= 0)
		{
			return;
		}
		final Context context = contextOf(botId);
		context.event = event;
		context.eventUntil = System.currentTimeMillis() + EVENT_TTL;
	}
	
	/**
	 * @param botId bot character id.
	 * @param boss raid boss name for the {boss} slot while the raid plan holds.
	 */
	public static void setRaid(int botId, String boss, long durationMs)
	{
		if (botId <= 0)
		{
			return;
		}
		setPlan(botId, "raid", durationMs);
		contextOf(botId).boss = boss;
	}
	
	/**
	 * @param botId bot character id.
	 * @return current facts: plan and (when fresh) event.
	 */
	public static Map<String, String> facts(int botId)
	{
		final Map<String, String> facts = new HashMap<>(2);
		final Context context = (botId > 0) ? CONTEXTS.get(botId) : null;
		if (context == null)
		{
			return facts;
		}
		final long now = System.currentTimeMillis();
		facts.put("plan", (now < context.planUntil) ? context.plan : "farm");
		if ((now < context.eventUntil) && !context.event.isEmpty())
		{
			facts.put("event", context.event);
		}
		return facts;
	}
	
	/**
	 * Sets a grounding slot of the bot ({zone}, {level}, {mob}, {town}, {crowd}, {drop}...)
	 * so any line can use it without the caller knowing about it.
	 * @param botId bot character id.
	 * @param key slot name.
	 * @param value slot value (null or empty removes the slot).
	 */
	public static void setSlot(int botId, String key, String value)
	{
		if (botId <= 0)
		{
			return;
		}
		final Context context = contextOf(botId);
		if ((value == null) || value.isEmpty())
		{
			context.grounding.remove(key);
		}
		else
		{
			context.grounding.put(key, value);
		}
	}
	
	/**
	 * @param botId bot character id.
	 * @return the bot's current plan ("farm" when none holds).
	 */
	public static String planOf(int botId)
	{
		final Context context = (botId > 0) ? CONTEXTS.get(botId) : null;
		return ((context != null) && (System.currentTimeMillis() < context.planUntil)) ? context.plan : "farm";
	}
	
	/**
	 * @param botId bot character id.
	 * @return grounding slots that follow from the context ({boss} during a raid, registered slots).
	 */
	public static Map<String, String> slots(int botId)
	{
		final Map<String, String> slots = new HashMap<>(4);
		final Context context = (botId > 0) ? CONTEXTS.get(botId) : null;
		if (context == null)
		{
			return slots;
		}
		slots.putAll(context.grounding);
		if ((System.currentTimeMillis() < context.planUntil) && "raid".equals(context.plan) && !context.boss.isEmpty())
		{
			slots.put("boss", context.boss);
		}
		return slots;
	}
}
