package jforex.strategies;

import com.dukascopy.api.*;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.indicators.IIndicator;
import java.text.DecimalFormat;

public class MAStrategy implements IStrategy {

    private IEngine engine;
    private IConsole console;
//    private IHistory history;
    private IIndicators indicators;
    private int counter = 0;
    private IOrder order;
    
    @Configurable("Instrument")
    public Instrument instrument = Instrument.EURUSD;
    @Configurable("Period")
    public Period selectedPeriod = Period.ONE_HOUR;
    @Configurable("Offer side")
    public OfferSide offerSide = OfferSide.BID;
    @Configurable("Applied price")
    public AppliedPrice appliedPrice = AppliedPrice.CLOSE;
    @Configurable("Slippage")
    public double slippage = 1;
    @Configurable("Amount")
    public double amount = 0.02;
//    @Configurable("Stop loss in pips")
//    public int stopLossPips = 50;
//    @Configurable("Take profit pips")
//    public int takeProfitPips = 10;
    @Configurable("Short MA type")
    public MaType shortMAType = MaType.SMA;
    @Configurable("Short MA time period")
    public int shortMAPeriod = 5;
    @Configurable("Long MA type")
    public MaType longMAType = MaType.SMA;
    @Configurable("Long MA time period")
    public int longMAPeriod = 30;
    @Configurable("Acceleration")
    public double acceleration = 0.02;
    @Configurable("Maximum")
    public double maximum = 0.2;

    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.history = context.getHistory();
        this.indicators = context.getIndicators();
        this.console = context.getConsole();

        IChart chart = context.getChart(instrument);
        if (chart != null) {
            chart.addIndicator(indicators.getIndicator("MA"), new Object[]{shortMAPeriod, shortMAType.ordinal()});
            chart.addIndicator(indicators.getIndicator("MA"), new Object[]{longMAPeriod, longMAType.ordinal()});
            chart.addIndicator(indicators.getIndicator("SAR"), new Object[]{acceleration, maximum});
        }
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
        // close all orders
        for (IOrder order : engine.getOrders()) {
            engine.getOrder(order.getLabel()).close();
        }
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
        
        if (!instrument.equals(this.instrument)) {
            return;
        }
        
        // close order signals using SAR
        double[] sar = indicators.sar(instrument, this.selectedPeriod, offerSide, acceleration, maximum, Filter.ALL_FLATS, 4, tick.getTime(), 0);
        if (this.order != null) {
            if (order.isLong() && sar[3] < sar[2] && sar[2] > sar[1] && sar[1] > sar[0]) {
                // close long order if uptrend ends
                closeOrder();

            } else if (!order.isLong() && sar[3] > sar[2] && sar[2] < sar[1] && sar[1] < sar[0]) {
                // close short order if downtrend ends
                closeOrder();
            }
        }

        // open order signals with MA
        double[] shortMA = indicators.ma(instrument, this.selectedPeriod, offerSide, appliedPrice, shortMAPeriod, shortMAType, Filter.ALL_FLATS, 2, tick.getTime(), 0);
        double[] longMA = indicators.ma(instrument, this.selectedPeriod, offerSide, appliedPrice, longMAPeriod, longMAType, Filter.ALL_FLATS, 2, tick.getTime(), 0);
        int CURRENT = 0;
        int PREVIOUS = 1;
        
        if (shortMA[PREVIOUS] >= longMA[PREVIOUS] && shortMA[CURRENT] < longMA[CURRENT]) {
            if (this.order != null && order.isLong()) {
                closeOrder();
            }
            if (this.order == null) { // short order
                submitOrder(OrderCommand.SELL);
            }
        } else if (shortMA[PREVIOUS] <= longMA[PREVIOUS] && shortMA[CURRENT] > longMA[CURRENT]) {
            if (this.order != null && !order.isLong()) {
                closeOrder();
            }
            if (this.order == null) { // long order
                submitOrder(OrderCommand.BUY);
            }
        }
    }

    public void onBar(Instrument instrument, Period period, IBar askBar,
            IBar bidBar) throws JFException {

        if (!instrument.equals(this.instrument)
                || !period.equals(this.selectedPeriod)) {
            return;
        }
    }

    private void closeOrder() throws JFException {
        if (this.order.getState() == IOrder.State.FILLED) {
            this.order.close();
            this.order = null;
        }
    }

    private void submitOrder(OrderCommand orderCmd) throws JFException {
        
//        double stopLossPrice,
//        takeProfitPrice;

        // Calculating stop loss and take profit prices 
//        if (orderCmd == OrderCommand.BUY) {
//            stopLossPrice = history.getLastTick(this.instrument).getBid() - getPipPrice(this.stopLossPips);
//            takeProfitPrice = history.getLastTick(this.instrument).getBid() + getPipPrice(this.takeProfitPips);
//        } else {
//            stopLossPrice = history.getLastTick(this.instrument).getAsk() + getPipPrice(this.stopLossPips);
//            takeProfitPrice = history.getLastTick(this.instrument).getAsk() - getPipPrice(this.takeProfitPips);
//        }
        // Submitting an order for the specified instrument at the current 
        //    market price

        order = engine.submitOrder(getLabel(instrument), this.instrument, orderCmd, this.amount, 0, 20, 0, 0);
    }

    protected String getLabel(Instrument instrument) {
        String label = instrument.name();
        label = label + (counter++);
        label = label.toUpperCase();
        return label;
    }

    private double getPipPrice(int pips) {
        return pips * this.instrument.getPipValue();
    }

    private void print(Object... o) {
        for (Object ob : o) {
            console.getOut().print(ob + "  ");
        }
        console.getOut().println();
    }

    private void print(Object o) {
        console.getOut().println(o);
    }

    private void print(double[] arr) {
        print(arrayToString(arr));
    }

    private void print(double[][] arr) {
        print(arrayToString(arr));
    }

    private void printIndicatorInfos(IIndicator ind) {
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfInputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Input " + ind.getInputParameterInfo(i).getName() + " " + ind.getInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOptionalInputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Opt Input " + ind.getOptInputParameterInfo(i).getName() + " " + ind.getOptInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOutputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Output " + ind.getOutputParameterInfo(i).getName() + " " + ind.getOutputParameterInfo(i).getType());
        }
    }

    public static String arrayToString(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += "[" + r + "] " + (new DecimalFormat("#.#######")).format(arr[r]) + "; ";
        }
        return str;
    }

    public static String arrayToString(double[][] arr) {
        String str = "";
        if (arr == null) {
            return "null";
        }
        for (int r = 0; r < arr.length; r++) {
            for (int c = 0; c < arr[r].length; c++) {
                str += "[" + r + "][" + c + "] " + (new DecimalFormat("#.#######")).format(arr[r][c]);
            }
            str += "; ";
        }
        return str;
    }
    // ________________________________
}
