/*
 * This program 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 2, or (at your option)
 * any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package com.l2jfrozen.gameserver.datatables.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.logging.Logger;

import javolution.util.FastList;
import javolution.util.FastMap;

import com.l2jfrozen.gameserver.model.L2TradeList;
import com.l2jfrozen.gameserver.model.actor.instance.L2ItemInstance;
import com.l2jfrozen.gameserver.thread.ThreadPoolManager;
import com.l2jfrozen.util.CloseUtil;
import com.l2jfrozen.util.database.L2DatabaseFactory;

/**
 * This class manages buylists from database
 * 
 * @version $Revision: 1.5.4.13 $ $Date: 2005/04/06 16:13:38 $
 */
public class TradeListTable
{
	private static final Logger _log = Logger.getLogger(TradeListTable.class.getName());
	private static TradeListTable _instance;

	private int _nextListId;
	private final FastMap<Integer, L2TradeList> _lists;

	/** Task launching the function for restore count of Item (Clan Hall) */
	private class RestoreCount implements Runnable
	{
		private final int timer;

		public RestoreCount(int time)
		{
			timer = time;
		}

		@Override
		public void run()
		{
			restoreCount(timer);
			dataTimerSave(timer);
			ThreadPoolManager.getInstance().scheduleGeneral(new RestoreCount(timer), (long) timer * 60 * 60 * 1000);
		}
	}

	public static TradeListTable getInstance()
	{
		if(_instance == null)
		{
			_instance = new TradeListTable();
		}

		return _instance;
	}

	private TradeListTable()
	{
		_lists = new FastMap<Integer, L2TradeList>();
		load();
	}

