package jforex;

import java.awt.Color;

import java.util.*;
import java.io.*;
import javax.sound.sampled.*;
import com.dukascopy.api.*;
import java.math.BigDecimal;
import com.dukascopy.api.drawings.IChartObjectFactory;
import com.dukascopy.api.drawings.IHorizontalLineChartObject;
import javax.swing.colorchooser.ColorChooserComponentFactory;

@RequiresFullAccess
public class PriceBreakAlert implements IStrategy {
    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IContext context;
    private IChart chart;
    
    IHorizontalLineChartObject hLineUp;
    IHorizontalLineChartObject hLineDown;
    private boolean afterOnBar;
    private boolean active;
        
    @Configurable("Instrument: ") public Instrument myInstrument = Instrument.EURUSD;
    @Configurable("Period: ") public Period myPeriod = Period.THIRTY_MINS;
    //upper alarm line on chart
    @Configurable("Upper alarm line ") public boolean upperLine = true;
    //@Configurable("Color of upper line ") public Color colorUpperLine = Color.blue;
    @Configurable(value="Price to break to up:", stepSize=0.00001) public double PriceToBreakUp  = 1.3;
    //lower alarm line on chart
    @Configurable("Lower alarm line ") public boolean lowerLine = true;
    //@Configurable("Color of lower line ") public Color colorLowerLine = Color.magenta;
    @Configurable(value="Price to break to down:", stepSize=0.00001) public double PriceToBreakDown  = 1.2;
    @Configurable(value = "Audio Alert File (wav): ", fileType="") public File AudioAlertFile;
    @Configurable(value="Number of Loops:") public int numLoops = 1;
    @Configurable("Alarm on Bar Close:") public boolean barClose = true;
    
    public void onStart(IContext context) throws JFException {
        engine = context.getEngine();
        console = context.getConsole();
        history = context.getHistory();
        chart = context.getChart(myInstrument);
        context = context;
        
        if(chart == null){
            console.getErr().println("No chart opened for " + myInstrument);
            context.stop(); //stop the strategy
        }
        
        IChartObjectFactory factory = chart.getChartObjectFactory();
        //if we chosen upper line
        if(upperLine==true){
            hLineUp = factory.createHorizontalLine("hLineUp", PriceToBreakUp);
            hLineUp.setColor(Color.cyan);
            hLineUp.setText("Alarm powyżej "+PriceToBreakUp);
            chart.addToMainChart(hLineUp); 
        }
        //if we chosen lower line
        if(lowerLine==true){
            hLineDown = factory.createHorizontalLine("hLineDown", PriceToBreakDown);
            hLineDown.setColor(Color.magenta);
            hLineDown.setText("Alarm poniżej "+PriceToBreakDown);
            chart.addToMainChart(hLineDown);
        }
                
        afterOnBar = false;
        active = false;
        
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
        if(upperLine==true){
            chart.remove(hLineUp);
        }
        if(lowerLine==true){
            chart.remove(hLineDown);
        }        
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
        if(upperLine==true && PriceToBreakUp!=hLineUp.getPrice(0)){
            PriceToBreakUp=hLineUp.getPrice(0);
            hLineUp.setText("Alarm powyżej "+Round(PriceToBreakUp, 5));
        }
        if(lowerLine==true && PriceToBreakDown!=hLineDown.getPrice(0)){
            PriceToBreakDown=hLineDown.getPrice(0);
            hLineDown.setText("Alarm poniżej "+Round(PriceToBreakDown, 5));
        }
        if(instrument.equals(myInstrument)){
            if(barClose==false || afterOnBar==true){
                if(upperLine==true && tick.getBid() >= PriceToBreakUp){
                    console.getOut().println("Price break to up price: "+Round(PriceToBreakUp, 5));
                    if(AudioAlertFile!=null && AudioAlertFile.exists()){
                        playSound(AudioAlertFile, numLoops);
                        active = true;
                    }else console.getOut().println("Problem with audio file");
                }
                if(lowerLine && tick.getBid() <= PriceToBreakDown){
                    console.getOut().println("Price break to down price: "+Round(PriceToBreakDown, 5));
                    if(AudioAlertFile!=null && AudioAlertFile.exists()){
                        playSound(AudioAlertFile, numLoops);
                        active = true;
                    }else console.getOut().println("Problem with audio file");
                }
            }
            if(afterOnBar==true && active==true){
                if(AudioAlertFile!=null && AudioAlertFile.exists()){
                        playSound(AudioAlertFile, numLoops);
                }else console.getOut().println("Problem with audio file");
            }
        }
    }
    
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
        if(instrument.equals(myInstrument) && period.equals(myPeriod))
        {
            IBar bar = history.getBar(myInstrument, myPeriod, OfferSide.BID, 1);
            if(upperLine==true && bar.getClose() >= PriceToBreakUp){
                afterOnBar = true;
            }
            if(lowerLine && bar.getClose() <= PriceToBreakDown){
                afterOnBar = true;
            }
            
        }
    }
    
    public class WaitForEndOfSoundThread  implements Runnable {
        Clip clip;
        public WaitForEndOfSoundThread(Clip clipp){
            clip=clipp;
        }
        public void run() {    
            while(true){
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(!clip.isRunning()){
                    clip.stop();
                    clip.close();
                    break;
                }
            }
        }
    }
    private void playSound(File wavFile, int loops) throws JFException {
        try {
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(wavFile);
            AudioFormat af = audioInputStream.getFormat();
            int nSize = (int) (af.getFrameSize() * audioInputStream.getFrameLength());
            byte[] audio = new byte[nSize];
            DataLine.Info info = new DataLine.Info(Clip.class, af, nSize);
            audioInputStream.read(audio, 0, nSize);
            for(int i=0; i<loops; i++) {
                Clip clip = (Clip) AudioSystem.getLine(info);
                clip.open(af, audio, 0, nSize);
                clip.start();
                Thread waifForEnd = new Thread(new WaitForEndOfSoundThread(clip));
                waifForEnd.start();
            }
        } catch (Exception e) {
            console.getOut().println(e.getMessage());
            e.printStackTrace();
        }
    }
    
    private static double Round(double amount, int decimalPlaces){
        return (new BigDecimal(amount)).setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}