/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;

import tat.chart.BaseCanvas;
import tat.chart.ChartPane;
import tat.chart.IndicatorType;
import java.util.Calendar;
import java.util.Iterator;
import java.util.concurrent.ConcurrentSkipListSet;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Window;
import tat.chart.ChartShape;
import tat.chart.MainChartCanvas;
import tat.chart.OBVCanvas;
import tat.chart.RSDCanvas;
import tat.chart.RSDYCanvas;
import tat.chart.RSMCanvas;
import tat.chart.RSMYCanvas;
import tat.chart.VolumeCanvas;
import tat.data.ChartType;
import tat.data.Market;
import tat.data.OHLC;
import tat.indicator.SimpleMovingAverage;

/**
 * This class is the ChartCanvasPane inside ChartTabPane
 * Each ChartCanvasPane contains a OHLC data
 * Each ChartCanvasPane have the ability to display multiple indicators
 * @author Thomas Tang
 */
public class ChartCanvasPane extends VBox {
    private TatMain application;
    private CommandPane commandPane;
    public SplitPane sp = new SplitPane();
    private ObservableList<ChartPane> chartPaneList = FXCollections.observableArrayList();
    public BooleanProperty showGridProperty = new SimpleBooleanProperty(false);
    //Volume will be a normal indicator chart type and it will no longer be openned by default
    //public BooleanProperty showVolumeProperty = new SimpleBooleanProperty(false);
    public BooleanProperty showMouseHoverIndicator = new SimpleBooleanProperty(false);
    public ObjectProperty<Background> ChartPaneBackgroundProperty = new SimpleObjectProperty<Background>();
    public ObjectProperty<Color> GridRectColorProperty = new SimpleObjectProperty<Color>(Color.web("#03193d")); //BaseTimeCanvas and BaseYCanvas use the same color
    public ObjectProperty<Color> CrossIndicatorLineColorProperty = new SimpleObjectProperty<Color>(Color.web("#f9f911"));
    public ObjectProperty<Color> ChartTickColorProperty = new SimpleObjectProperty<Color>(Color.web("#afe0ff")); //Tick Text use the same color
    public ObjectProperty<Color> YearTickColorProperty = new SimpleObjectProperty<Color>(Color.web("#4bf211"));  //Year Tick Text use this color
    public ObjectProperty<Color> UpBarColorProperty = new SimpleObjectProperty<Color>(Color.GREEN);
    public ObjectProperty<Color> DownBarColorProperty = new SimpleObjectProperty<Color>(Color.RED);
    public ObjectProperty<Color> OutsideBarColorProperty = new SimpleObjectProperty<Color>(Color.BLUE);
    public ObjectProperty<Color> LineChartColorProperty = new SimpleObjectProperty<Color>(Color.web("#d7eef7"));
    public DoubleProperty xIntervalProperty = new SimpleDoubleProperty(8);
    public DoubleProperty LineChartWidthProperty = new SimpleDoubleProperty(1);
    public DoubleProperty GannSwingChartWidthProperty = new SimpleDoubleProperty(1);
    public ObjectProperty<Color> GannSwingChartUpColorProperty = new SimpleObjectProperty<Color>(Color.web("#46f409"));
    public ObjectProperty<Color> GannSwingChartDownColorProperty = new SimpleObjectProperty<Color>(Color.web("#f60c0c"));
    /**
     * These are the new properties that only belongs to this extended sub-class
     */
    public IntegerProperty ChartShapeProperty = new SimpleIntegerProperty(ChartShape.BAR.ordinal());
    public IntegerProperty ChartTypeProperty = new SimpleIntegerProperty(ChartType.DAY.ordinal());
    public DoubleProperty indicatorCanvasHeightProperty = new SimpleDoubleProperty(400);
    
    private double chartAreaHeight = 0;
    
    // Volume will be a normal indicator type and it no longer be openned by default
    /**
     * volume divider initial pos property
     */
    //public DoubleProperty volumeDividerPositionProperty = new SimpleDoubleProperty(0.7);
    
