using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Data;
using VBNET = Microsoft.VisualBasic;
using gmRTL.Core;
using Microsoft.VisualBasic.CompilerServices;
using System.Linq;
using System.Collections.Generic;

namespace FMStocks_Bus
{
   public class Broker
   {
      private const string m_modName = "FMStocks_Bus.Broker";
      private const int m_errInsufficentFunds = -2147217900;
      private const decimal m_Commission = 9.95M;
      //  returns 0 on nothing to do, -1 on error, otherwise the TxID
      public int BuyStock(int AccountID,string Ticker,int Shares)
      {
         int BuyStock = 0;


         FMStocks_DB.Tx objTX = null;
         FMStocks_DB.Ticker objTicker = null;
         FMStocks_DB.Broker objbroker = null;
         FMStocks_DB.TxNew objTXNew = null;
         int lngTXID = 0;
         try
         {
            gmRTL.Core.GlobalException.Clear();

            int lngErr = 0;
            int lngPositionID = 0;
            int lngPrice = 0;


            objTX = new FMStocks_DB.Tx();
            objTicker = new FMStocks_DB.Ticker();
            objbroker = new FMStocks_DB.Broker();

            //  first log the transaction
            lngTXID = objTX.AddBuyOrder(AccountID,Ticker,Shares);

            // 
            // Get the current price
            lngPrice = objTicker.GetPrice(Ticker).ToInt();     //  price in pennies

            gmRTL.Core.ErrorHandling.ResumeNext(() =>
            {
               lngPositionID = objbroker.Buy(lngTXID,AccountID,Ticker,Shares,(decimal)(lngPrice),m_Commission);
               lngErr = gmRTL.Core.GlobalException.Instance.Number;     //  the next line resets Err.Number to 0, so we need to cache it
            });
            // UPGRADE_TODO: OnErrorGoto(errorHandler);

            if (lngPositionID == 0 && lngErr == m_errInsufficentFunds)
            {
               //  It's common that the user doesn't have enough funds.
               //  Set the TransactionTypeID = 5 indicating insufficient funds
               // 
               //  This guy runs as a separate transaction
               objTXNew = new FMStocks_DB.TxNew();
               objTXNew.SetTxType(lngTXID,((int)FMStocks_DB.Helpers.FMStocks_TransactionType.Insufficient_Funds));

               goto InsufficientFundsHandler;
            }
            else if (lngPositionID == 0)
            {
               throw new System.Exception();     //  some other error was thrown that we can't handle
            }
            else
            {
               BuyStock = lngTXID;     //  The trade was handled
            }

            objTX = null;
            objbroker = null;
            objTXNew = null;
            objTicker = null;


            return BuyStock;

         InsufficientFundsHandler:
            BuyStock = 0;     //  Signify that we handled this error.
            //  The caller should be aware that when this is returned,
            //  the transaction has been aborted, the object deactivated.
            //  An error isn't raised here (SP2) to be consistent with
            //  prior versions.

            objTX = null;
            objTicker = null;
            objbroker = null;
            objTXNew = null;


            return BuyStock;

         }
         catch(System.Exception exc)
         {
            gmRTL.Core.GlobalException.Initialize(exc);
            BuyStock = -1;
            objTX = null;
            objTicker = null;
            objbroker = null;
            objTXNew = null;


            FMStocks_DB.Helpers.RaiseError(m_modName,"BuyStock(" + AccountID + "," + Ticker + "," + Shares + ")",String.Empty,0,false);

         }
         return BuyStock;
      }
      //  returns 0 on nothing to do, -1 on error, otherwise the TxID
      public int SellStock(int AccountID,string Ticker,int Shares)
      {
         int SellStock = 0;


         FMStocks_DB.Tx objTX = null;
         FMStocks_DB.Broker objbroker = null;
         FMStocks_DB.TxNew objTXNew = null;
         FMStocks_DB.Ticker objTicker = null;
         try
         {
            gmRTL.Core.GlobalException.Clear();

            decimal price = 0.00M;
            int txid = 0;
            bool bSellretval = false;


            objTX = new FMStocks_DB.Tx();
            objbroker = new FMStocks_DB.Broker();
            objTicker = new FMStocks_DB.Ticker();

            txid = objTX.AddSellOrder(AccountID,Ticker,Shares);
            objTX = null;

            // Get the current price
            price = objTicker.GetPrice(Ticker);     //  price in pennies

            gmRTL.Core.ErrorHandling.ResumeNext(() =>
            {
               // ignore errors on this.
               bSellretval = objbroker.Sell(txid,price,m_Commission);

            });
            // UPGRADE_TODO: OnErrorGoto(errorHandler);

            if (!bSellretval)
            {
               //  This runs as a separate transaction
               objTXNew = new FMStocks_DB.TxNew();

               //  User tried to sell shares they didn't own
               objTXNew.SetTxType(txid,((int)FMStocks_DB.Helpers.FMStocks_TransactionType.Not_Enough_Shares));

               goto NotEnoughSharesHandler;
            }
            else
            {
               //  We're responsible for removing the appropriate shares from their positions
               AdjustPositions(AccountID,Ticker,Shares);
               SellStock = txid;
            }

            objTX = null;
            objbroker = null;
            objTXNew = null;
            objTicker = null;


            return SellStock;

         NotEnoughSharesHandler:
            SellStock = 0;     //  Signify that we handled this error.
            //  The caller should be aware that when this is returned,
            //  the transaction has been aborted, the object deactivated.
            //  An error isn't raised here (SP2) to be consistent with
            //  prior versions.

            objTX = null;
            objbroker = null;
            objTXNew = null;
            objTicker = null;


            return SellStock;

         }
         catch(System.Exception exc)
         {
            gmRTL.Core.GlobalException.Initialize(exc);
            SellStock = -1;
            objTX = null;
            objbroker = null;
            objTXNew = null;
            objTicker = null;


            FMStocks_DB.Helpers.RaiseError(m_modName,"SellStock(" + AccountID + "," + Ticker + "," + Shares + ")",String.Empty,0,false);
         }
         return SellStock;
      }
      //  no longer in used by buy or sell stocks
      //  Used by ExecuteTrade program.
      //  Returns 0 on nothing to do, -1 on error, otherwise the TxID
      public int ExecuteTransaction(int txid)
      {
         int ExecuteTransaction = 0;

         // UPGRADE_TODO: OnErrorGoto(errorHandler);

         gmRTL.DataLib.SqlClient.Recordset rs = null;
         FMStocks_DB.Tx objTX = null;
         FMStocks_DB.Ticker objTicker = null;
         FMStocks_DB.Broker objbroker = null;
         FMStocks_DB.TxNew o = null;

         objTX = new FMStocks_DB.Tx();
         objTicker = new FMStocks_DB.Ticker();
         objbroker = new FMStocks_DB.Broker();

         // information about the current transaction
         int AccountID = 0;
         string Ticker = "";
         int PositionID = 0;
         decimal price = 0.00M;
         bool retval = false;

         int lerr = 0;

         // 
         //  Get the Tx details
         rs = objTX.GetByID(txid);

         if (rs.EOF && rs.BOF)
         {
            return ExecuteTransaction;
         }

         AccountID = rs.Fields["AccountID"].Value.ToInt();
         Ticker = (rs.Fields["Ticker"].Value.ToStr()).TrimR();

         //  Only execute buy or sell orders
         if (rs.Fields["TransactionTypeID"].Value.ToInt() != 1 && rs.Fields["TransactionTypeID"].Value.ToInt() != 2)
         {
            ExecuteTransaction = 0;

            return ExecuteTransaction;
         }

         // Get the current price
         price = objTicker.GetPrice(Ticker);     //  price in pennies

         // Execute the trade
         if (rs.Fields["TransactionTypeID"].Value.ToInt() == 1)
         {
            //  Buy order

            gmRTL.Core.ErrorHandling.ResumeNext(() =>
            {
               PositionID = objbroker.Buy(txid,AccountID,Ticker,rs.Fields["shares"].Value.ToInt(),price,m_Commission);
               lerr = gmRTL.Core.GlobalException.Instance.Number;     //  the next line resets Err.Number to 0, so we need to cache it
            });
            // UPGRADE_TODO: OnErrorGoto(errorHandler);

            if (PositionID == 0 && lerr == m_errInsufficentFunds)
            {
               //  It's common that the user doesn't have enough funds.
               //  Set the TransactionTypeID = 5 indicating insufficient funds
               // 
               //  This guy runs as a separate transaction
               o = new FMStocks_DB.TxNew();
               o.SetTxType(txid,((int)FMStocks_DB.Helpers.FMStocks_TransactionType.Insufficient_Funds));
               o = null;

               goto InsufficientFundsHandler;
            }
            else if (PositionID == 0)
            {
               goto errorHandler;     //  some other error was thrown that we can't handle
            }
            else
            {
               ExecuteTransaction = txid;     //  The trade was handled
            }
         }
         else
         {
            //  Sell order

            gmRTL.Core.ErrorHandling.ResumeNext(() =>
            {
               retval = objbroker.Sell(txid,price,m_Commission);
               lerr = gmRTL.Core.GlobalException.Instance.Number;     //  the next line resets Err.Number to 0, so we need to cache it
            });
            // UPGRADE_TODO: OnErrorGoto(errorHandler);

            if (!retval)
            {
               gmRTL.Core.GlobalException.Clear();

               //  This runs as a separate transaction
               o = new FMStocks_DB.TxNew();
               o.SetTxType(txid,((int)FMStocks_DB.Helpers.FMStocks_TransactionType.Not_Enough_Shares));     //  User tried to sell shares they didn't own
               o = null;

               goto NotEnoughSharesHandler;
            }
            else
            {
               //  We're responsible for removing the appropriate shares from their positions
               AdjustPositions(AccountID,Ticker,rs.Fields["shares"].Value.ToInt());
               ExecuteTransaction = txid;
            }

         }


         return ExecuteTransaction;

      InsufficientFundsHandler:
      NotEnoughSharesHandler:
         ExecuteTransaction = 0;     //  Signify that we handled this error.
         //  The caller should be aware that when this is returned,
         //  the transaction has been aborted, the object deactivated.
         //  An error isn't raised here (SP2) to be consistent with
         //  prior versions.

         objTX = null;
         objTicker = null;
         objbroker = null;
         rs = null;


         return ExecuteTransaction;

      errorHandler:
         ExecuteTransaction = -1;     // an invalid TransactionID used to flag an error.
         objTX = null;
         objTicker = null;
         objbroker = null;
         rs = null;
         o = null;


         FMStocks_DB.Helpers.RaiseError(m_modName,"ExecuteTransaction(" + txid + ")",String.Empty,0,false);

         return ExecuteTransaction;
      }
      // all errors are handled by the caller, ExecuteTransaction
      private void AdjustPositions(int AccountID,string Ticker,int Shares)
      {
         FMStocks_DB.Position objPosition = null;
         objPosition = new FMStocks_DB.Position();

         gmRTL.DataLib.SqlClient.Recordset rs = null;
         int posShares = 0;     //  number of shares for the current position
         int origShares = 0;     // number of shares before sell
         bool boolAdjusted = false;

         boolAdjusted = false;

         rs = objPosition.ListForAdjustment(AccountID,Ticker);
         while (!rs.EOF && Shares > 0)
         {
            posShares = rs.Fields["shares"].Value.ToInt();
            boolAdjusted = true;
            if (posShares <= Shares)
            {
               //  we can delete this whole record
               rs.Delete();
               Shares = Shares - posShares;
            }
            else
            {
               rs.Fields["shares"].Value = posShares - Shares;     //  decrement current share amount by amount sold
               Shares = 0;
            }
            rs.MoveNext();
         }
         rs.UpdateBatch(gmRTL.DataLib.AffectEnum.adAffectAll);
         rs.Close();
         rs = null;
      }
   }
}
