/*
 * Copyright (C) 2004-2013 L2J Server
 * 
 * This file is part of L2J Server.
 * 
 * L2J Server is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * L2J Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.l2jserver.gameserver.instancemanager;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Logger;

import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;

/**
 * @author _DS_, GKR
 */
public class HellboundManager
{
	private static final Logger _log = Logger.getLogger(HellboundManager.class.getName());
	
	private static final String LOAD_SPAWNS = "SELECT npc_templateid, locx, locy, locz, heading, " + "respawn_delay, respawn_random, min_hellbound_level, " + "max_hellbound_level FROM hellbound_spawnlist ORDER BY npc_templateid";
	
	private int _level = 0;
	private int _trust = 0;
	private int _maxTrust = 0;
	private int _minTrust = 0;
	
	private ScheduledFuture<?> _engine = null;
	private final List<HellboundSpawn> _population = new ArrayList<>();
	
	protected HellboundManager()
	{
		loadData();
		loadSpawns();
	}
	
	public final int getLevel()
	{
		return _level;
	}
	
	public final synchronized void updateTrust(int t, boolean useRates)
	{
		if (isLocked())
		{
			return;
		}
		
		int reward = t;
		if (useRates)
		{
			reward = (int) (t > 0 ? Config.RATE_HB_TRUST_INCREASE * t : Config.RATE_HB_TRUST_DECREASE * t);
		}
		
		final int trust = Math.max(_trust + reward, _minTrust);
		if (_maxTrust > 0)
		{
			_trust = Math.min(trust, _maxTrust);
		}
		else
		{
			_trust = trust;
		}
	}
	
	public final void setLevel(int lvl)
	{
		_level = lvl;
	}
	
	public final int getTrust()
	{
		return _trust;
	}
	
	public final int getMaxTrust()
	{
		return _maxTrust;
	}
	
	public final int getMinTrust()
	{
		return _minTrust;
	}
	
	public final void setMaxTrust(int trust)
	{
		_maxTrust = trust;
		if ((_maxTrust > 0) && (_trust > _maxTrust))
		{
			_trust = _maxTrust;
		}
	}
	
	public final void setMinTrust(int trust)
	{
		_minTrust = trust;
		
		if (_trust >= _maxTrust)
		{
			_trust = _minTrust;
		}
	}
	
	/**
	 * @return true if Hellbound is locked
	 */
	public final boolean isLocked()
	{
		return _level == 0;
	}
	
	public final void unlock()
	{
		if (_level == 0)
		{
			setLevel(1);
		}
	}
	
	public final void registerEngine(Runnable r, int interval)
	{
		if (_engine != null)
		{
			_engine.cancel(false);
		}
		_engine = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(r, interval, interval);
	}
	
	public final void doSpawn()
	{
		int added = 0;
		int deleted = 0;
		for (HellboundSpawn spawnDat : _population)
		{
			try
			{
				if (spawnDat == null)
				{
					continue;
				}
				
				L2Npc npc = spawnDat.getLastSpawn();
				if ((_level < spawnDat.getMinLvl()) || (_level > spawnDat.getMaxLvl()))
				{
					// npc should be removed
					spawnDat.stopRespawn();
					
					if ((npc != null) && npc.isVisible())
					{
						npc.deleteMe();
						deleted++;
					}
				}
				else
				{
					// npc should be added
					spawnDat.startRespawn();
					npc = spawnDat.getLastSpawn();
					if (npc == null)
					{
						npc = spawnDat.doSpawn();
						added++;
					}
					else
					{
						if (npc.isDecayed())
						{
							npc.setDecayed(false);
						}
						if (npc.isDead())
						{
							npc.doRevive();
						}
						if (!npc.isVisible())
						{
							added++;
						}
						
						npc.setCurrentHp(npc.getMaxHp());
						npc.setCurrentMp(npc.getMaxMp());
						// npc.spawnMe(spawnDat.getLocx(), spawnDat.getLocy(),
						// spawnDat.getLocz());
					}
				}
			}
			catch (Exception e)
			{
				_log.warning(getClass().getSimpleName() + ": " + e.getMessage());
			}
		}
		
		if (added > 0)
		{
			_log.info(getClass().getSimpleName() + ": Spawned " + added + " NPCs.");
		}
		if (deleted > 0)
		{
			_log.info(getClass().getSimpleName() + ": Removed " + deleted + " NPCs.");
		}
	}
	
	public final void cleanUp()
	{
		saveData();
		
		if (_engine != null)
		{
			_engine.cancel(true);
			_engine = null;
		}
		_population.clear();
	}
	
	private final void loadData()
	{
		if (GlobalVariablesManager.getInstance().hasVariable("HBLevel"))
		{
			_level = GlobalVariablesManager.getInstance().getInt("HBLevel");
			_trust = GlobalVariablesManager.getInstance().getInt("HBTrust");
		}
		else
		{
			saveData();
		}
	}
	
	public final void saveData()
	{
		GlobalVariablesManager.getInstance().set("HBLevel", _level);
		GlobalVariablesManager.getInstance().set("HBTrust", _trust);
	}
	
	private final void loadSpawns()
	{
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			Statement s = con.createStatement();
			ResultSet rs = s.executeQuery(LOAD_SPAWNS))
		{
			HellboundSpawn spawnDat;
			L2NpcTemplate template;
			while (rs.next())
			{
				template = NpcTable.getInstance().getTemplate(rs.getInt("npc_templateid"));
				if (template != null)
				{
					spawnDat = new HellboundSpawn(template);
					spawnDat.setAmount(1);
					spawnDat.setX(rs.getInt("locx"));
					spawnDat.setY(rs.getInt("locy"));
					spawnDat.setZ(rs.getInt("locz"));
					spawnDat.setHeading(rs.getInt("heading"));
					spawnDat.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random"));
					spawnDat.setMinLvl(rs.getInt("min_hellbound_level"));
					spawnDat.setMaxLvl(rs.getInt("max_hellbound_level"));
					
					// _population.put(spawnDat, null);
					_population.add(spawnDat);
					SpawnTable.getInstance().addNewSpawn(spawnDat, false);
				}
				else
				{
					_log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + rs.getInt("npc_templateid") + ".");
				}
			}
			rs.close();
			s.close();
		}
		catch (Exception e)
		{
			_log.warning(getClass().getSimpleName() + ": problem while loading spawns: " + e);
		}
		_log.info(getClass().getSimpleName() + ": Loaded " + _population.size() + " npc spawn locations.");
	}
	
	public static final class HellboundSpawn extends L2Spawn
	{
		private int _minLvl;
		private int _maxLvl;
		
		public HellboundSpawn(L2NpcTemplate mobTemplate) throws SecurityException, ClassNotFoundException, NoSuchMethodException
		{
			super(mobTemplate);
		}
		
		public final int getMinLvl()
		{
			return _minLvl;
		}
		
		public final void setMinLvl(int lvl)
		{
			_minLvl = lvl;
		}
		
		public final int getMaxLvl()
		{
			return _maxLvl;
		}
		
		public final void setMaxLvl(int lvl)
		{
			_maxLvl = lvl;
		}
	}
	
	public static final HellboundManager getInstance()
	{
		return SingletonHolder._instance;
	}
	
	private static class SingletonHolder
	{
		protected static final HellboundManager _instance = new HellboundManager();
	}
}