    /**
     * When this canvas pane is selected, this property means what is the current date
     * on mouse on hover
     */
    public ObjectProperty<Calendar> currentCalendar = new SimpleObjectProperty<Calendar>();
    public DoubleProperty openProperty = new SimpleDoubleProperty(0);
    public DoubleProperty highProperty = new SimpleDoubleProperty(0);
    public DoubleProperty lowProperty = new SimpleDoubleProperty(0);
    public DoubleProperty closeProperty = new SimpleDoubleProperty(0);
    public DoubleProperty volumeProperty = new SimpleDoubleProperty(0);
    
    /**
     * Indicator drawing properties
     */
    public BooleanProperty DrawSMAProperty = new SimpleBooleanProperty(false);
    
    
    /**
     * Helper attributes to control the volume canvas show or hide
     */
    private double volumeDividerInitialPosition = 0;
    private Timeline animation;
    
    /**
     * some of the identity attributes. Make them public
     */
    public String countryName;
    public String category;
    public String marketCode;
    public String code;
    /**
     * This property indicates is this chart canvas pane is the current showing one
     * We will caching the hiding canvas to avoid memory is consumed too much in terms
     * of slowing down the computer or system
     */ 
    public BooleanProperty isChartCached = new SimpleBooleanProperty(true);
    
    private Calendar startDate = Calendar.getInstance();
    private Calendar endDate = Calendar.getInstance();
    private double savedParentWidth;
    
    /**
     * Constructor
     * @param application - to refer the main application
     * @param title - set title for this tab
     */
    public ChartCanvasPane (TatMain application) {        
        this.application = application;
        commandPane = new CommandPane(this);
        sp.setOrientation(Orientation.VERTICAL);
        sp.getItems().add(commandPane);
        this.getChildren().addAll(sp);
        VBox.setVgrow(sp, Priority.ALWAYS);
        ChartTypeProperty.bindBidirectional(commandPane.ChartTypeProperty);
        ChartShapeProperty.bind(commandPane.ChartShapeProperty);
        
//Volume will be a normal indicator chart type and it will no longer be openned by default
//        showVolumeProperty.bindBidirectional(commandPane.showVolumeProperty);
//        showVolumeProperty.addListener(evt -> {
//            if (showVolumeProperty.getValue() == true){
//                goUp();
//            }else {
//                goDown();
//            }
//        });
        
//        volumeDividerPositionProperty.addListener(new ChangeListener<Number>() {
//            @Override
//            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
//                if (newValue.doubleValue() == 1 || newValue.doubleValue() == volumeDividerInitialPosition){
//                    animation.pause();
//                }
//            }  
//        });
    }
    /**
     * Open an OHLC chart in the main chart area
     * @param bIndex - if this chart is an Index chart
     * @param countryName - the market country name
     * @param category - the category of this OHLC
     * @param marketCode - the market code of this OHLC
     * @param code - the code of this OHLC
     * @param startDate - the StartDate
     * @param endDate - the EndDate
     * @param type - the {@link ChartType}
     * @param allTimeHigh - the All Time High price
     * @param allTimeLow  - the All Time Low price
     * @param parentWidth - the parent pane's width
     * @param chartAreaHeight - the charting area height
     * @return BaseCanvas created by this function
     */
    public BaseCanvas openMainChart(
                               boolean bIndex,
                               String countryName,
                               String category,
                               String marketCode,
                               String code,
                               ChartType type,
                               Calendar startDate,
                               Calendar endDate,
                               double allTimeHigh,
                               double allTimeLow,
                               double parentWidth,
                               double chartAreaHeight) {
        this.countryName = countryName;
        this.category = category;
        this.marketCode = marketCode;
        this.code = code;
        this.startDate.setTime(startDate.getTime());
        this.endDate.setTime(endDate.getTime());
        this.savedParentWidth = parentWidth;
        ChartTypeProperty.setValue(type.ordinal());
        ChartPane chartPane = new ChartPane(
                bIndex,
                countryName,
                category, 
                marketCode,
                code,
                startDate,
                endDate,
                allTimeHigh,
                allTimeLow,
                parentWidth,
                chartAreaHeight);
        chartPane.setPrefHeight(sp.heightProperty().getValue());
        chartPane.setMinHeight(300);
        chartPane.setMaxHeight(Control.USE_COMPUTED_SIZE);
        chartPane.setMinWidth(Control.USE_PREF_SIZE);
        chartPaneList.add(chartPane);
        sp.getItems().add(chartPane);
        BindProperties(chartPane);
        return chartPane.getBaseCanvas();
    }
    
