package singlejartest;

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

import java.util.logging.Level;
import java.util.logging.Logger;

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 com.dukascopy.api.feed.FeedDescriptor;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.feed.IRenkoBar;
import com.dukascopy.api.feed.IRenkoBarFeedListener;



public class RSMAtest implements IStrategy, IRenkoBarFeedListener {
	
    private IEngine engine;
    private IHistory history;
    private IConsole console;  
    private IIndicators indicators;
    private IIndicator indicator;
    private IChart chart;
    private IOrder order;
    private IFeedDescriptor feedDescriptor;
    
	
    private int counter = 0;    
    public File jfxFile = new File("/Users/tinfx/JForex/Indicators/MAOverRSI/SMAOverRSIIndicator.jfx");
	
	//    @Configurable("Brick size (pips)")
    public int brickSize = 1;
	//    @Configurable("SMI MA period")
    public int smaoMaPeriod = 5;
	//    @Configurable("SMAo MA type")
    public MaType smaoMaType = MaType.SMA;
	//    @Configurable("RSI MA period")
    public int rsiPeriod = 5;
	//    @Configurable("Instrument")
    public Instrument selectedInstr = Instrument.EURUSD;
	//    @Configurable("Offer side")
    public OfferSide offerSide = OfferSide.BID;
	//    @Configurable("Slippage")
    public double slippage = 3;
	//    @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 SimpleDateFormat gmtSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public String indName = "SMA_RSI";
	
    private Object[] optInputs;
	
	
	
    @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);
        
        optInputs = new Object[] { rsiPeriod,smaoMaPeriod };
		
		
		//        IChart chart = context.getChart(selectedInstr);
		//        if (chart != null) {
		//            chart.addIndicator(indicators.getIndicator("SMA_RSI"),optInputs);
		
		//        }
		
        // 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
            
			
			
			
            Object[] smaoMAFeed =   indicators.calculateIndicator(
																  feedDescriptor,
																  new OfferSide[]{OfferSide.BID},
																  "SMA_RSI",
																  new AppliedPrice[]{AppliedPrice.CLOSE},
																  optInputs,
																  2, bar.getTime(), 
																  0);
            
            int last = smaoMAFeed.length - 1; 
			//           long currBarTime = bar.getTime();
            
            double [] r = (double[]) smaoMAFeed[0];
            double [] s = (double[]) smaoMAFeed[1];
			
			// Print indicator outputs            
			print(String.format("%s r: last - %.5f, previous - %.5f; s: last - %.5f, previous = %.5f",
								gmtSdf.format(bar.getTime()),r[last],r[last-1], s[last], s[last-1]));
			
			
			
			
            // PLACE ORDERS    
            if (s[last] >= 50.00 && s[last-1] < 50.00) {
                // long if fast ma crosses slow ma from below            
                if (order == null || !order.isLong()) {
                    closeOrder(order);
                    order = submitOrder(OrderCommand.BUY);
                }
				
            } else if (s[last] < 50.00 && s[last-1] >= 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());
    }
}