	private void load(boolean custom)
	{
		Connection con = null;
		/*
		 * Initialize Shop buylist
		 */
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection(false);
			final PreparedStatement statement1 = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[]
			{
					"shop_id", "npc_id"
			}) + " FROM " + (custom ? "custom_merchant_shopids" : "merchant_shopids"));
			final ResultSet rset1 = statement1.executeQuery();

			while(rset1.next())
			{
				final PreparedStatement statement = con.prepareStatement("SELECT item_id, price, shop_id, order, count, time, currentCount FROM " + 
						(custom ? "custom_merchant_buylists" : "merchant_buylists") + " WHERE shop_id=? ORDER BY order ASC");
				statement.setString(1, String.valueOf(rset1.getInt("shop_id")));
				final ResultSet rset = statement.executeQuery();

				L2TradeList buylist = new L2TradeList(rset1.getInt("shop_id"));

				buylist.setNpcId(rset1.getString("npc_id"));
				int _itemId = 0;
				int _itemCount = 0;
				int _price = 0;

				if(!buylist.isGm() && NpcTable.getInstance().getTemplate(rset1.getInt("npc_id")) == null)
				{
					_log.warning("TradeListTable: Merchant id {} with {} buylist {} not exist. "+ rset1.getString("npc_id")+" "+ buylist.getListId());
				}

				try
				{
					while(rset.next())
					{
						_itemId = rset.getInt("item_id");
						_price = rset.getInt("price");
						int count = rset.getInt("count");
						int currentCount = rset.getInt("currentCount");
						int time = rset.getInt("time");

						L2ItemInstance buyItem = ItemTable.getInstance().createDummyItem(_itemId);

						if(buyItem == null)
						{
							continue;
						}

						_itemCount++;

						if(count > -1)
						{
							buyItem.setCountDecrease(true);
						}
						buyItem.setPriceToSell(_price);
						buyItem.setTime(time);
						buyItem.setInitCount(count);

						if(currentCount > -1)
						{
							buyItem.setCount(currentCount);
						}
						else
						{
							buyItem.setCount(count);
						}

						buylist.addItem(buyItem);

						if(!buylist.isGm() && buyItem.getReferencePrice() > _price)
						{
							_log.warning("TradeListTable: Reference price of item {} in buylist {} higher then sell price. "+ _itemId+" "+ buylist.getListId());
						}
					}
				}
				catch(Exception e)
				{
					_log.severe("TradeListTable: Problem with buylist {}. "+ buylist.getListId()+" "+ e);
				}

				if(_itemCount > 0)
				{
					_lists.put(buylist.getListId(), buylist);
					_nextListId = Math.max(_nextListId, buylist.getListId() + 1);
				}
				else
				{
					_log.warning("TradeListTable: Empty buylist {}."+ buylist.getListId());
				}

				statement.close();
				rset.close();
			}
			rset1.close();
			statement1.close();

			_log.finest("TradeListTable: Loaded {} Buylists. "+ _lists.size());
			/*
			 *  Restore Task for reinitialize count of buy item
			 */
			try
			{
				int time = 0;
				long savetimer = 0;
				long currentMillis = System.currentTimeMillis();
				final PreparedStatement statement2 = con.prepareStatement("SELECT DISTINCT time, savetimer FROM " + (custom ? "merchant_buylists" : "merchant_buylists") + " WHERE time <> 0 ORDER BY time");
				final ResultSet rset2 = statement2.executeQuery();

				while(rset2.next())
				{
					time = rset2.getInt("time");
					savetimer = rset2.getLong("savetimer");
					if(savetimer - currentMillis > 0)
					{
						ThreadPoolManager.getInstance().scheduleGeneral(new RestoreCount(time), savetimer - System.currentTimeMillis());
					}
					else
					{
						ThreadPoolManager.getInstance().scheduleGeneral(new RestoreCount(time), 0);
					}
				}

				rset2.close();
				statement2.close();
			}
			catch(Exception e)
			{
				_log.severe("TradeController: Could not restore Timer for Item count. "+ e);
			}
		}
		catch(Exception e)
		{
			// problem with initializing buylists, go to next one
			_log.severe("TradeListTable: Buylists could not be initialized. "+  e);
		}
		finally
		{
			CloseUtil.close(con);
		}
	}

	public void load()
	{
		load(false); // not custom
		load(true); //custom		
	}

	public void reloadAll()
	{
		_lists.clear();

		load();
	}

	public L2TradeList getBuyList(int listId)
	{
		if(_lists.containsKey(listId))
			return _lists.get(listId);

		return null;
	}

	public FastList<L2TradeList> getBuyListByNpcId(int npcId)
	{
		FastList<L2TradeList> lists = new FastList<L2TradeList>();

		for(L2TradeList list : _lists.values())
		{
			if(list.isGm())
			{
				continue;
			}
			/** if (npcId == list.getNpcId()) **/
			lists.add(list);
		}

		return lists;
	}

	protected void restoreCount(int time)
	{
		if(_lists == null)
			return;

		for(L2TradeList list : _lists.values())
		{
			list.restoreCount(time);
		}
	}

	protected void dataTimerSave(int time)
	{
		Connection con = null;
		long timerSave = System.currentTimeMillis() + (long) time * 3600000; //60*60*1000

		try
		{
			con = L2DatabaseFactory.getInstance().getConnection(false);
			PreparedStatement statement = con.prepareStatement("UPDATE merchant_buylists SET savetimer =? WHERE time =?");
			statement.setLong(1, timerSave);
			statement.setInt(2, time);
			statement.executeUpdate();
			statement.close();
		}
		catch(Exception e)
		{
			_log.severe("TradeController: Could not update Timer save in Buylist "+ e);
		}
		finally
		{
			CloseUtil.close(con);
		}
	}

	public void dataCountStore()
	{
		if(_lists == null)
			return;

		int listId;
		Connection con = null;
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection(false);
			PreparedStatement statement;
			
			for(L2TradeList list : _lists.values())
			{
				if(list == null)
				{
					continue;
				}

				listId = list.getListId();

				for(L2ItemInstance Item : list.getItems())
				{
					if(Item.getCount() < Item.getInitCount()) //needed?
					{
						statement = con.prepareStatement("UPDATE merchant_buylists SET currentCount=? WHERE item_id=? AND shop_id=?");
						statement.setInt(1, Item.getCount());
						statement.setInt(2, Item.getItemId());
						statement.setInt(3, listId);
						statement.executeUpdate();
						statement.close();
					}
				}
			}
		}
		catch(Exception e)
		{
			_log.severe("TradeController: Could not store Count Item "+ e);
		}
		finally
		{
			CloseUtil.close(con);
		}
	}
}