    /**
     * Open an indicator chart for a OHLC
     * @param bIndex - If this chart is an index chart or not
     * @param countryName - the market country name
     * @param category - the category of this OHLC
     * @param marketCode - the market code of this OHLC
     * @param code - the code of this OHLC
     * @param startDate - the StartDate
     * @param endDate - the EndDate
     * @param type - the {@link ChartType}
     * @param indicatorType - the {@link IndicatorType}
     * @param allTimeHigh - the all time high value for this indicator if required
     * @param allTimeLow  - the All Time Low price
     * @param parentWidth - the parent pane width
     * @param chartAreaHeight - the charting area height
     * @return BaseCanvas - created by this function
     */
    public BaseCanvas openIndicatorChart(
                                   boolean bIndex,
                                   String countryName,
                                   String category,
                                   String marketCode,
                                   String code,
                                   Calendar startDate,
                                   Calendar endDate,
                                   IndicatorType indicatorType,
                                   double allTimeHigh,
                                   double allTimeLow,
                                   double parentWidth,
                                   double chartAreaHeight) {
        this.countryName = countryName;
        this.category = category;
        this.marketCode = marketCode;
        this.code = code;
        this.startDate.setTime(startDate.getTime());
        this.endDate.setTime(endDate.getTime());
        this.savedParentWidth = parentWidth;
        this.chartAreaHeight = chartAreaHeight;
        Iterator<ChartPane> itt = chartPaneList.iterator();
        ChartPane parentChartPane = null;
        if (itt.hasNext()) {
            parentChartPane = itt.next();
        }
        ChartPane chartPane = new ChartPane(
                bIndex,
                countryName,
                category,
                marketCode,
                code,
                startDate,
                endDate,
                indicatorType,
                allTimeHigh,
                allTimeLow,
                parentWidth,
                chartAreaHeight,
                parentChartPane);
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane thisChartPane = (ChartPane)it.next();
            if (thisChartPane.isMainChart() == false) {
                //Rebind the timeline position and only shows the last indciator's timeline
                thisChartPane.showTimeLineProperty.setValue(false);
                thisChartPane.getTimeCanvas().heightProperty().bind(new SimpleDoubleProperty(0));
            }
        }
        chartPaneList.add(chartPane);
        sp.getItems().add(chartPane);

        CalculateNewIndicatorCanvasHeight();     
        chartPane.indicatorCanvasHeightProperty.bindBidirectional(indicatorCanvasHeightProperty);
        
        //Fixed indicator chart size and this is designed like this for now
        //Ideally, the indicator chart height should be scrollable
        chartPane.maxHeightProperty().bind(indicatorCanvasHeightProperty);
        chartPane.prefHeightProperty().bind(indicatorCanvasHeightProperty);
        chartPane.minHeightProperty().bind(indicatorCanvasHeightProperty);
        
        //Get the last ChartPane and only show it's timeline
        chartPane.showTimeLineProperty.setValue(true);
        chartPane.BindPropertiesForIndicatorCanvas();
        BindProperties(chartPane);
        
//        if (indicatorType == IndicatorType.VOLUME){
//               double n = sp.getDividers().get(1).positionProperty().getValue();
//               volumeDividerInitialPosition = n;
//               animation = new Timeline(new KeyFrame(new Duration(0.0), new KeyValue(this.volumeDividerPositionProperty, n)),
//               new KeyFrame(new Duration(700.0), new KeyValue(this.volumeDividerPositionProperty, 1, Interpolator.LINEAR)));
//               animation.setAutoReverse(true);
//               animation.setCycleCount(Animation.INDEFINITE);
//               sp.getDividers().get(1).positionProperty().bindBidirectional(this.volumeDividerPositionProperty);
//               goDown();
//        }
        
