/*
 * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
 */
package l2.universe.gameserver.datatables;

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

import javolution.util.FastList;
import javolution.util.FastMap;
import l2.universe.L2DatabaseFactory;
import l2.universe.gameserver.ThreadPoolManager;
import l2.universe.gameserver.model.L2ItemMarketModel;
import l2.universe.gameserver.model.actor.instance.L2PcInstance;

public class ItemMarketTable
{
	private static final Logger _log = Logger.getLogger(ItemMarketTable.class.getName());

	private static ItemMarketTable _instance = null;
	private Map<Integer, String> _itemIcons = null;
	private Map<Integer, List<L2ItemMarketModel>> _marketItems = null;
	private Map<Integer, Integer> _sellers = null;

	private ItemMarketTable()
	{
	}

	public static ItemMarketTable getInstance()
	{
		if (_instance == null)
		{
			_instance = new ItemMarketTable();
			_instance.load();
		}
		return _instance;
	}

	private void load()
	{
		_marketItems = new FastMap<Integer, List<L2ItemMarketModel>>();
		Connection con = null;
		int mrktCount = 0;
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("Select * From item_market Order By ownerId");
			ResultSet rset = statement.executeQuery();
			while (rset.next())
			{
				int ownerId = rset.getInt("ownerId");
				String ownerName = rset.getString("ownerName");
				int itemObjId = rset.getInt("itemObjId");
				int itemId = rset.getInt("itemId");
				String itemName = rset.getString("itemName");
				String itemType = rset.getString("itemType");
				String l2Type = rset.getString("l2Type");
				int itemGrade = rset.getInt("itemGrade");
				int enchLvl = rset.getInt("enchLvl");
				int count = rset.getInt("_count");
				int price = rset.getInt("price");
				L2ItemMarketModel mrktItem = new L2ItemMarketModel();
				mrktItem.setOwnerId(ownerId);
				mrktItem.setOwnerName(ownerName);
				mrktItem.setItemObjId(itemObjId);
				mrktItem.setItemId(itemId);
				mrktItem.setItemName(itemName);
				mrktItem.setItemType(itemType);
				mrktItem.setL2Type(l2Type);
				mrktItem.setItemGrade(itemGrade);
				mrktItem.setEnchLvl(enchLvl);
				mrktItem.setCount(count);
				mrktItem.setPrice(price);
				if (_marketItems.containsKey(ownerId))
				{
					List<L2ItemMarketModel> list = _marketItems.get(ownerId);
					list.add(mrktItem);
					_marketItems.put(ownerId, list);
				}
				else
				{
					List<L2ItemMarketModel> list = new FastList<L2ItemMarketModel>();
					list.add(mrktItem);
					_marketItems.put(ownerId, list);
				}
				mrktCount++;
			}
			_log.info("ItemMarketTable: Loaded: " + mrktCount + " market item(s).");
			rset.close();
			statement.close();
		}
		catch (Exception e)
		{
			_log.severe("Error while loading market items " + e.getMessage());
		}
		finally
		{
			L2DatabaseFactory.close(con);
		}
		loadSellers();
		loadIcons();
	}

	private void loadSellers()
	{
		_sellers = new FastMap<Integer, Integer>();
		Connection con = null;
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("Select * From market_seller");
			ResultSet rset = statement.executeQuery();
			while (rset.next())
			{
				int sellerId = rset.getInt("sellerId");
				int money = rset.getInt("money");
				_sellers.put(sellerId, money);
			}
			rset.close();
			statement.close();
		}
		catch (Exception e)
		{
			_log.severe("Error while loading market sellers " + e.getMessage());
		}
		finally
		{
			L2DatabaseFactory.close(con);
		}
	}

	private void loadIcons()
	{
		_itemIcons = new FastMap<Integer, String>();
		Connection con = null;
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("Select * From market_icons");
			ResultSet rset = statement.executeQuery();
			while (rset.next()) 
			{
				int itemId = rset.getInt("itemId");
				String itemIcon = rset.getString("itemIcon").intern();
				_itemIcons.put(itemId, itemIcon);
			}
			rset.close();
			statement.close();
		}
		catch (Exception e)
		{
			_log.severe("Error while loading market icons " + e.getMessage());
		}
		finally
		{
			L2DatabaseFactory.close(con);
		}
	}

	public void addItemToMarket(L2ItemMarketModel itemToMarket, L2PcInstance owner)
	{
		synchronized (this)
		{
			if (_marketItems != null && owner != null && itemToMarket != null)
			{
				List<L2ItemMarketModel> list = _marketItems.get(owner.getObjectId());
				if (list != null)
				{
					list.add(itemToMarket);
					_marketItems.put(owner.getObjectId(), list);
				}
				else
				{
					list = new FastList<L2ItemMarketModel>();
					list.add(itemToMarket);
					_marketItems.put(owner.getObjectId(), list);
				}
				ThreadPoolManager.getInstance().scheduleGeneral(new SaveTask(itemToMarket), 2000);
			}
		}
	}

	public void removeItemFromMarket(int ownerId, int itemObjId, int count)
	{
		L2ItemMarketModel mrktItem = getItem(itemObjId);
		List<L2ItemMarketModel> list = getItemsByOwnerId(ownerId);
		synchronized (this)
		{
			if (list != null && mrktItem != null && !list.isEmpty())
			{
				if (mrktItem.getCount() == count)
				{
					list.remove(mrktItem);
					_marketItems.put(ownerId, list);
					ThreadPoolManager.getInstance().scheduleGeneral(new DeleteTask(mrktItem), 2000);
				}
				else
				{
					list.remove(mrktItem);
					mrktItem.setCount(mrktItem.getCount() - count);
					list.add(mrktItem);
					_marketItems.put(ownerId, list);
					ThreadPoolManager.getInstance().scheduleGeneral(new UpdateTask(mrktItem), 2000);
				}
			}
		}
	}

	public void addMoney(int sellerId, int money)
	{
		synchronized (this)
		{
			if (_sellers != null)
			{
				if (_sellers.containsKey(sellerId))
				{
					int oldMoney = _sellers.get(sellerId);
					money += oldMoney;
					_sellers.put(sellerId, money);
					ThreadPoolManager.getInstance().scheduleGeneral(new AddMoneyTask(sellerId, money), 2000);
				}
				else
				{
					_sellers.put(sellerId, money);
					ThreadPoolManager.getInstance().scheduleGeneral(new AddSellerTask(sellerId, money), 2000);
				}
			}
		}
	}

	public int getMoney(int sellerId)
	{
		synchronized (this)
		{
			if (_sellers != null && !_sellers.isEmpty())
			{
				if (_sellers.containsKey(sellerId))
					return _sellers.get(sellerId);
				else
					return 0;
			}
		}
		return 0;
	}

	public void takeMoney(int sellerId, int amount)
	{
		synchronized (this)
		{
			if (_sellers != null && !_sellers.isEmpty())
			{
				if (_sellers.containsKey(sellerId))
				{
					int oldMoney = _sellers.get(sellerId);
					if (oldMoney >= amount)
					{
						oldMoney -= amount;
						_sellers.put(sellerId, oldMoney);
						ThreadPoolManager.getInstance().scheduleGeneral(new AddMoneyTask(sellerId, oldMoney), 2000);
					}
				}
			}
		}
	}

	public List<L2ItemMarketModel> getItemsByOwnerId(int ownerId)
	{
		synchronized (this)
		{
			if (_marketItems != null && !_marketItems.isEmpty())
				return _marketItems.get(ownerId);
		}
		return null;
	}

	public L2ItemMarketModel getItem(int itemObjId)
	{
		List<L2ItemMarketModel> list = getAllItems();
		synchronized (this)
		{
			for (L2ItemMarketModel model : list) {
				if (model.getItemObjId() == itemObjId)
					return model;
			}
		}
		return null;
	}

	public List<L2ItemMarketModel> getAllItems()
	{
		synchronized (this)
		{
			if (_marketItems != null && !_marketItems.isEmpty())
			{
				List<L2ItemMarketModel> list = new FastList<L2ItemMarketModel>();
				for (List<L2ItemMarketModel> lst : _marketItems.values())
				{
					if (lst != null && !lst.isEmpty())
					{
						for (L2ItemMarketModel auctItem : lst) {
							if (auctItem != null)
								list.add(auctItem);
						}
					}
				}
				return list;
			}
		}
		return null;
	}

	public String getItemIcon(int itemId)
	{
		if (_itemIcons != null && !_itemIcons.isEmpty())
			return _itemIcons.get(itemId);
		return null;
	}

	private static class SaveTask implements Runnable
	{
		private final L2ItemMarketModel _marketItem;

		public SaveTask(L2ItemMarketModel marketItem)
		{
			this._marketItem = marketItem;
		}

		public void run()
		{
			Connection con = null;
			try
			{
				con = L2DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con.prepareStatement("Insert Into item_market Values (?,?,?,?,?,?,?,?,?,?,?)");
				statement.setInt(1, _marketItem.getOwnerId());
				statement.setString(2, _marketItem.getOwnerName());
				statement.setString(3, _marketItem.getItemName());
				statement.setInt(4, _marketItem.getEnchLvl());
				statement.setInt(5, _marketItem.getItemGrade());
				statement.setString(6, _marketItem.getL2Type());
				statement.setString(7, _marketItem.getItemType());
				statement.setInt(8, _marketItem.getItemId());
				statement.setInt(9, _marketItem.getItemObjId());
				statement.setInt(10, _marketItem.getCount());
				statement.setInt(11, _marketItem.getPrice());
				statement.execute();
				statement.close();
			}
			catch (Exception e)
			{
				_log.severe("Error while saving market item into DB " + e.getMessage());
			}
			finally
			{
				L2DatabaseFactory.close(con);
			}
		}
	}

	private static class DeleteTask implements Runnable
	{
		private final L2ItemMarketModel _marketItem;

		public DeleteTask(L2ItemMarketModel marketItem)
		{
			this._marketItem = marketItem;
		}

		public void run()
		{
			Connection con = null;
			try
			{
				con = L2DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con.prepareStatement("Delete From item_market Where ownerId = ? AND itemObjId = ?");
				statement.setInt(1, _marketItem.getOwnerId());
				statement.setInt(2, _marketItem.getItemObjId());
				statement.execute();
				statement.close();
			}
			catch (Exception e)
			{
				_log.severe("Error while deleting market item from DB " + e.getMessage());
			}
			finally
			{
				L2DatabaseFactory.close(con);
			}
		}
	}

	private static class UpdateTask implements Runnable
	{
		private final L2ItemMarketModel _marketItem;

		public UpdateTask(L2ItemMarketModel marketItem)
		{
			this._marketItem = marketItem;
		}

		public void run()
		{
			Connection con = null;
			try
			{
				con = L2DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con.prepareStatement("Update item_market Set _count = ? Where itemObjId = ? AND ownerId = ?");
				statement.setInt(1, _marketItem.getCount());
				statement.setInt(2, _marketItem.getItemObjId());
				statement.setInt(3, _marketItem.getOwnerId());
				statement.executeUpdate();
				statement.close();
			}
			catch (Exception e)
			{
				_log.severe("Error while updating market item in DB " + e.getMessage());
			}
			finally
			{
				L2DatabaseFactory.close(con);
			}
		}
	}

	private static class AddMoneyTask implements Runnable
	{
		private final int sellerId;
		private final int money;

		public AddMoneyTask(int sellerId, int money)
		{
			this.sellerId = sellerId;
			this.money = money;
		}

		public void run()
		{
			Connection con = null;
			try
			{
				con = L2DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con.prepareStatement("Update market_seller Set money = ? Where sellerId = ?");
				statement.setInt(1, money);
				statement.setInt(2, sellerId);
				statement.executeUpdate();
				statement.close();
			}
			catch (Exception e)
			{
				_log.severe("Error while adding money in DB " + e.getMessage());
			}
			finally
			{
				L2DatabaseFactory.close(con);
			}
		}
	}

	private static class AddSellerTask implements Runnable
	{
		private final int sellerId;
		private final int money;

		public AddSellerTask(int sellerId, int money)
		{
			this.sellerId = sellerId;
			this.money = money;
		}

		public void run()
		{
			Connection con = null;
			try
			{
				con = L2DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con.prepareStatement("Insert Into market_seller(sellerId, money) Values (?,?)");
				statement.setInt(1, sellerId);
				statement.setInt(2, money);
				statement.executeUpdate();
				statement.close();
			}
			catch (Exception e)
			{
				_log.severe("Error while adding seller in DB " + e.getMessage());
			}
			finally
			{
				L2DatabaseFactory.close(con);
			}
		}
	}
}