context: // Eternal Lands Bot
  // Copyright (C) 2006  Artem Makhutov
  // artem@makhutov.org
  // 
  // 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
  // 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, write to the Free Software
  // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
  using System;
  using System.Collections;
  
  namespace cs_elbot
  {
  	/// <summary>
  	/// description of TradeHandler.
  	/// </summary>
  	public class TradeHandler
  	{
          	private AdvancedCommunication.HelpCommandHandler TheHelpCommandHandler;
          	private errorHandler TheErrorHandler;
  		private TCPWrapper TheTCPWrapper;
  		private BasicCommunication.MessageParser TheMessageParser;
  		private Logger TheLogger;
  		private MySqlManager TheMySqlManager;
  		private Inventory TheInventory;
          	private AdvancedCommunication.ActorHandler TheActorHandler;
          	private Storage TheStorage;
  
  		private int TradeTimeElapsed = 0;
  		public System.Timers.Timer TradeTimer = new System.Timers.Timer();
  		
  		public int AcceptStateMe = 0;
  		public int AcceptStatePartner = 0;
  		
  		private int MyMoneySlotID = 0;
  		//private byte MyMoneyOnTradeSlotID = 0;
  		private uint MyMoneyAmount = 0;
  		private uint MyMoneyOnTrade = 0;
  		
  		private System.Collections.ArrayList InventorySnapshop = new System.Collections.ArrayList();
  		private System.Collections.ArrayList InventorySnapshopLoging = new System.Collections.ArrayList();
  
          	private System.Collections.ArrayList StorageSnapshot = new System.Collections.ArrayList();
          
  		public System.Collections.Hashtable PartnerTradeItemsList = new System.Collections.Hashtable();
  	        public System.Collections.ArrayList MyItemList = new System.Collections.ArrayList();
  
  
          	public bool storageAvailable = false;
          	public double Billance = 0;
  		
  		public static string username = "";
  		public bool Trading = false;
          	public bool SentThanks = false;
  		public bool Donating = false;
          	public bool Gambling = false;
          	public bool claimingPrize = false;
          	public bool Giving = false;
  		public bool PutOnSale = false;
  
          	//variables for new total command
          	public bool totalCalculated = false;
          	public double totalOwed = 0;
          	public bool invalidItem = false;
          	public int idleTradeTime = 0;
          	public bool idleWarningSent = false;
  
  
          	public struct TradeLogItem
  		{
  			public double price;
  			public uint quantity;
  			public int KnownItemsSqlID;
  			public string action;
              		public int categoryNum;
  		}
  		
  		public struct SellingItem
  		{
  			public int PricelistSqlID;
  			public int SellingItemsSqlID;
  			public int KnownItemsSqlID;
  			public bool announce;
  			public double pricesale;
  			public double pricesalemembers;
  		}
  		
  		public struct WantedItem
  		{
  			public int PricelistSqlID;
  			public int SellingItemsSqlID;
  			public int KnownItemsSqlID;
  			public bool announce;
              		public double pricepurchase;
              		public double pricepurchasemembers;
              		public int maxquantity;
              		public string name;
              		public int weight;
          	}
  		
  		public struct TradeItem
  		{
  			public byte pos;
  			public int imageid;
  			public uint quantity;
  			public string name;
  			public int KnownItemsSqlID;
              		public double pricepurchase;
              		public double pricepurchasemembers;
              		public int weight;
              		public int maxQuantity;
              		public bool validTradeItem;
              		public int ELServerItemID;
          	}
  
  		
  		public TradeHandler(TCPWrapper MyTCPWrapper, BasicCommunication.MessageParser MyMessageParser, MySqlManager MyMySqlManager, Inventory MyInventory, AdvancedCommunication.ActorHandler MyActorHandler, Logger MyLogger, Storage MyStorage, errorHandler MyErrorHandler, AdvancedCommunication.HelpCommandHandler MyHelpCommandHandler)
  		{
              		this.TheErrorHandler = MyErrorHandler;
              		this.TheHelpCommandHandler = MyHelpCommandHandler;
  			this.TheTCPWrapper = MyTCPWrapper;
  			this.TheMessageParser = MyMessageParser;
  			this.TheMySqlManager = MyMySqlManager;
  			this.TheInventory = MyInventory;
  			this.TheActorHandler = MyActorHandler;
  			this.TheLogger = MyLogger;
              		this.TheStorage = MyStorage;
              		//this.TheErrorHandler = MyErrorHandler;
  
  			
  			TradeTimer.Stop();
  			TradeTimer.Interval=1000;
  			TradeTimer.Elapsed += new System.Timers.ElapsedEventHandler(TradeTimer_Tick );
  			
  			this.TheTCPWrapper.GotCommand += new TCPWrapper.GotCommandEventHandler(OnGotCommand);
  			this.TheMessageParser.Got_TradeRequest += new BasicCommunication.MessageParser.Got_TradeRequest_EventHandler(OnGotTradeRequest);
  			this.TheMessageParser.Got_AbortTrade += new BasicCommunication.MessageParser.Got_AbortTrade_EventHandler(OnGotAbortTrade);
  			this.TheInventory.GotNewInventoryList += new Inventory.GotNewInventoryListEventHandler(OnGotNewInventoryList);
              		if (Settings.IsTradeBot == true)
              		{
              		    MyHelpCommandHandler.AddCommand("#total - get a detailed total of your transaction");
              		}
  
              		TheMessageParser.Got_PM += new BasicCommunication.MessageParser.Got_PM_EventHandler(OnGotPM);
  		}
  
  
         		public bool AmITrading()
          	{
          	    return Trading;
          	}
  
  
  		private void OnGotNewInventoryList(object sender, Inventory.GotNewInventoryListEventArgs e)
  		{
  	            	if (Trading == true)
          	    	{
                  		Console.WriteLine("Getting inventory list while trading!!!");
                  		return;
  	            	}
   	        	bool Member = (TheMySqlManager.CheckIfTradeMember(TradeHandler.username, Settings.botid) == true);
              		//Log items got
  			foreach (TradeItem MyTradeItem in PartnerTradeItemsList.Values)
  			{
  				if (MyTradeItem.name!="Gold Coins")
  				{
  					string action = "";
                          		if (storageAvailable)
                          		{
                              			action = "received from (to sto)";
                          		}
                          		else
                          		{
                              			action = "received from (to inv)";
                          		}
  					if (this.PutOnSale==true)
  					{
  						action = "putonsale";
  					}
  					if (this.Donating == false && this.PutOnSale==false && !storageAvailable)
  					{
  						action = "bought from";
  					}
                      			if (((!Member && MyTradeItem.pricepurchase == 0) || (!Member && MyTradeItem.pricepurchase == 0)) && action == "bought from")
  					{
  						action = "received from (to inv)";
  					}
                      			AddTrade(MyTradeItem.KnownItemsSqlID, (Member ? MyTradeItem.pricepurchasemembers : MyTradeItem.pricepurchase), MyTradeItem.quantity, action);
                  		}
  				else
  				{
  					//Check if money was donated
                      			string action = "";
                      			if (this.Donating == true)
                      			{
                          			if (storageAvailable)
                          			{
                              				action = "received from (to sto)";
                          			}
                          			else
                          			{
                              				action = "received from (to inv)";
                          			}
                      			}
                      			else
                      			{
                          			action = "transaction coins from";
                      			}
                      			AddTrade(MyTradeItem.KnownItemsSqlID, (Member ? MyTradeItem.pricepurchasemembers : MyTradeItem.pricepurchase), (uint)(MyTradeItem.quantity - System.Convert.ToInt32(System.Math.Ceiling(this.Billance))), action);
                  		}
  			}
  
              		//can't we just check the tradeloglist, if we have a trade to log, we log it?
              		//Check this later!!!
              		if (this.MyItemList.Count > 0)
              		{
                  		LogTrade();
              		}
  
              		Trading = false;
              		itemTraded = false;
              		return;
  		}
  		
  
  		public void LogTrade()
  		{
              		if (SentThanks == false)
              		{
                  		// Log the trade
                  		// get the flag to say if coins should be displayed to the owner...
                  		bool sendOwnerCoinPMs = this.TheMySqlManager.sendCoinPMs();
                  		foreach (TradeLogItem MyTradeLogItem in MyItemList)
                  		{
                      			if (MyTradeLogItem.action.ToLower().Contains("transaction coins"))
                      			{
                          			continue;
                      			}
                      			this.TheMySqlManager.LogTrade(MyTradeLogItem, TradeHandler.username, Settings.botid, sendOwnerCoinPMs);
  
                      			if (Settings.IsTradeBot == false && !MyTradeLogItem.action.Contains("inv") && Donating == false && !withdrawMade && !depositMade)
                      			{
                          			TheMySqlManager.UpdateStorageItem(MyTradeLogItem, false);
                      			}
                  		}
                  		foreach (TradeLogItem MyTradeLogItem in MyItemList)
                  		{
                      			if (!MyTradeLogItem.action.ToLower().Contains("transaction coins"))
                      			{
                          			continue;
                      			}
                      			this.TheMySqlManager.LogTrade(MyTradeLogItem, TradeHandler.username, Settings.botid, sendOwnerCoinPMs);
  
                      			if (Settings.IsTradeBot == false && !MyTradeLogItem.action.Contains("inv") && Donating == false && !withdrawMade && !depositMade)
                      			{
                          			TheMySqlManager.UpdateStorageItem(MyTradeLogItem, false);
                      			}
                  		}
  
                  		this.PartnerTradeItemsList = new System.Collections.Hashtable();
                  		//Check this later
                  		this.MyItemList = new System.Collections.ArrayList();
  
                  		this.InventorySnapshop = TheInventory.GetInventoryList();
                  		this.InventorySnapshopLoging = (System.Collections.ArrayList)InventorySnapshop.Clone();
  
                  		this.Donating = false;
                  		this.PutOnSale = false;
                  		TheTCPWrapper.Send(CommandCreator.SEND_PM(TradeHandler.username, "Thank you for using my services!"));
                  		TheLogger.Log("Trade was successful!");
                  		SentThanks = true;
                  		openingStorage = false;
                  		TradeHandler.username = "";
              		}
              		this.storageAvailable = false;
          	}
  		
  
  		public void AddTrade(int KnownItemsSqlID, double price, uint quantity, string action)
  		{
              		TradeLogItem MyTradeLogItem;
  			MyTradeLogItem.action = action;
  			MyTradeLogItem.quantity = quantity;
  			MyTradeLogItem.price = price;
  			MyTradeLogItem.KnownItemsSqlID = KnownItemsSqlID;
              		MyTradeLogItem.categoryNum = -1;
              		int count = 0;
              		bool found = false;
              		for (count = 0; count < MyItemList.Count; count++)
              		{
                  		TradeLogItem tempItem = (TradeLogItem)MyItemList[count];
                  		if (tempItem.KnownItemsSqlID == MyTradeLogItem.KnownItemsSqlID && tempItem.action == MyTradeLogItem.action)
                  		{
                      			tempItem.quantity += quantity;
                      			MyItemList[count] = tempItem;
                      			found = true;
                      			break;
                  		}
              		}
              		if (!found)
              		{
                  		MyItemList.Add(MyTradeLogItem);
              		}
              		TheLogger.Debug("AddTrade " + KnownItemsSqlID.ToString() + "\n");
  		}
  
  
          	private void OnGotTradeRequest(object sender, BasicCommunication.MessageParser.Got_TradeRequest_EventArgs e, bool junk)
          	{
              		string TradePartnerUsername = e.username;
              		Int16 TradePartnerUserID = TheActorHandler.GetUserIDFromname(TradePartnerUsername);
              		TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
          	}
  
  
          private void OnGotTradeRequest(object sender, BasicCommunication.MessageParser.Got_TradeRequest_EventArgs e)
          {
              string TempPartnerUsername = e.username;
              Int16 TempPartnerUserID = TheActorHandler.GetUserIDFromname(TempPartnerUsername);
              if (Trading == true)
              {
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TempPartnerUsername, "Sorry, I am already trading. Please try #retrade in a few seconds!"));
                  return;
              }
              totalCalculated = false;
              string TradePartnerUsername = e.username;
              Int16 TradePartnerUserID = TheActorHandler.GetUserIDFromname(TradePartnerUsername);
              int playerRank = TheMySqlManager.GetUserRank(e.username, Settings.botid);
              int buyCommandRank = TheMySqlManager.GetCommandRank("#buy", Settings.botid);
              int guildRank = TheMySqlManager.CheckIfBannedGuild(e.username, Settings.botid);
  
              if ( playerRank < buyCommandRank )
              {
                  //TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
                  TheTCPWrapper.Send(CommandCreator.EXIT_TRADE());
                  if (playerRank < 0)
                  {
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "I don't trade with players that are banned from using my services"));
                  }
                  else
                  {
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(e.username, "Sorry, you are not authorized to trade with me!"));
                  }
                  return;
              }
  
  
              if (guildRank < 0 && ((playerRank <= buyCommandRank)||playerRank == 0))
              {
                  //TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
                  TheTCPWrapper.Send(CommandCreator.EXIT_TRADE());
                  if (guildRank < 0)
                  {
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "I don't trade with guilds that are banned from using my services"));
                  }
                  return;
              }
  
              if (MainClass.botType == 1 && !MainClass.atHome)
              {
                  string owners = TheMySqlManager.botowner(Settings.botid).ToString();
                  if (!owners.ToLower().Contains(TradePartnerUsername.ToLower()))
                  {
                      //TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
                      TheTCPWrapper.Send(CommandCreator.EXIT_TRADE());
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "I may only trade with my owners at storage.  Please try me later, after I return home."));
                      return;
                  }
              }
  			
  			// Check if I am not busy (e.g. getting inventory item names)
  			if (TheInventory.GettingInventoryItems==true || TheInventory.inventoryRequested)
  			{
                  //TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
                  //TheTCPWrapper.Send(CommandCreator.EXIT_TRADE());
  				TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername,"Sorry, I am busy. Please try #retrade in a few seconds!"));
                  return;
  			}
  
              // Check if I am a trade bot or a storage bot
              if (MainClass.botType == 3)
              {
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "Hello " + TradePartnerUsername + ", I am a guard bot"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To give me an item, just drop it in the trade window"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To get an item, use the give command eg give 1 pickaxe"));
              }
              else if (Settings.IsTradeBot == false)
              {
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "Hello " + TradePartnerUsername + ", I am a storage bot"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To give me an item, just drop it in the trade window"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To get an item, use the give command eg give 1 pickaxe"));
              }
              else
              {
  
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "Hello " + TradePartnerUsername + ", I am a trade bot"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To sell me an item, just drop it in the trade window"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "To buy an item, use the BUY command eg buy 1 pickaxe"));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "At any time during the trade you may click accept or use the #total command to get the trade details."));
                  TheTCPWrapper.Send(CommandCreator.SEND_PM(TradePartnerUsername, "I won't put coins in the trade window until you click accept once."));
              }
              // Reset all variables for fresh trade
              idleWarningSent = false;
              idleTradeTime = 0;
              totalCalculated = false;
              totalOwed = 0;
              invalidItem = false;
              TradeHandler.username = TradePartnerUsername;
  			this.MyItemList.Clear();
              this.PartnerTradeItemsList.Clear();
  			this.AcceptStateMe = 0;
  			this.AcceptStatePartner = 0;
  			this.Billance = 0;
              SentThanks = false;
  			this.InventorySnapshop = TheInventory.GetInventoryList();
  //            this.StorageSnapshot = TheStorage.GetStorageList();
  			this.InventorySnapshopLoging = (System.Collections.ArrayList)InventorySnapshop.Clone();
  			
  			this.MyMoneyAmount = TheInventory.GetMoneyAmount();
  			this.MyMoneySlotID = TheInventory.GetMoneySlotID();
              this.MyMoneyOnTrade = 0;
  			//this.MyMoneyOnTradeSlotID = 0;
  			
  			//this.Trading = false;
  			//this.Donating = false;
              if (Settings.IsTradeBot == false && MainClass.botType == 2)
              {
                  this.Donating = true;
              }
              this.PutOnSale = false;
              //TheTCPWrapper.Send(CommandCreator.SIT_DOWN(true));
  			// Start the counter for Trade-Timeout
  			if (Settings.TradeTimeOut!=0)
  			{
  				TradeTimeElapsed = 0;
  				TradeTimer.Start();
  			}
  			
  			//Reset the TradeItemsLists
              MyItemList = new ArrayList();
  			//MyTradeItemsList = new System.Collections.Hashtable();
  			PartnerTradeItemsList = new System.Collections.Hashtable();
  			
  			// Accept the trade
  			TheTCPWrapper.Send(CommandCreator.TRADE_WITH(TradePartnerUserID));
              Trading = true;
  
          }
          public bool itemTraded = false;
          public void stopTimer()
          {
              TradeTimer.Stop();
              TradeTimeElapsed = 0;
          }
  		private void OnGotAbortTrade(object sender, System.EventArgs e)
  		{
              totalOwed = 0;
              totalCalculated = false;
              TradeTimer.Stop();
              TradeTimeElapsed = 0;
              if (TheInventory.GettingInventoryItems == false && itemTraded)
              {
                  username = "";
                  this.PartnerTradeItemsList.Clear();
                  this.MyItemList.Clear();
                  //TheInventory.GettingInventoryItems = true;
                  itemTraded = false;
                  TheInventory.inventoryRequested = false;
                  TheInventory.requestInventory();
              }
              else if (TheInventory.GettingInventoryItems == false && itemTraded == false)
              {
                  TheInventory.inventoryRequested = false;
              }
              itemTraded = false;
              SentThanks = true;
              Trading = false;
              Donating = false;
              //storageAvailable = false;
              storageOpen = false;
              openingStorage = false;
              //let's try this, see if it fixes it...
              username = "";
          }
  		
  		private void TradeTimer_Tick(object sender,System.Timers.ElapsedEventArgs eArgs)
  		{
              try
              {
                  TradeTimeElapsed++;
                  idleTradeTime++;
  
                  // Check if we have reached the Trade-Timeout
                  if (TradeTimeElapsed > Settings.TradeTimeOut && Settings.TradeTimeOut != 0)
                  {
                      TheTCPWrapper.Send(CommandCreator.EXIT_TRADE());
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(username, "Sorry, your trade time is over. You had " + Settings.TradeTimeOut.ToString() + " seconds to do the trade. Please try again!"));
                      TradeTimer.Stop();
                      TradeTimeElapsed = 0;
                      Trading = false;
                      username = "";
                      SentThanks = true;
                  }
                  if (idleTradeTime > 15 && !idleWarningSent)
                  {
                      idleWarningSent = true;
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(username, "============= Reminder (from 15 seconds idle time) =============="));
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(username, "At any time during the trade you may click accept or use the #total command to get the trade details."));
                      TheTCPWrapper.Send(CommandCreator.SEND_PM(username, "I won't put coins in the trade window until you click accept once."));
                  }
              }
              catch (Exception exception)
              {
                  TheErrorHandler.writeErrorMessage("Exception raised in the trade handler timer..." + exception);
              }
              finally
              { }
  		}
  		
  		private void OnGotCommand(object sender, TCPWrapper.GotCommandEventArgs e)
  		{
              if (Gambling || claimingPrize)
              {
                  return;
              }
              if (e.CommandBuffer[0] == 35) // GET_TRADE_OBJECT
  			{
                  GET_TRADE_OBJECT(e.CommandBuffer);
  				return;
  			}
  			if (e.CommandBuffer[0] == 36)
  			{
  				GET_TRADE_ACCEPT(e.CommandBuffer);
  				return;
  			}
  			if (e.CommandBuffer[0] == 37)
  			{
  				GET_TRADE_REJECT(e.CommandBuffer);
  				return;
  			}
              if (e.CommandBuffer[0] == 38)
              {
                  GET_TRADE_ABORT(e.CommandBuffer);
                  return;
              }
              if (e.CommandBuffer[0] == 39)
  			{
  				REMOVE_TRADE_OBJECT(e.CommandBuffer);
  				return;
  			}
  			if (e.CommandBuffer[0] == 20)
  			{
  				INVENTORY_ITEM_TEXT(e.CommandBuffer);
  				return;
  			}
              // 40 = GET_YOUR_TRADE_OBJECTS, sent whenever a trade is initiated
              if (e.CommandBuffer[0] == 40) //GET_YOUR_TRADE_OBJECTS
              {
                  GET_YOUR_TRADE_OBJECTS(e.CommandBuffer);
                  return;
              }
              if (e.CommandBuffer[0] == 41) //GET_TRADE_PARTNER_name
              {
                  int i,storagetrade;
                  i = System.BitConverter.ToInt16(e.CommandBuffer, 1) - 2;
                  storagetrade = System.BitConverter.ToInt16(e.CommandBuffer, 3);
                  string name = System.Text.Encoding.ASCII.GetString(e.CommandBuffer, 4, i);
                  return;
              }
              if (e.CommandBuffer[0] == 67) // STORAGE_LIST
              {
                  STORAGE_LIST(e.CommandBuffer);
                  return;
              }
              
              if (e.CommandBuffer[0] == 68) // STORAGE_ITEMS (per category)
              {
                  STORAGE_ITEMS(e.CommandBuffer);
                  return;
              }
          
              if (e.CommandBuffer[0] == 69) // STORAGE_TEXT
              {
                  STORAGE_TEXT(e.CommandBuffer);
                  return;
              }
          }
  
          private void GET_YOUR_TRADE_OBJECTS(byte[] data)
          {
              int itemCount = data[3];
              return;
          }
  
          private void STORAGE_TEXT(byte[] data)
          {
              if (TheInventory.GettingInventoryItems == true || withdrawMade || depositMade || storageOpen)
                  return;
  
              string TempItemDescription;
  
              TempItemDescription = System.Text.ASCIIEncoding.ASCII.GetString(data, 4, data.Length - 4).Trim();
              TheStorage.UpdateText(TempItemDescription);
              //if (!openingStorage && storageOpen)
              if (!openingStorage)
              {
  
gt:                 this.StorageSnapshot = TheStorage.GetStorageList();