        return chartPane.getBaseCanvas();
    }
    
    /**
     * Close the Indicator Chart. This is the opposite method of the previous
     * {@link openIndicatorChart}. Remember, it only close the indicator chart
     * which has its own drawing canvas e.g. Volume, OBV, MACD etc
     * @param type Indicator type
     */
    public void closeIndicatorChart(IndicatorType type){
        if (hasIndicatorPane(type)){
            Iterator<ChartPane> it = chartPaneList.iterator();
            while (it.hasNext()){
                ChartPane chartPane = it.next(); 
                BaseCanvas baseCanvas = chartPane.getBaseCanvas();
                if (type == IndicatorType.VOLUME){
                    if ( baseCanvas instanceof VolumeCanvas){
                        //Remove it from internal data structure to save ChartPane
                        chartPaneList.remove(chartPane);
                        //Remove it from the scrollPane
                        sp.getItems().remove(chartPane);
                        //Once we close it, stop iterating
                        break;
                    }  
                }
                else if (type == IndicatorType.OBV) {
                    if ( baseCanvas instanceof OBVCanvas){
                        chartPaneList.remove(chartPane);
                        sp.getItems().remove(chartPane);
                        //Once we close it, stop iterating
                        break;
                    }
                }
                else if (type == IndicatorType.RSD) {
                    if ( baseCanvas instanceof RSDCanvas){
                        chartPaneList.remove(chartPane);
                        sp.getItems().remove(chartPane);
                        //Once we close it, stop iterating
                        break;
                    }                    
                }
                else if (type == IndicatorType.RSM) {
                    if ( baseCanvas instanceof RSMCanvas){
                        chartPaneList.remove(chartPane);
                        sp.getItems().remove(chartPane);
                        //Once we close it, stop iterating
                        break;
                    }                    
                }
            }
            //When we close one indicator chart, we should make sure the last remaining indciator chart still shows the time line
            ChartPane chartPane = chartPaneList.get(chartPaneList.size()-1);
            if (chartPane.isMainChart()== false) {
                chartPane.showTimeLineProperty.setValue(true);
                chartPane.getTimeCanvas().heightProperty().bind(new SimpleDoubleProperty(30));
            }
        }
    }
    
    public void CalculateNewIndicatorCanvasHeight(){
        //This binding will make sure the main chart height is always roughly twice as indicator's height
        IntegerBinding size = (IntegerBinding) Bindings.size(chartPaneList).add(new SimpleIntegerProperty(2));
        NumberBinding sub = Bindings.divide(chartAreaHeight, size);
        indicatorCanvasHeightProperty.setValue(sub.getValue());
    } 
    
    public void BindProperties(ChartPane chartPane) {
        chartPane.showMouseHoverIndicator.bind(showMouseHoverIndicator);
        chartPane.showGridProperty.bind(showGridProperty);
        chartPane.backgroundProperty().bind(ChartPaneBackgroundProperty);
        chartPane.GridRectColorProperty.bind(GridRectColorProperty);
        chartPane.CrossIndicatorLineColorProperty.bind(CrossIndicatorLineColorProperty);
        chartPane.ChartTickColorProperty.bind(ChartTickColorProperty);
        chartPane.YearTickColorProperty.bind(YearTickColorProperty);
        chartPane.UpBarColorProperty.bind(UpBarColorProperty);
        chartPane.DownBarColorProperty.bind(DownBarColorProperty);
        chartPane.OutsideBarColorProperty.bind(OutsideBarColorProperty);
        chartPane.xIntervalProperty.bindBidirectional(xIntervalProperty);
        chartPane.isChartCached.bind(isChartCached);
        chartPane.ChartShapeProperty.bind(ChartShapeProperty);
        chartPane.ChartTypeProperty.bind(ChartTypeProperty);
        chartPane.LineChartColorProperty.bind(LineChartColorProperty);
        chartPane.LineChartWidthProperty.bind(LineChartWidthProperty);
        chartPane.GannSwingChartWidthProperty.bind(GannSwingChartWidthProperty);
        chartPane.GannSwingChartUpColorProperty.bind(GannSwingChartUpColorProperty);
        chartPane.GannSwingChartDownColorProperty.bind(GannSwingChartDownColorProperty);
        currentCalendar.bind(chartPane.getBaseCanvas().currentCalendar);
        openProperty.bind(chartPane.getBaseCanvas().openProperty);
        highProperty.bind(chartPane.getBaseCanvas().highProperty);
        lowProperty.bind(chartPane.getBaseCanvas().lowProperty);
        closeProperty.bind(chartPane.getBaseCanvas().closeProperty);
        volumeProperty.bind(chartPane.getBaseCanvas().volumeProperty);
    }
    
    /**
     * Return all BaseCanvas of this ChartCanvasPane
     * it might include main chart and a few indicator
     * canvases
     * @return  ObservableList contains {@link BaseCanvas}
     */
    public ObservableList<BaseCanvas> getBaseCanvasList() {
        ObservableList<BaseCanvas> baseCanvasList = FXCollections.observableArrayList();
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            baseCanvasList.add(chartPane.getBaseCanvas());
        }
        return baseCanvasList;
    }
    /**
     * Get the {@link MainChartCanvas} of this object
     * @return {@link MainChartCanvas}
     */
    public MainChartCanvas getMainChartCanvas() {
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            if (chartPane.getBaseCanvas() instanceof MainChartCanvas){
                return (MainChartCanvas)chartPane.getBaseCanvas();
            }
        } 
        return null;
    }
    
    /**
     * Get the {@link VolumeCanvas} of this object
     * @return {@link VolumeCanvas}
     */
    public VolumeCanvas getVolumeCanvas() {
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            if (chartPane.getBaseCanvas() instanceof VolumeCanvas){
                return (VolumeCanvas)chartPane.getBaseCanvas();
            }
        } 
        return null;
    }
    
    /**
     * Get the {@link OBVCanvas} of this object
     * @return {@link OBVCanvas} or null if it is not found
     */
    public OBVCanvas getOBVCanvas() {
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            if (chartPane.getBaseCanvas() instanceof OBVCanvas){
                return (OBVCanvas)chartPane.getBaseCanvas();
            }
        } 
        return null;       
    }
    
    /**
     * Get {@link RSDCanvas} of this object
     * @return {@link RSDCanvas} or null if it is not found
     */
    public RSDCanvas getRSDCanvas() {
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            if (chartPane.getBaseCanvas() instanceof RSDCanvas){
                return (RSDCanvas)chartPane.getBaseCanvas();
            }
        } 
        return null;        
    }
    
    /**
     * Get {@link RSMCanvas} of this object
     * @return {@link RSMCanvas} or null if it is not found
     */
    public RSMCanvas getRSMCanvas() {
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()) {
            ChartPane chartPane = it.next();
            if (chartPane.getBaseCanvas() instanceof RSMCanvas){
                return (RSMCanvas)chartPane.getBaseCanvas();
            }
        } 
        return null;        
    }
    
    /**
     * Check if this ChartCanvasPane already has the same type of indicator pane or not
     * @param type
     * @return true - yes it has false otherwise
     */
    public boolean hasIndicatorPane(IndicatorType type){
        Iterator<ChartPane> it = chartPaneList.iterator();
        while (it.hasNext()){
            BaseCanvas baseCanvas = it.next().getBaseCanvas();
            if (type == IndicatorType.VOLUME){
                if ( baseCanvas instanceof VolumeCanvas){
                    return true;
                }  
            }
            
            if (type == IndicatorType.OBV){
                if ( baseCanvas instanceof OBVCanvas){
                    return true;
                }  
            }
            
            if (type == IndicatorType.RSD){
                if ( baseCanvas instanceof RSDCanvas) {
                    return true;
                }
            }
            
            if (type == IndicatorType.RSM) {
                if ( baseCanvas instanceof RSMCanvas) {
                    return true;
                }
            }
        }
        return false;
    }
   
