package jforex.requests;

import java.util.*;
import java.text.*;
import com.dukascopy.api.*;

public class Simple2 implements IStrategy {
    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IContext context;
    private IIndicators indicators;

    private int counter = 0;
    private String slabel = "simple";
    private static Random random = new Random();
    double[] lastZig = new double[4];
    double[] lastZig1 = new double[4];

    public double sl = 20; // Loss Limit
    public double tp = 20; // Profit Limit
    public double amount = 1; // Volumen

    // Setting the limits for the zigzag

    public Period period1 = Period.ONE_MIN;
    public int extDepth = 5;
    public int extDeviation = 5;
    public int extBackstep = 3;
    public OfferSide ZZos = OfferSide.BID;

    public int extDepth1 = 14;
    public int extDeviation1 = 5;
    public int extBackstep1 = 3;
    public OfferSide ZZos1 = OfferSide.BID;

    // Setting the limits for the ma

    public double distPriceMA = 200;
    public IIndicators.MaType maType = IIndicators.MaType.SMA;
    Period MAperiodThis = Period.ONE_HOUR;
    public int MAtimePeriod = 200;
    public OfferSide MAos = OfferSide.BID;
    public IIndicators.AppliedPrice MAappliedPrice = IIndicators.AppliedPrice.CLOSE;
    // TS and BE
    public double tsStart = 10.0;
    public double tssl = 10.0;
    public double Breakeven = 8;
    public double breakevengain = 1;
    
       private Set<Instrument> instruments = new HashSet<Instrument>(
                Arrays.asList(new Instrument[] {Instrument.EURUSD, Instrument.NZDCAD, Instrument.EURAUD})
        );  

    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();

