package jwsunleashed.trading.ejb;

import java.rmi.*;
import javax.rmi.*;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;

public class TradingImpl implements SessionBean
{
// References to other EJBs used by this class
    protected TradeHome tradeHome;
    protected TraderHome traderHome;
    protected BuyOrderHome buyOrderHome;
    protected PutOrderHome putOrderHome;

// The TraderID for this session's user
    protected int currTraderID;

    private SessionContext context;

    public TradingImpl()
    {
    }

/** Creates a new instance of the Trading session */
    public void ejbCreate(String userID, String password)
        throws CreateException
    {
        try
        {
            Context ctx = new InitialContext();

/** Locate the Home interfaces for the other objects this session uses */
            tradeHome = (TradeHome) PortableRemoteObject.narrow(
                ctx.lookup("java:comp/env/ejb/TradeHome"),
                TradeHome.class);

            traderHome = (TraderHome) PortableRemoteObject.narrow(
                ctx.lookup("java:comp/env/ejb/TraderHome"),
                TraderHome.class);

            putOrderHome = (PutOrderHome) PortableRemoteObject.narrow(
                ctx.lookup("java:comp/env/ejb/PutOrderHome"),
                PutOrderHome.class);

            buyOrderHome = (BuyOrderHome) PortableRemoteObject.narrow(
                ctx.lookup("java:comp/env/ejb/BuyOrderHome"),
                BuyOrderHome.class);

// Look for a Trader with the specified user ID
            Trader thisTrader = traderHome.findByUserID(userID);

            if (thisTrader == null)
            {
                throw new CreateException("Invalid User ID");
            }

            String traderPassword = thisTrader.getPassword();

// Make sure the passwords match
            if ((password == null) || (traderPassword == null) ||
                !password.equals(traderPassword))
            {
                throw new CreateException("Invalid password");
            }

// Keep track of this user's trader ID
            Integer traderKey = (Integer) thisTrader.getPrimaryKey();
            currTraderID = traderKey.intValue();
        }
        catch (NamingException exc)
        {
            throw new CreateException("Unable to create trading session: "+
                exc.toString());
        }
        catch (FinderException exc)
        {
            throw new CreateException("Invalid User ID");
        }
        catch (EJBException exc)
        {
            throw new CreateException("Unable to create trading session: "+
                exc.toString());
        }
        catch (RemoteException exc)
        {
            throw new CreateException("Unable to create trading session: "+
                exc.toString());
        }
    }

    public void setSessionContext(SessionContext aContext)
    {
        context = aContext;
    }

    public void ejbActivate()
    {
    }

    public void ejbPassivate()
    {
    }

    public void ejbRemove()
    {
    }