// Volume will be a normal indicator type and it no longer to be openned by default
//    /**
//     * Hide volume canvas
//     */
//    public void goDown() {
//        animation.playFromStart();
//    }
//    /**
//     * Show volume canvas
//     */
//    public void goUp() {
//        animation.play();
//    }
    
    /**
     * Return start date of this chart
     * @return start date
     */
    public Calendar getStartDate() {
        return startDate;
    }
    
    /**
     * Return end date of this chart
     * @return end date
     */
    public Calendar getEndDate() {
        return endDate;
    }
    /**
     * This is the method to open SMA for a chart
     * It will add a SMA accordion to indicator pane
     * It will add a SMA array to BaseCanvas
     */
    public void addSMA(){
        //As the coodinator of the main application and the canvas
        //It will call main app's add sma function to manage the GUI
        //It also calls the BaseCanvas's SMA management method to add SMA
        //It also bind the properties of the GUI with the SMA objects
        IndicatorSMASettingPane smaPane = application.openIndicatorSMASettingPane(category, marketCode, code);
        //Get the primaryKey of this pane
        String primaryKey = smaPane.getPrimaryKey();
        //Create a SMA in main chart
        SimpleMovingAverage smaMain = this.getMainChartCanvas().addSMA(primaryKey);
        //Bind properties
        smaMain.LineColorProperty.bind(smaPane.LineColorProperty);
        smaMain.ExponentialProperty.bind(smaPane.ExponentialProperty);  
        smaMain.LineDashOffsetProperty.bind(smaPane.LineDashOffsetProperty);
        smaMain.LineWidthProperty.bind(smaPane.LineWidthProperty);
        smaMain.avgOnProperty.bind(smaPane.avgOnProperty);
        smaMain.calBaseProperty.bind(smaPane.calBaseProperty);
        //Create a SMA in volume chart if volume chart is not null (be opened)
        if (this.getVolumeCanvas() != null){
            SimpleMovingAverage smaVolume = this.getVolumeCanvas().addSMA(primaryKey);
            //Bind properties
            smaVolume.LineColorProperty.bind(smaPane.LineColorProperty);
            smaVolume.ExponentialProperty.bind(smaPane.ExponentialProperty);
            smaVolume.LineDashOffsetProperty.bind(smaPane.LineDashOffsetProperty);
            smaVolume.LineWidthProperty.bind(smaPane.LineWidthProperty);
            smaVolume.avgOnProperty.bind(smaPane.avgOnProperty);
            smaVolume.calBaseProperty.bind(smaPane.calBaseProperty);
        }
    }
    /**
     * This is the method to remove SMA for a chart
     * It will remove SMA accordion to indicator pane
     * It will remove SMA array from BaseCanvas
     */
    public void removeSMA(String primaryKey) {
        //As the coodinator of the main application and the canvas
        //It also calls the BaseCanvas's SMA management method to remove SMA
        if(getMainChartCanvas() != null) {
            getMainChartCanvas().removeSMA(primaryKey);
        }
        if (getVolumeCanvas() != null) {
            getVolumeCanvas().removeSMA(primaryKey);
        }
    }
    
    /**
     * Open volume indicator chart
     */
    public void openVolume(){
        application.openIndicatorChart(
            application.getFinancialMarket().getIndexBook().isIndex(countryName, marketCode, code),
            countryName,
            category, 
            marketCode, 
            code, 
            startDate, 
            endDate, 
            IndicatorType.VOLUME, 
            null, 
            0d /*Volume all time low is always zero*/);     
    }
    
    /**
     * Close volume indicator chart
     */
    public void closeVolume() {
        this.closeIndicatorChart(IndicatorType.VOLUME);
    }
    
    /**
     * This is the method to open OBV for a chart
     * It will add a OBV accordion to indicator pane
     */
    public void openOBV(){
        if (getOBVCanvas() != null) {
            //Do nothing
            return;
        }
        
        //Create an OBV Indicator Setting Pane
        IndicatorOBVSettingPane obvPane = application.openIndicatorOBVSettingPane(category, marketCode, code);
        application.openIndicatorChart(
                application.getFinancialMarket().getIndexBook().isIndex(countryName, marketCode, code),
                countryName, 
                category, 
                marketCode, 
                code, 
                getStartDate(), 
                getEndDate(), 
                IndicatorType.OBV,
                null, //All Time High is not required, it will be calcualted when drawing the chart
                null); //All Time Low is not required, it will be calculated when drawing the chart
        OBVCanvas obvCanvas = getOBVCanvas();
        if (obvCanvas!=null){
            obvCanvas.OBVLineWidthProperty.bind(obvPane.LineWidthProperty);
            obvCanvas.OBVLineDashOffsetProperty.bind(obvPane.LineDashOffsetProperty);
            obvCanvas.OBVLineColorProperty.bind(obvPane.LineColorProperty);           
        }
    }
    
    /**
     * Method to close OBV Indicator for a chart 
     */
    public void closeOBV(){
        //Need to remove this OBVCanvas
        //Need to close the OBV Setting Pane
        application.closeIndicatorOBVSettingPane(category, marketCode, code);
    }
    
    /**
     * Method to open Relative Strength Dorsey indicator
     */
    public void openRSD() {
        if (getRSDCanvas() != null) {
            //Do nothing
            return;
        }
        
        //Need to open an instance of {@link DataUpdateDialog} to let user chose the compared index or stock
        ConcurrentSkipListSet<OHLC> denominatorData = application.selectOHLC();
        
        //If user has selected nothing, return from here
        if (denominatorData == null) {
            return;
        }
        
        String denominatorCode = denominatorData.first().getCode();
        //Create an RSD Indicator Setting Pane
        IndicatorRSDSettingPane rsdPane = application.openIndicatorRSDSettingPane(category, marketCode, code, denominatorCode);
        
        application.openIndicatorChart(
        false,
        countryName, 
        category, 
        marketCode, 
        code, 
        getStartDate(), 
        getEndDate(), 
        IndicatorType.RSD,
        null, //All Time High is not required, it will be calcualted when drawing the chart
        null); //All Time Low is not required, it will be calculated when drawing the chart
        RSDCanvas rsdCanvas = getRSDCanvas();
        RSDYCanvas rsdYCanvas = (RSDYCanvas)rsdCanvas.getYCanvas();
        Market market = application.getFinancialMarket().getMarket(category, marketCode);
        //Must setup this first
        rsdCanvas.setDenominatorData(market, denominatorCode);
        rsdYCanvas.setDenominatorData(market, denominatorCode);
        
        if (rsdCanvas!=null){
            rsdCanvas.RSLineWidthProperty.bind(rsdPane.LineWidthProperty);
            rsdCanvas.RSLineDashOffsetProperty.bind(rsdPane.LineDashOffsetProperty);
            rsdCanvas.RSLineColorProperty.bind(rsdPane.LineColorProperty);           
        }
    }
    
    /**
     * Method to open Relative Strength Mansfield indicator
     */
    public void openRSM() {
        if (getRSMCanvas() != null) {
            //Do nothing
            return;
        }
        
        //Need to open an instance of {@link DataUpdateDialog} to let user chose the compared index or stock
        ConcurrentSkipListSet<OHLC> denominatorData = application.selectOHLC();
        
        //If user has selected nothing, return from here
        if (denominatorData == null) {
            return;
        }
        
        String denominatorCode = denominatorData.first().getCode();
        //Create an RSD Indicator Setting Pane
        IndicatorRSMSettingPane rsmPane = application.openIndicatorRSMSettingPane(category, marketCode, code, denominatorCode);
        
        application.openIndicatorChart(
        false,
        countryName, 
        category, 
        marketCode, 
        code, 
        getStartDate(), 
        getEndDate(), 
        IndicatorType.RSM,
        null, //All Time High is not required, it will be calcualted when drawing the chart
        null); //All Time Low is not required, it will be calculated when drawing the chart
        RSMCanvas rsmCanvas = getRSMCanvas();
        RSMYCanvas rsmYCanvas = (RSMYCanvas)rsmCanvas.getYCanvas();
        Market market = application.getFinancialMarket().getMarket(category, marketCode);
        //Must setup this first
        rsmCanvas.setDenominatorData(market, denominatorCode);
        rsmYCanvas.setDenominatorData(market, denominatorCode);
        if (rsmCanvas!=null){
            rsmCanvas.RSMLineWidthProperty.bind(rsmPane.LineWidthProperty);
            rsmCanvas.RSMLineDashOffsetProperty.bind(rsmPane.LineDashOffsetProperty);
            rsmCanvas.RSMLineColorProperty.bind(rsmPane.LineColorProperty);
            rsmCanvas.avgOnProperty.bind(rsmPane.avgOnProperty);
            rsmCanvas.fillPathProperty.bind(rsmPane.fillPathProperty);
            rsmYCanvas.avgOnProperty.bind(rsmCanvas.avgOnProperty);
        }
    }
}