        context.setSubscribedInstruments(instruments);
        int i = 5;
        while(!context.getSubscribedInstruments().containsAll(instruments) && i >0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i--;
        }
        
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
        if (instruments.contains(instrument)) {
            checkTrailingStop(instrument, tick);
            CheckBreakeven(tick, slabel, instrument);

            int[] positions = CountOrders2(instrument, slabel);
            if (positions[2] == 0) {
                double pv = instrument.getPipValue();
                double[] ma = indicators.ma(instrument, MAperiodThis, MAos, MAappliedPrice, MAtimePeriod, maType, Filter.WEEKENDS, 2,
                        tick.getTime(), 0);
                if (ma[1] < tick.getAsk() && tick.getAsk() - ma[1] <= distPriceMA * pv && ma[0] < ma[1]) { // Conditional for BUY
                    double[] zig = countZigZag(tick, instrument);
                    if (zig[1] - zig[0] > 0)
                        zig[0] = tick.getAsk();
                    else {
                        zig[3] = zig[2];
                        zig[2] = zig[1];
                        zig[1] = zig[0];
                        zig[0] = tick.getAsk();
                    }
                    double[] zig1 = countZigZag1(tick, instrument);
                    if (zig1[1] - zig1[0] > 0)
                        zig1[0] = tick.getAsk();
                    else {
                        zig1[3] = zig1[2];
                        zig1[2] = zig1[1];
                        zig1[1] = zig1[0];
                        zig1[0] = tick.getAsk();
                    }
                    if ((zig[0] > zig[2]) || (zig1[0] > zig1[2] && zig1[2] > zig1[3] && zig1[1] > zig1[3])) {
                        if ((lastZig[3] != zig[3] && lastZig[2] != zig[2]) || (lastZig1[3] != zig1[3] && lastZig1[2] != zig1[2])) {
                            double takeProfit;
                            if (tp == 0)
                                takeProfit = 0;
                            else
                                takeProfit = tick.getAsk() + pv * tp;
                            double stopLoss;
                            if (sl == 0)
                                stopLoss = 0;
                            else
                                stopLoss = tick.getAsk() - pv * sl;
                            IOrder order = engine.submitOrder(slabel + "Buy" + getLabel(), instrument, IEngine.OrderCommand.BUY,
                                    amount, 0, 5, stopLoss, takeProfit);
                            order.waitForUpdate(2000);
                            lastZig[3] = zig[3];
                            lastZig[2] = zig[2];
                        }

                    }
                } else if (ma[1] > tick.getBid() && ma[1] - tick.getBid() <= distPriceMA * pv && ma[0] > ma[1]) { // Conditional for SELL
                    double[] zig = countZigZag(tick, instrument);
                    if (zig[1] - zig[0] < 0)
                        zig[0] = tick.getBid();
                    else {
                        zig[3] = zig[2];
                        zig[2] = zig[1];
                        zig[1] = zig[0];
                        zig[0] = tick.getBid();
                    }
                    double[] zig1 = countZigZag1(tick, instrument);
                    if (zig1[1] - zig1[0] < 0)
                        zig1[0] = tick.getBid();
                    else {
                        zig1[3] = zig1[2];
                        zig1[2] = zig1[1];
                        zig1[1] = zig1[0];
                        zig1[0] = tick.getBid();
                    }
                    if ((zig[0] < zig[2]) || (zig1[0] < zig1[2] && zig1[2] < zig1[3] && zig1[1] < zig1[3])) {
                        if ((lastZig[3] != zig[3] && lastZig[2] != zig[2]) || (lastZig1[3] != zig1[3] && lastZig1[2] != zig1[2])) {
                            double takeProfit;
                            if (tp == 0)
                                takeProfit = 0;
                            else
                                takeProfit = tick.getBid() - pv * tp;
                            double stopLoss;
                            if (sl == 0)
                                stopLoss = 0;
                            else
                                stopLoss = tick.getBid() + pv * sl;
                            IOrder order = engine.submitOrder(slabel + "Sell" + getLabel(), instrument, IEngine.OrderCommand.SELL,
                                    amount, 0, 5, stopLoss, takeProfit);
                            order.waitForUpdate(2000);
                            lastZig[3] = zig[3];
                            lastZig[2] = zig[2];
                        }

                    }
                }
            }

        }
    }

    double[] countZigZag(ITick tick, Instrument instrument) throws JFException {
        double[] result = new double[4];
        int nrOfBars = Math.max(140, extDepth * 50);
        double[] zig = indicators.zigzag(instrument, Period.ONE_MIN, ZZos, extDepth, extDeviation, extBackstep, Filter.WEEKENDS,
                nrOfBars, tick.getTime(), 0);
        int licz = 0;
        int length = zig.length - 1;
        for (int i = length; i >= 0; i--) {
            if (zig[i] > 0)
                result[licz++] = zig[i];
            if (licz > 3)
                break;
        }
        return result;
    }

    double[] countZigZag1(ITick tick, Instrument instrument) throws JFException {
        double[] result = new double[4];
        int nrOfBars = Math.max(140, extDepth1 * 50);
        double[] zig1 = indicators.zigzag(instrument, period1, ZZos1, extDepth1, extDeviation1, extBackstep1, Filter.WEEKENDS,
                nrOfBars, tick.getTime(), 0);
        int licz = 0;
        int length = zig1.length - 1;
        for (int i = length; i >= 0; i--) {
            if (zig1[i] > 0)
                result[licz++] = zig1[i];
            if (licz > 3)
                break;
        }
        return result;
    }

    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }

    void checkTrailingStop(Instrument instrument, ITick tick) throws JFException {
        if (tsStart != 0) {
            for (IOrder order : engine.getOrders(instrument)) {
                if (order.getLabel().substring(0, slabel.length()).equals(slabel) && order.getState() == IOrder.State.FILLED
                        && order.getProfitLossInPips() >= tsStart) {
                    int precision = instrument.getPipScale() + 1;
                    if (order.getOrderCommand() == IEngine.OrderCommand.BUY) {
                        if (order.getStopLossPrice() < NormalizeDouble(tick.getBid() - tssl * instrument.getPipValue(), precision)) {
                            order.setStopLossPrice(NormalizeDouble(tick.getBid() - tssl * instrument.getPipValue(), precision));
                            // console.getOut().println("Traling Stop for position: "+ order.getLabel());
                        }
                    } else if (order.getOrderCommand() == IEngine.OrderCommand.SELL) {
                        if (order.getStopLossPrice() > NormalizeDouble(tick.getBid() + tssl * instrument.getPipValue(), precision)
                                || order.getStopLossPrice() == 0) {
                            order.setStopLossPrice(NormalizeDouble(tick.getBid() + tssl * instrument.getPipValue(), precision),
                                    OfferSide.BID);
                            // console.getOut().println("Traling Stop for position: "+ order.getLabel());
                        }
                    }
                }
            }
        }
    }

    void CheckBreakeven(ITick tick, String label, Instrument instrument) throws JFException {
        if (Breakeven == 0)
            return;
        for (IOrder order : engine.getOrders(instrument)) {
            if (order.isLong() && order.getLabel().substring(0, label.length()).equals(label)) {
                if (order.getProfitLossInPips() >= this.Breakeven) {
                    if (order.getStopLossPrice() < NormalizeDouble(
                            order.getOpenPrice() + this.breakevengain * instrument.getPipValue(), instrument.getPipScale() + 1)) {
                        order.setStopLossPrice(NormalizeDouble(order.getOpenPrice() + this.breakevengain * instrument.getPipValue(),
                                instrument.getPipScale() + 1));
                        // console.getOut().println("Breakeven for position: "+ order.getLabel());
                    }
                }
            }
            if (!order.isLong() && order.getLabel().substring(0, label.length()).equals(label)) {
                if (order.getProfitLossInPips() >= this.Breakeven) {
                    if (order.getStopLossPrice() > NormalizeDouble(
                            order.getOpenPrice() - this.breakevengain * instrument.getPipValue(), instrument.getPipScale() + 1)
                            || order.getStopLossPrice() == 0) {
                        order.setStopLossPrice(NormalizeDouble(order.getOpenPrice() - this.breakevengain * instrument.getPipValue(),
                                instrument.getPipScale() + 1));
                        // console.getOut().println("Breakeven for position: "+ order.getLabel());
                    }
                }
            }
        }
    }

    int[] CountOrders2(Instrument instrument, String label) throws JFException {
        int[] counter1 = new int[3]; // 0-BUY 1-SELL 2-BUYSTOP_BYBID 3-SELLSTOP
        for (IOrder order : engine.getOrders(instrument)) {
            if (order.getLabel().substring(0, label.length()).equals(label)) {
                if (order.getOrderCommand() == IEngine.OrderCommand.BUY) {
                    counter1[0]++;
                    counter1[2]++;
                } else if (order.getOrderCommand() == IEngine.OrderCommand.SELL) {
                    counter1[1]++;
                    counter1[2]++;
                }
            }
        }
        return counter1;
    }

    // Get a Unique Label for each position
    protected String getLabel() {
        String label = "" + (random.nextInt(89) + 10);
        label = label + (counter++);
        label = label.toUpperCase();
        return label;
    }

    void print(String what) {
        console.getOut().println(what);
    }

    double NormalizeDouble(double operand, int decimal) {
        String model = "0.0";
        for (int i = 1; i < decimal; i++) {
            if (i == decimal - 1)
                model += "#";
            else
                model += "0";
        }
        DecimalFormat applydeci = new DecimalFormat(model);
        Double result = new Double(applydeci.format(operand)).doubleValue();
        return (result);
    }
}