    public TradeResult put(String stock, int shares, int price)
        throws EJBException
    {
        try
        {
// Find a buy order that matches this put
            Collection matchedBuys = buyOrderHome.findMatches(
                stock, shares, price);

            if (matchedBuys.size() >= 1)
            {
                Iterator iter = matchedBuys.iterator();

                BuyOrder bestMatch = null;

// Find the earliest buy order that matches
                while (iter.hasNext())
                {
                    BuyOrder currMatch = (BuyOrder) PortableRemoteObject.
                        narrow(iter.next(), BuyOrder.class);
                    
                    if ((bestMatch == null) ||
                        (currMatch.getBuyTime().before(
                            bestMatch.getBuyTime())))
                    {
                        bestMatch = currMatch;
                    }
                }

// Register this trade
                Trade newTrade = tradeHome.create();

                newTrade.setSellerID(currTraderID);
                newTrade.setBuyerID(bestMatch.getBuyerID());
                newTrade.setStock(stock);
                newTrade.setShares(shares);
                newTrade.setPrice(price);
                newTrade.setTransTime(new Date());
                newTrade.setBuyID(((Integer) bestMatch.getPrimaryKey()).
                    intValue());

// Remove the buy order from the database
                bestMatch.remove();

// Create the return result
                TradeResult result = new TradeResult();
                result.status = TradeResult.TRADE_COMPLETED;
                result.tradeID = ((Integer) newTrade.getPrimaryKey()).
                    intValue();
                result.sellerID = currTraderID;
                result.buyerID = newTrade.getBuyerID();
                result.buyID = newTrade.getBuyID();
                result.tradeType = TradeResult.PUT;
                result.stock = stock;
                result.price = price;
                result.shares = shares;
    
                return result;
            }
        }
        catch (FinderException exc)
        {
        }
        catch (CreateException exc)
        {
            exc.printStackTrace();
        }
        catch (RemoveException exc)
        {
            exc.printStackTrace();
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
            
        try
        {
// If there were no matching buy orders, create a new put order
            PutOrder newPut = putOrderHome.create();

            newPut.setSellerID(currTraderID);
            newPut.setStock(stock);
            newPut.setShares(shares);
            newPut.setPrice(price);
            newPut.setPutTime(new Date());

// Create the result
            TradeResult result = new TradeResult();
            result.status = TradeResult.TRADE_PENDING;
            result.sellerID = currTraderID;
            result.putID = ((Integer) newPut.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.PUT;
            result.stock = stock;
            result.price = price;
            result.shares = shares;

            return result;
        }
        catch (CreateException exc)
        {
            exc.printStackTrace();
            throw new EJBException("Unable to create new put order");
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult buy(String stock, int shares, int price)
        throws EJBException
    {
        try
        {
// Find a put order that matches this buy
            Collection matchedPuts = putOrderHome.findMatches(
                stock, shares, price);

            if (matchedPuts.size() >= 1)
            {
                Iterator iter = matchedPuts.iterator();

                PutOrder bestMatch = null;

// Find the earliest put order that matches
                while (iter.hasNext())
                {
                    PutOrder currMatch = (PutOrder) PortableRemoteObject.
                        narrow(iter.next(), PutOrder.class);
                    
                    if ((bestMatch == null) ||
                        (currMatch.getPutTime().before(
                            bestMatch.getPutTime())))
                    {
                        bestMatch = currMatch;
                    }
                }

// Register the trade
                Trade newTrade = tradeHome.create();

                newTrade.setBuyerID(currTraderID);
                newTrade.setSellerID(bestMatch.getSellerID());
                newTrade.setStock(stock);
                newTrade.setShares(shares);
                newTrade.setPrice(price);
                newTrade.setTransTime(new Date());
                newTrade.setPutID(((Integer) bestMatch.getPrimaryKey()).
                    intValue());

// Remove the buy order
                bestMatch.remove();

                TradeResult result = new TradeResult();
                result.status = TradeResult.TRADE_COMPLETED;
                result.tradeID = ((Integer) newTrade.getPrimaryKey()).
                    intValue();
                result.buyerID = currTraderID;
                result.sellerID = newTrade.getSellerID();
                result.putID = newTrade.getPutID();
                result.tradeType = TradeResult.BUY;
                result.stock = stock;
                result.price = price;
                result.shares = shares;

                return result;
            }
        }
        catch (FinderException exc)
        {
        }
        catch (CreateException exc)
        {
            exc.printStackTrace();
        }
        catch (RemoveException exc)
        {
            exc.printStackTrace();
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
            
        try
        {
// If there was no matching put, store this buy order in the database
            BuyOrder newBuy = buyOrderHome.create();

            newBuy.setBuyerID(currTraderID);
            newBuy.setStock(stock);
            newBuy.setShares(shares);
            newBuy.setPrice(price);
            newBuy.setBuyTime(new Date());

// Create the result to return to the client
            TradeResult result = new TradeResult();
            result.status = TradeResult.TRADE_PENDING;
            result.buyerID = currTraderID;
            result.buyID = ((Integer) newBuy.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.BUY;
            result.stock = stock;
            result.price = price;
            result.shares = shares;

            return result;
        }
        catch (CreateException exc)
        {
            exc.printStackTrace();
            throw new EJBException("Unable to create new buy order");
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult getPutStatus(int putID)
        throws EJBException
    {
        PutOrder putOrder = null;

        try
        {
// Locate the put order with the specified ID
            putOrder = putOrderHome.findByPrimaryKey(new Integer(putID));
        }
        catch (FinderException exc)
        {
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }

        try
        {
// If there was no put order found, see if it has already been
// accepted for a trade
            if (putOrder == null)
            {
                Trade trade = null;

                try
                {
                    trade = tradeHome.findByPutID(putID);
                }
                catch (FinderException exc)
                {
                }

// If the put was accepted for a trade, return a result describing the trade
                if ((trade != null) &&
                    (trade.getSellerID() == currTraderID))
                {
                    TradeResult result = new TradeResult();
                    result.status = TradeResult.TRADE_COMPLETED;
                    result.tradeID = ((Integer) trade.getPrimaryKey()).
                        intValue();
                    result.sellerID = currTraderID;
                    result.buyerID = trade.getBuyerID();
                    result.tradeType = TradeResult.PUT;
                    result.stock = trade.getStock();
                    result.price = trade.getPrice();
                    result.shares = trade.getShares();

                    return result;
                }
            }

// If there was no put found, tell the user
            if ((putOrder == null) ||
                (putOrder.getSellerID() != currTraderID))
            {
                TradeResult result = new TradeResult();
                result.status = TradeResult.NO_PUT_FOUND;
                result.putID = putID;

                return result;
            }

// Return a result describing the put
            TradeResult result = new TradeResult();

            result.status = TradeResult.TRADE_PENDING;
            result.sellerID = putOrder.getSellerID();
            result.putID = ((Integer) putOrder.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.PUT;
            result.stock = putOrder.getStock();
            result.price = putOrder.getPrice();
            result.shares = putOrder.getShares();

            return result;
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult getBuyStatus(int buyID)
        throws EJBException
    {
        BuyOrder buyOrder = null;

        try
        {
// See if there is a buy order matching the specified ID
            buyOrder = buyOrderHome.findByPrimaryKey(
                new Integer(buyID));
        }
        catch (FinderException exc)
        {
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }

        try
        {
// If there was no matching buy order, see if it has been
// converted to a trade
            if (buyOrder == null)
            {
                Trade trade = null;

                try
                {
                    trade = tradeHome.findByBuyID(buyID);
                }
                catch (FinderException exc)
                {
                }

// If the buy has been converted to a trade, describe the trade
                if ((trade != null) &&
                    (trade.getBuyerID() == currTraderID))
                {
                    TradeResult result = new TradeResult();
                    result.status = TradeResult.TRADE_COMPLETED;
                    result.tradeID = ((Integer) trade.getPrimaryKey()).
                        intValue();
                    result.sellerID = trade.getSellerID();
                    result.buyerID = currTraderID;
                    result.tradeType = TradeResult.BUY;
                    result.stock = trade.getStock();
                    result.price = trade.getPrice();
                    result.shares = trade.getShares();

                    return result;
                }
            }

// If there was no buy order found, tell the user
            if ((buyOrder == null) ||
                (buyOrder.getBuyerID() != currTraderID))
            {
                TradeResult result = new TradeResult();
                result.status = TradeResult.NO_BUY_FOUND;
                result.buyID = buyID;

                return result;
            }

// Otherwise, decribe the buy order
            TradeResult result = new TradeResult();

            result.status = TradeResult.TRADE_PENDING;
            result.buyerID = buyOrder.getBuyerID();
            result.buyID = ((Integer) buyOrder.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.BUY;
            result.stock = buyOrder.getStock();
            result.price = buyOrder.getPrice();
            result.shares = buyOrder.getShares();

            return result;
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult retractPut(int putID)
        throws EJBException
    {
        try
        {
// Locate the put order to retract
            PutOrder putOrder = putOrderHome.findByPrimaryKey(
                new Integer(putID));

            if ((putOrder == null) ||
                (putOrder.getSellerID() != currTraderID))
            {
                TradeResult result = new TradeResult();
                result.status = TradeResult.NO_PUT_FOUND;
                result.putID = putID;

                return result;
            }

// Create the result before removing the put
            TradeResult result = new TradeResult();

            result.status = TradeResult.TRADE_RETRACTED;
            result.sellerID = putOrder.getSellerID();
            result.putID = ((Integer) putOrder.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.PUT;
            result.stock = putOrder.getStock();
            result.price = putOrder.getPrice();
            result.shares = putOrder.getShares();

// Delete the put from the database
            putOrder.remove();

            return result;
        }
        catch (FinderException exc)
        {
            TradeResult result = new TradeResult();
            result.status = TradeResult.NO_PUT_FOUND;
            result.putID = putID;

            return result;
        }
        catch (RemoveException exc)
        {
            exc.printStackTrace();
            throw new EJBException("Unable to remove put order");
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult retractBuy(int buyID)
        throws EJBException
    {
        try
        {
// Locate the buy order
            BuyOrder buyOrder = buyOrderHome.findByPrimaryKey(
                new Integer(buyID));

            if ((buyOrder == null) ||
                (buyOrder.getBuyerID() != currTraderID))
            {
                TradeResult result = new TradeResult();
                result.status = TradeResult.NO_BUY_FOUND;
                result.buyID = buyID;

                return result;
            }

// Create the result before deleting
            TradeResult result = new TradeResult();

            result.status = TradeResult.TRADE_RETRACTED;
            result.sellerID = buyOrder.getBuyerID();
            result.buyID = ((Integer) buyOrder.getPrimaryKey()).intValue();
            result.tradeType = TradeResult.BUY;
            result.stock = buyOrder.getStock();
            result.price = buyOrder.getPrice();
            result.shares = buyOrder.getShares();

// Remove the buy order
            buyOrder.remove();

            return result;
        }
        catch (FinderException exc)
        {
            TradeResult result = new TradeResult();
            result.status = TradeResult.NO_BUY_FOUND;
            result.buyID = buyID;

            return result;
        }
        catch (RemoveException exc)
        {
            exc.printStackTrace();
            throw new EJBException("Unable to remove buy order");
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult[] getOutstandingPuts()
        throws EJBException
    {
        try
        {
// Locate all puts for this trader
            Collection puts = putOrderHome.findBySellerID(currTraderID);

            Iterator iter = puts.iterator();

            ArrayList results = new ArrayList();

            while (iter.hasNext())
            {
                PutOrder putOrder = (PutOrder) PortableRemoteObject.narrow(
                    iter.next(), PutOrder.class);

// Create a TradeResult for each outstanding put
                TradeResult result = new TradeResult();
                result.status = TradeResult.TRADE_PENDING;
                result.sellerID = putOrder.getSellerID();
                result.putID = ((Integer) putOrder.getPrimaryKey()).intValue();
                result.tradeType = TradeResult.PUT;
                result.stock = putOrder.getStock();
                result.price = putOrder.getPrice();
                result.shares = putOrder.getShares();

                results.add(result);
            }

// Return the list of results as an array
            return (TradeResult[]) results.toArray(
                new TradeResult[results.size()]);
        }
        catch (FinderException exc)
        {
            exc.printStackTrace();
            return new TradeResult[0];
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }

    public TradeResult[] getOutstandingBuys()
        throws EJBException
    {
        try
        {
// Locate all the outstanding buys for this trader
            Collection buys = buyOrderHome.findByBuyerID(currTraderID);

            Iterator iter = buys.iterator();

            ArrayList results = new ArrayList();

            while (iter.hasNext())
            {
                BuyOrder buyOrder = (BuyOrder) PortableRemoteObject.narrow(
                    iter.next(), BuyOrder.class);

// Create a result for each buy
                TradeResult result = new TradeResult();
                result.status = TradeResult.TRADE_PENDING;
                result.buyerID = buyOrder.getBuyerID();
                result.buyID = ((Integer) buyOrder.getPrimaryKey()).intValue();
                result.tradeType = TradeResult.BUY;
                result.stock = buyOrder.getStock();
                result.price = buyOrder.getPrice();
                result.shares = buyOrder.getShares();

                results.add(result);
            }

// Return the list as an array
            return (TradeResult[]) results.toArray(
                new TradeResult[results.size()]);
        }
        catch (FinderException exc)
        {
            exc.printStackTrace();
            return new TradeResult[0];
        }
        catch (RemoteException exc)
        {
            exc.printStackTrace();
            throw new EJBException(exc.toString());
        }
    }
}

