/**
 * \example TradingSample.java  \@code
 */
/** \@code */

package com.marketfactory.sample_zerogc;

import com.marketfactory.zerogc.api.*;

import java.util.Map;
import java.util.HashMap;
import java.util.logging.Logger;
import java.io.IOException;

public class TradingSample {

    private static final String DEFAULT_HOSTNAME = "ny9.marketfactory.com";
    private static final int DEFAULT_PORT = 10033;

    private static final String USAGE_PARAMETERS = "username password [hostname] [port]";

    public static void main(String args[]) throws IOException, InterruptedException {
        // Parse command-line arguments, 1980's-style.
        if (args.length < 2) {
            throw new RuntimeException("Not enough parameters provided. Parameters: " + USAGE_PARAMETERS);
        }

        final String username = args[0];
        final String password = args[1];
        final String hostname = args.length >=3 ? args[2] : DEFAULT_HOSTNAME;
        final int port = args.length >=4 ? Integer.valueOf(args[3]) : DEFAULT_PORT;

        // Setup logging.
        Logger log = Logger.getLogger("example");

        // Connect to our server.
        try {
            MFClient client = MFClient.open(new MFString(username), new MFString(password), new MFString(hostname), port);
            if (client == null) {
                log.severe("Could not connect.");
                System.exit(1);
            }

            TradingHandlerImpl handler = new TradingHandlerImpl();
            // Dispatch messages on the handler (until client.stop() is called).
            client.run(handler);

            try {
                Thread.sleep(10000);
            } catch (InterruptedException ex) {
                // ...
            }

            // Reconnect.
            client.connect();
            APIResult status = client.run(handler);

            System.exit(status.get());
        } catch (IOException e) {
            log.severe("Error: " + e);
        }
    }
}

// A concrete implementation of the MFHandler Interface
class TradingHandlerImpl extends AbstractMFHandler {

    // The last received sequenceID from a given feed.
    // It would be best to persist this data on disk.
    private Map<Integer, Integer> _feedSeqIDMap = new HashMap<Integer, Integer>();

    TradingHandlerImpl() {
    }

    // The OnConnect call back indicates that your list of available Feeds
    // and Markets has been received from the server and you may subscribe
    public void onStart(MFClient client) {
        System.out.println("onStart() callback");

        // Without a subscription orders will be immediately rejected
        APIFeed hotspot = client.getFeedByName("HOTSPOT");

        // Subscribe to the corresponding order flow feed.
        client.subscribeOF(hotspot.feedID);
    }

    public void onStop(MFClient client) {
    }

    public void onDisconnect(MFClient client) {
    }

    public void onConnect(MFClient client) {
    }

    public void onSubscriptionEvent(SubscriptionEvent subscriptionEvent, MFClient client) {
        System.out.println("onSubscriptionEvent()");
        System.out.println(subscriptionEvent);

        if (subscriptionEvent.status == SubscriptionStatus.ENABLED) {
            APIFeed hotspot = client.getFeedByName("HOTSPOT");
            assert subscriptionEvent.sub.feedID == hotspot.feedID;

            // Check for missed messages.
            int lastSeqID = client.getSequenceID(hotspot.feedID);
            Integer lastReceivedID = _feedSeqIDMap.get(new Integer(hotspot.feedID));
            if (lastReceivedID != null && lastReceivedID < lastSeqID) {
                System.out.println("Missed messages. Requesting replay..");
                // Send replay request to the server.
                client.replay(hotspot.feedID, lastReceivedID + 1, -1);
            } else {
                APIMarket m = client.getMarketByName(hotspot, "USD/JPY");
                if (m != null) {
                    final OrderIdPair res = new OrderIdPair(null, 0L);
                    client.submitOrder(new MFString("TestOrder"),
                            hotspot.feedID,
                            m.marketID,
                            Side.BID,
                            new MFFloat("1000000"),
                            new MFFloat("78.123"),
                            OrderType.LIMIT,
                            TimeInForce.GTC,
                            res);
                    System.out.println("submitted order ID:" + res.second);
                }
            }
        }
    }

    public void onMarketData(MarketView md, MFClient client) {
    }

    public void onEventReport(EventReport eventReport, MFClient client) {
    }

    public void onOrderReceived(OrderReceived orderReceived, MFClient client) {
        updateLastSequenceID(orderReceived.feedID, orderReceived.sequenceID);
        System.out.println("onOrderReceived()");
        System.out.println("order ID:" + orderReceived.orderID);
    }

    public void onOrderRejected(OrderRejected orderRejected, MFClient client) {
        updateLastSequenceID(orderRejected.feedID, orderRejected.sequenceID);
        System.out.println("onOrderRejected()");
        System.out.println("order ID:" + orderRejected.orderID);
        System.out.println("reason:" + orderRejected.reason);
    }

    public void onOrderSubmitted(OrderSubmitted orderSubmitted, MFClient client) {
        updateLastSequenceID(orderSubmitted.feedID, orderSubmitted.sequenceID);
        System.out.println("onOrderSubmitted()");
        System.out.println("order ID:" + orderSubmitted.orderID);
    }

    public void onOrderCanceled(OrderCanceled orderCanceled, MFClient client) {
        updateLastSequenceID(orderCanceled.feedID, orderCanceled.sequenceID);
        System.out.println("onOrderCanceled()");
        System.out.println("order ID:" + orderCanceled.orderID);
        System.out.println("amount:" + orderCanceled.amount);
        System.out.println("cancel ID:" + orderCanceled.cxlID);
    }

    public void onOrderCancelRejected(OrderCancelRejected orderCancelRejected, MFClient client) {
        updateLastSequenceID(orderCancelRejected.feedID, orderCancelRejected.sequenceID);
        System.out.println("onOrderCancelRejected()");
        System.out.println("order ID:" + orderCancelRejected.orderID);
        System.out.println("reject reason:" + orderCancelRejected.reason);
    }

    public void onOrderDone(OrderDone orderDone, MFClient client) {
        updateLastSequenceID(orderDone.feedID, orderDone.sequenceID);
        System.out.println("onOrderDone()");
        System.out.println("order ID:" + orderDone.orderID);
    }

    public void onDropCopy(DropCopy dropCopy, MFClient client) {
        updateLastSequenceID(dropCopy.feedID, dropCopy.sequenceID);
        System.out.println("onDropCopy()");
        System.out.println(dropCopy.toString());
    }

    public void onTradeCapture(TradeCapture tradeCapture, MFClient client) {
        updateLastSequenceID(tradeCapture.feedID, tradeCapture.sequenceID);
        System.out.println("onTradeCapture()");
        System.out.println("order ID:" + tradeCapture.orderID);
        System.out.println("amount:" + tradeCapture.amount);
        System.out.println("price:" + tradeCapture.price);
        System.out.println("match status:" + tradeCapture.matchStatus);
        System.out.println("side:" + tradeCapture.side);
        System.out.println("is Taker:" + tradeCapture.isTaker);
        System.out.println("exchange trade id:" + tradeCapture.exTradeID);
    }

    // Update the last sequence ID for a given feed.
    private void updateLastSequenceID(int feedID, int sequenceID) {
        Integer lastID = _feedSeqIDMap.get(new Integer(feedID));
        // Update only if this message has bigger sequenceID than the last sequenceID.
        if (lastID == null || sequenceID > lastID.intValue())
            _feedSeqIDMap.put(new Integer(feedID), new Integer(sequenceID));
    }
}
/** \@endcode */
