package jforex.strategies.renko;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.io.File;
import java.text.ParseException;



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.feed.FeedDescriptor;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.feed.IRenkoBar;
import com.dukascopy.api.feed.IRenkoBarFeedListener;
import com.dukascopy.api.indicators.IIndicator;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RSMAtest implements IStrategy, IRenkoBarFeedListener {

    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IIndicators indicators;
    private int counter = 0;
    private IFeedDescriptor feedDescriptor;
    private IOrder order;
    
    
    public File jfxFile = new File("/Users/tinfx/JForex/Strategies/SMIOverRSIIndicator.jfx");

    @Configurable("Brick size (pips)")
    public int brickSize = 2;
    @Configurable("SMI MA period")
    public int smaoMaPeriod = 14;
    @Configurable("SMAo MA type")
    public MaType smaoMaType = MaType.SMA;
    @Configurable("RSI MA period")
    public int rsiPeriod = 14;
    @Configurable("Instrument")
    public Instrument selectedInstr = Instrument.EURUSD;
    @Configurable("Offer side")
    public OfferSide offerSide = OfferSide.BID;
    @Configurable("Slippage")
    public double slippage = 2;
    @Configurable("Amount")
    public double amount = 0.1;
    @Configurable("Take profit pips")
    public int takeProfitPips = 0;
    @Configurable("Stop loss in pips")
    public int stopLossPips = 0 ;
    
    private IAccount account;
    private String indName = "SMA_RSI";

    @Override
    public void onStart(IContext context) throws JFException {
        this.console = context.getConsole();
        this.indicators = context.getIndicators();
        this.history = context.getHistory();
        this.engine = context.getEngine();
        
        // Register custom indicator SMA_RSI
        
        indicators.registerCustomIndicator(jfxFile);
        indicator = context.getIndicators().getIndicator(indName);

        IChart chart = context.getChart(selectedInstr);
        if (chart != null) {
            chart.addIndicator(indicators.getIndicator("SMA_RSI"), new Object[]{smaoMaPeriod, smaoMaType.ordinal()});

        }

        // renko bar feed

        PriceRange priceRange = PriceRange.valueOf(brickSize);

        feedDescriptor = new FeedDescriptor();
        feedDescriptor.setDataType(DataType.RENKO);
        feedDescriptor.setOfferSide(offerSide);
        feedDescriptor.setInstrument(selectedInstr);
        feedDescriptor.setPriceRange(PriceRange.valueOf(brickSize));

        context.subscribeToRenkoBarFeed(selectedInstr, offerSide, priceRange, this);
    }

    @Override
    public void onTick(Instrument instrument, ITick tick) throws JFException {
        if (instrument != this.selectedInstr) {
            return;
        }
    }

    @Override
    public void onBar(Instrument instrument, OfferSide offerSide, PriceRange brickSize, IRenkoBar bar) {
        try {
            if (!isActive(order)) {
                order = null;
            }

            // calculate indicator
            



            double[] smaoMAFeed = (double[]) indicators.calculateIndicator(
                    feedDescriptor,
                    new OfferSide[]{OfferSide.BID},
                    "SMA_RSI",
                    new AppliedPrice[]{AppliedPrice.CLOSE},
                    new Object[]{smaoMaPeriod, smaoMaType.ordinal()},
                    2, bar.getTime(), 0)[0];

            // PLACE ORDERS    
            if (smaoMAFeed[1] >= 50.00 && smaoMAFeed[0] < 50.00) {
                // long if fast ma crosses slow ma from below            
                if (order == null || !order.isLong()) {
                    closeOrder(order);
                    order = submitOrder(OrderCommand.BUY);
                }

            } else if (smaoMAFeed[1] < 50.00 && smaoMAFeed[0] >= 50.00) {
                // short if fast ma crosses slow ma from top            
                if (order == null || order.isLong()) {
                    closeOrder(order);
                    order = submitOrder(OrderCommand.SELL);
                }
            }

        } catch (JFException ex) {
            Logger.getLogger (RSMAtest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    

    
    private IOrder submitOrder(OrderCommand orderCmd) throws JFException {

        double stopLossPrice = 0.0, takeProfitPrice = 0.0;

        // Calculating order price, stop loss and take profit prices
        if (orderCmd == OrderCommand.BUY) {
            
            if (stopLossPips > 0) {
                stopLossPrice = history.getLastTick(selectedInstr).getBid() - getPipPrice(stopLossPips);
            }
            if (takeProfitPips > 0) {
                takeProfitPrice = history.getLastTick(selectedInstr).getBid() + getPipPrice(takeProfitPips);
            }
        } else {
            if (stopLossPips > 0) {
                stopLossPrice = history.getLastTick(selectedInstr).getAsk() + getPipPrice(stopLossPips);
            }
            if (takeProfitPips > 0) {
                takeProfitPrice = history.getLastTick(selectedInstr).getAsk() - getPipPrice(takeProfitPips);
            }
        }

        return engine.submitOrder(getLabel(selectedInstr), selectedInstr, orderCmd, amount, 0, slippage, stopLossPrice, takeProfitPrice);
    }

    private void closeOrder(IOrder order) throws JFException {
        if (order != null
                && (order.getState() == IOrder.State.OPENED || order.getState() == IOrder.State.FILLED)) {
            order.close();
        }
    }

    private boolean isActive(IOrder order) throws JFException {
        if (order != null
                && (order.getState() == IOrder.State.CREATED || order.getState() == IOrder.State.OPENED || order.getState() == IOrder.State.FILLED)) {
            return true;
        }
        return false;
    }

    private double getPipPrice(int pips) {
        return pips * this.selectedInstr.getPipValue();
    }

    private String getLabel(Instrument instrument) {
        String label = instrument.name();
        label = label + (counter++);
        label = label.toUpperCase();
        return label;
    }

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

    @Override
    public void onMessage(IMessage message) throws JFException {
        print(message);
    }

    @Override
    public void onAccount(IAccount account) throws JFException {
    }

    @Override
    public void onStop() throws JFException {
    }

    /**************** debug print functions ***********************/
    private void print(Object... o) {
        for (Object ob : o) {
            //console.getOut().print(ob + "  ");
            if (ob instanceof double[]) {
                print((double[]) ob);
            } else if (ob instanceof double[]) {
                print((double[][]) ob);
            } else if (ob instanceof Long) {
                print2(toStr((Long) ob));
            } else {
                print2(ob);
            }
            print2(" ");
        }
        console.getOut().println();
    }

    private void print(Object o) {
        console.getOut().println(o);
    }

    private void print2(Object o) {
        console.getOut().print(o);
    }

    private void print(double[] arr) {
        print(toStr(arr));
    }

    private void print(double[][] arr) {
        print(toStr(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());
        }
        console.getOut().println();
    }

    public static String toStr(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += "[" + r + "] " + (new DecimalFormat("#.#######")).format(arr[r]) + "; ";
        }
        return str;
    }

    public static String toStr(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;
    }

    public String toStr(double d) {
        return (new DecimalFormat("#.#######")).format(d);
    }

    public String toStr(Long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {

            {
                setTimeZone(TimeZone.getTimeZone("GMT"));
            }
        };
        return sdf.format(time);
    }

    private void printTime(Long time) {
        console.getOut().println(toStr(time));
    }

    private void print(IBar bar) {

        console.getOut().println(toStr(bar.getTime()) + " O:" + bar.getOpen() + " C:" + bar.getClose() + " H:" + bar.getHigh() + " L:" + bar.getLow());
    }
}