/*
 * 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.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.w3c.dom.Document;

import org.l2jmobius.commons.util.IXmlReader;
import org.l2jmobius.gameserver.util.StatSet;

/**
 * Combat profiles for bots: which skills a class uses, in what order and under
 * which conditions. Data driven (data/FakePlayerCombat.xml) so the behaviour can
 * be tuned without recompiling.
 * @author Mobius SPP
 */
public class FakePlayerCombatData implements IXmlReader
{
	private static final Logger LOGGER = Logger.getLogger(FakePlayerCombatData.class.getName());
	
	/**
	 * One skill entry with its usage conditions.
	 */
	public static class BotSkill
	{
		public int id;
		public int priority;
		public int maxTargetHp = 100;
		public int minMp = 0;
		public int minHp = 100;
	}
	
	/**
	 * Combat profile of a single class.
	 */
	public static class Profile
	{
		public String role = "fighter";
		public final List<BotSkill> offensive = new ArrayList<>();
		public final List<BotSkill> self = new ArrayList<>();
		public final List<BotSkill> heal = new ArrayList<>();
	}
	
	private final Map<Integer, Profile> _profiles = new HashMap<>();
	private Profile _default = new Profile();
	
	protected FakePlayerCombatData()
	{
		load();
	}
	
	@Override
	public void load()
	{
		_profiles.clear();
		parseDatapackFile("data/FakePlayerCombat.xml");
		LOGGER.info(getClass().getSimpleName() + ": Loaded combat profiles for " + _profiles.size() + " classes.");
	}
	
	@Override
	public void parseDocument(Document document, File file)
	{
		forEach(document, "combat", listNode ->
		{
			forEach(listNode, "class", classNode ->
			{
				final StatSet set = new StatSet(parseAttributes(classNode));
				final Profile profile = new Profile();
				profile.role = set.getString("role", "fighter");
				fill(classNode, profile);
				_profiles.put(set.getInt("id"), profile);
			});
			
			forEach(listNode, "default", defaultNode ->
			{
				final StatSet set = new StatSet(parseAttributes(defaultNode));
				final Profile profile = new Profile();
				profile.role = set.getString("role", "fighter");
				fill(defaultNode, profile);
				_default = profile;
			});
		});
	}
	
	private void fill(org.w3c.dom.Node classNode, Profile profile)
	{
		forEach(classNode, "offensive", node -> profile.offensive.add(skill(node, false)));
		forEach(classNode, "self", node -> profile.self.add(skill(node, false)));
		forEach(classNode, "heal", node -> profile.heal.add(skill(node, true)));
		profile.offensive.sort(Comparator.comparingInt(s -> s.priority));
		profile.heal.sort(Comparator.comparingInt(s -> s.priority));
	}
	
	private BotSkill skill(org.w3c.dom.Node node, boolean healing)
	{
		final StatSet set = new StatSet(parseAttributes(node));
		final BotSkill skill = new BotSkill();
		skill.id = set.getInt("id");
		skill.priority = set.getInt("priority", 10);
		skill.maxTargetHp = set.getInt("maxTargetHp", 100);
		skill.minMp = set.getInt("minMp", 0);
		skill.minHp = set.getInt("minHp", healing ? 70 : 100);
		return skill;
	}
	
	/**
	 * @param classId player class id.
	 * @return the profile for this class, never null.
	 */
	public Profile getProfile(int classId)
	{
		return _profiles.getOrDefault(classId, _default);
	}
	
	public static FakePlayerCombatData getInstance()
	{
		return SingletonHolder.INSTANCE;
	}
	
	private static class SingletonHolder
	{
		protected static final FakePlayerCombatData INSTANCE = new FakePlayerCombatData();
	}
}
