/*
 * $Id: chart_drawer.js,v 1.2 2011/08/09 13:04:55 obo Exp $
 */
dojo.declare("swx.inv.ChartDrawer", null, {

/*************************************************************************************************
 * Constructor                                                                                   *
 *************************************************************************************************/
  constructor: function(isSimple, key, chartLayout, isIntraday) {
    // all arrays and complex objects should be declared here

    this._isSimple = isSimple;         // the type of the chart (simple / advanced)
    this._key = key;                   // the key used to find HTML elements
    this._chartLayout = chartLayout;   // the layout of the chart

    this._isIntraday = isIntraday;
    this._isVML = false;       // Is this browser supporting VML or not (SVG)
    this._surface = null;      // The whole gfx surface
    this._border = null;       // the gfx border around the graph
    this._infoLayer = null;    // the layer containing text (loading, preview, ...)
    this._mainGroup = null;    // one BIG gfx group for everything that has to hide in preview mode
    this._hiddenGroup = null;  // this group is just a hidden group used to buffer graph rendering
    this._init();
  },

/*************************************************************************************************
 * Static members ( You have to use this.statics.XXXX )                                          *
 *************************************************************************************************/
  statics: {

    INFO_LAYER_BGCOLOR   : "transparent", //"#ABCDEF",
    INFO_LAYER_TXT_COLOR : "transparent", //"#FFFFFF",
    BORDER_COLOR         : "#CBCBCB",
    ID_COLOR             : "#306E92",
    ID_DOWN_COLOR        : "#CC0015",
    IDX1_COLOR           : "green",
    IDX2_COLOR           : "red",
    TI1_COLOR            : "#7D007D", // violet
    TI2_COLOR            : "#A2A200", // dark yellow
    TI3_COLOR            : "#FF55FF", // pink
    TI4_COLOR            : "#FF55FF", // pink (same as TI3 for Bollinger bands)
    TI_COLOR             : "#7D007D", // purple

    OLD_CLOSING_COLOR    : "#173651", // color of the line used to show old closing value

    PREVIEW_COLOR        : "#9999AA",

    ID_FILL_COLOR        : [0, 50, 150, 0.1],  // A bit transparent

    //axis colors
    GRID_COLOR           : "#dddddd",  // Grid basic color
    DARK_GRID_COLOR      : "#888888",  // Grid color if day has changed in intraday view
    GRID_BACKGROUND      : "#F7F7F7",  // Color used one out of two blocks
    //---

    LINE_STYLE           : "line",
    BARS_STYLE           : "bars",
    CANDLESTICKS_STYLE   : "candles",

    CROSSHAIR_FULL_STYLE  : "full",
    CROSSHAIR_SMALL_STYLE : "small",
    CROSSHAIR_NONE_STYLE  : "none",

    /**
     * Just (re)set the vml path to the shape.
     */
    setVmlPath: function(shape, path) {
      shape.vmlPath = path;
      shape.getNode().path.v = path + " r0,0 e";
    }

  },

/*************************************************************************************************
 * Private init function. Used to initialise everything                                          *
 *************************************************************************************************/
  _init: function() {
    // overwrite default chart styles in statics fields if they are present inside chartConfig.
    this._applyCustomChartStyles(this._chartLayout.chartStyles, this.statics);

    var gfxNode = dojo.byId("gfxHolder" + this._key);
    // create the main surface
    this._surface = dojox.gfx.createSurface(gfxNode, this._chartLayout.surfaceWidth,
        this._chartLayout.surfaceHeight);

    // calculate the offset between the surface and the div containing it to correct it
    var gfxNode_coords = dojo.coords(gfxNode, true);
    var surface_coords = dojo.coords(this._surface.rawNode, true);
    var surface_offset = {};
    surface_offset.x = surface_coords.x - gfxNode_coords.x;
    surface_offset.y = surface_coords.y - gfxNode_coords.y;

    // move the surface holding node to be adjusted to everything else (Only do this the first time)
    if (!gfxNode.isFudged) {
      dojo.style(gfxNode, "left", addPX(rmvPX(dojo.style(gfxNode, "left")) - surface_offset.x));
      dojo.style(gfxNode, "top", addPX(rmvPX(dojo.style(gfxNode, "top")) - surface_offset.y));
      gfxNode.isFudged = true;
    }

    // is VML or not?
    var nothing = this._surface.createPath();
    if (nothing.vmlPath != null)
      this._isVML = true;
    this._surface.remove(nothing);  // don't use it

    // setup the infoLayer (before the border to have the border in front)
    this._initInfoLayer();

    // the border around the graph ( not anti-aliased )
    this._border = this._surface.createGroup();
    this._border.createRect(
      {
        x: this._chartLayout.marginLeft,
        y: this._chartLayout.marginTop,
        width: this._chartLayout.paddingLeft + this._chartLayout.graphWidth
          + this._chartLayout.paddingRight,
        height: this._chartLayout.paddingTop + this._chartLayout.graphHeight
          + this._chartLayout.paddingBottom
      }
    ).setStroke(this.statics.BORDER_COLOR);

    if (!this._isSimple) {
      this._border.createLine(
        {
          x1: this._chartLayout.marginLeft,
          y1: this._chartLayout.marginTop + 20,
          x2: this._chartLayout.marginLeft + this._chartLayout.paddingLeft
            + this._chartLayout.graphWidth + this._chartLayout.paddingRight,
          y2: this._chartLayout.marginTop + 20
        }
      ).setStroke(this.statics.BORDER_COLOR);
    }
    this._border.getNode().setAttribute("shape-rendering","crispEdges");

    // one BIG group for everything that has to hide in preview
    this._mainGroup = this._surface.createGroup();

    // one hidden group
    this._hiddenGroup = this._surface.createGroup();
    dojo.style(this._hiddenGroup.getNode(), "visibility", "hidden");

    /**
     *  for advanced graphs:
     */
    if (!this._isSimple) {
      // The preview layer
      this._previewGroup = this._surface.createGroup();

      // The addons (volume + technical indicators)
      this._addonDrawer = new swx.inv.ChartAddonDrawer(surface_offset, this._chartLayout, this._isVML);

      // The events
      this._eventDrawer = new swx.inv.ChartEventDrawer(this._key, this._chartLayout, this._isVML);

      // Small graph for all values
      this._zsurface_gray = dojox.gfx.createSurface("zoom_holder_gray", this._chartLayout.paddingLeft +
                                                                       this._chartLayout.graphWidth +
                                                                       this._chartLayout.paddingRight,
                                                                       this._chartLayout.smallGraphHeight);
      this._zsurface_color = dojox.gfx.createSurface("zoom_holder_color", this._chartLayout.paddingLeft +
                                                                       this._chartLayout.graphWidth +
                                                                       this._chartLayout.paddingRight,
                                                                       this._chartLayout.smallGraphHeight);

      // Correct the slider position because of some border between the div and the surface
      dojo.style("zoom_gfx_holder", "left",
          addPX(rmvPX(dojo.style("zoom_gfx_holder", "left")) - surface_offset.x));
      dojo.style("zoom_gfx_holder", "top",
          addPX(rmvPX(dojo.style("zoom_gfx_holder", "top")) - surface_offset.y));

      // The slider
      this.sliderHelper = new swx.inv.ChartSliderHelper(this._chartLayout, gfxNode_coords.x, surface_offset.x);

      this.crosshairStyle = this.statics.CROSSHAIR_FULL_STYLE;
    }

    // the render style (only line for simple charts)
    this.renderStyle = this.statics.LINE_STYLE;

    /* fix for ie -> loosing the link on not drawn surface when many tab in the header (index graphs)*/
    /* we create a fake white big box */
    if(dojo.isIE){
        this._mainGroup.createRect(
        { x: 0, y: 0, width: this._chartLayout.graphWidth, height: this._chartLayout.graphHeight })
        .setFill("white");
    }

    // two important members doing all the hard work!
    this._graphDrawer    = new swx.inv.ChartGraphDrawer(
        this._chartLayout,
        this._mainGroup,
        this._isVML);
    this._axisGridDrawer = new swx.inv.ChartAxisGridDrawer(
        this._key,
        this._chartLayout,
        this._mainGroup,
        this._isVML,
        this._border,
        this._isSimple);
  },


  /*************************************************************************************************
   * Utility method used to overwrite default chart styles with custom ones from charConfig        *
   *************************************************************************************************/

_applyCustomChartStyles: function(src,dst) {
    if(!src) return;
    if (src.ID_COLOR) dst.ID_COLOR = src.ID_COLOR ;

    if (src.INFO_LAYER_BGCOLOR) dst.INFO_LAYER_BGCOLOR = src.INFO_LAYER_BGCOLOR;
    if (src.INFO_LAYER_TXT_COLOR) dst.INFO_LAYER_TXT_COLOR = src.INFO_LAYER_TXT_COLOR;
    if (src.BORDER_COLOR) dst.BORDER_COLOR = src.BORDER_COLOR;
    if (src.ID_DOWN_COLOR) dst.ID_DOWN_COLOR = src.ID_DOWN_COLOR;
    if (src.IDX1_COLOR) dst.IDX1_COLOR = src.IDX1_COLOR;
    if (src.IDX2_COLOR) dst.IDX2_COLOR = src.IDX2_COLOR;
    if (src.TI1_COLOR) dst.TI1_COLOR = src.TI1_COLOR;
    if (src.TI2_COLOR) dst.TI2_COLOR = src.TI2_COLOR;
    if (src.TI3_COLOR) dst.TI3_COLOR = src.TI3_COLOR;
    if (src.TI4_COLOR) dst.TI4_COLOR = src.TI4_COLOR;
    if (src.TI_COLOR) dst.TI_COLOR = src.TI_COLOR;
    if (src.OLD_CLOSING_COLOR) dst.OLD_CLOSING_COLOR = src.OLD_CLOSING_COLOR;

    if (src.PREVIEW_COLOR) dst.PREVIEW_COLOR = src.PREVIEW_COLOR;

    if (src.ID_FILL_COLOR) dst.ID_FILL_COLOR = src.ID_FILL_COLOR;

    if (src.GRID_COLOR) dst.GRID_COLOR = src.GRID_COLOR;
    if (src.DARK_GRID_COLOR) dst.DARK_GRID_COLOR = src.DARK_GRID_COLOR;
    if (src.GRID_BACKGROUND) dst.GRID_BACKGROUND = src.GRID_BACKGROUND;

    if (src.LINE_STYLE) dst.LINE_STYLE = src.LINE_STYLE;
    if (src.BARS_STYLE) dst.BARS_STYLE = src.BARS_STYLE;
    if (src.CANDLESTICKS_STYLE) dst.CANDLESTICKS_STYLE = src.CANDLESTICKS_STYLE;

    if (src.CROSSHAIR_FULL_STYLE) dst.CROSSHAIR_FULL_STYLE = src.CROSSHAIR_FULL_STYLE;
    if (src.CROSSHAIR_SMALL_STYLE) dst.CROSSHAIR_SMALL_STYLE = src.CROSSHAIR_SMALL_STYLE;
    if (src.CROSSHAIR_NONE_STYLE) dst.CROSSHAIR_NONE_STYLE = src.CROSSHAIR_NONE_STYLE;

  },

/*************************************************************************************************
 * Private initInfoLayer function. Used to create the info layer                                 *
 *************************************************************************************************/
  _initInfoLayer: function() {
    var infoLayerFontSize = null;
    var infoLayerMarginLeft = null;
    var infoLayerMarginTop  = null;
    if (this._chartLayout.surfaceWidth < 200) {
      infoLayerFontSize = "10pt";
      infoLayerMarginLeft = 10;
      infoLayerMarginTop  = 15;
    } else if (this._chartLayout.surfaceWidth < 300) {
      infoLayerFontSize = "13pt";
      infoLayerMarginLeft = 15;
      infoLayerMarginTop  = 28;
    } else {
      infoLayerFontSize = "25pt";
      infoLayerMarginLeft = 20;
      infoLayerMarginTop  = 40;
    }

    this._infoLayer = this._surface.createGroup();
    dojo.style(this._infoLayer.getNode(), "visibility", "hidden");
    this._infoLayer.createRect(
      {
        x: this._chartLayout.marginLeft,
        y: this._chartLayout.marginTop,
        width:  this._chartLayout.paddingLeft + this._chartLayout.graphWidth
              + this._chartLayout.paddingRight,
        height: this._chartLayout.paddingTop + this._chartLayout.graphHeight
              + this._chartLayout.paddingBottom
      }
    ).setFill(this.statics.INFO_LAYER_BGCOLOR);
    this._infoLayer.txt = this._infoLayer.createText(
                            {
                              x: this._chartLayout.marginLeft + infoLayerMarginLeft,
                              y: this._chartLayout.marginTop + infoLayerMarginTop
                            }
                          ).setFont({family: "Times", size: infoLayerFontSize, weight: "bold"})
                           .setFill(this.statics.INFO_LAYER_TXT_COLOR);

  },
/*************************************************************************************************
 * Public displayInfotxt function. Used to show 'loading...' waiting... etc..                    *
 *************************************************************************************************/
  displayInfotxt: function(textToDisplay) {
    this._infoLayer.txt.getShape().text = textToDisplay;            // change the text
    this._infoLayer.txt.setShape(this._infoLayer.txt.getShape());   // just to refresh the browser
    dojo.style(this._infoLayer.getNode(), "visibility", "visible"); // show the layer
  },

  displayLoading: function() {
    dojo.style("gfxLoading"+this._key, "display", "");
  },

  hideLoading: function() {
    dojo.style("gfxLoading"+this._key, "display", "none");
  },

  changeDomain: function(domain) {
    this._isIntraday = ("1d" == domain);
  },

  drawSimple: function(graph_values, values, onlyDrawLine) {

    var shapes = null;
    // draw new line
    if (onlyDrawLine) {

      // draw new ones to the hidden group
      if (this._isIntraday) {
        shapes = this._graphDrawer.drawIntraday2ColorLine(graph_values.line, this._hiddenGroup, graph_values.oldClosePy,
            this.statics.ID_COLOR, this.statics.ID_DOWN_COLOR, this.statics.ID_FILL_COLOR, this._chartLayout.useCrispEdges);
      } else {
        shapes = this._graphDrawer.drawLine(graph_values.line, this._hiddenGroup, this.statics.ID_COLOR,
            this.statics.ID_FILL_COLOR,this._chartLayout.useCrispEdges);
      }

      // transfer paths/shapes to the visible group (prevents it from flashing when updating)
      if (this._isVML) {
        this._graphDrawer.graphShapes["idFill"].rawNode.path = shapes[0].rawNode.path;
        if (shapes[1]) this._graphDrawer.graphShapes["idLineUp"].rawNode.path = shapes[1].rawNode.path;
        if (shapes[2]) this._graphDrawer.graphShapes["idLineDown"].rawNode.path = shapes[2].rawNode.path;
      } else {
        // changing rawnode for SVG does not work, we have to use setShape
        this._graphDrawer.graphShapes["idFill"].setShape(shapes[0].getShape());
        if (shapes[1]) this._graphDrawer.graphShapes["idLineUp"].setShape(shapes[1].getShape());
        if (shapes[2]) this._graphDrawer.graphShapes["idLineDown"].setShape(shapes[2].getShape());
      }
      this._hiddenGroup.clear(); // remove all from the hidden group to save memory
    } else {
      // remove old ones
      this.clean();
      // draw new ones
      if (this._isIntraday) {
        shapes = this._graphDrawer.drawIntraday2ColorLine(graph_values.line, this._mainGroup, graph_values.oldClosePy,
            this.statics.ID_COLOR, this.statics.ID_DOWN_COLOR, this.statics.ID_FILL_COLOR,this._chartLayout.useCrispEdges);
      } else {
        shapes = this._graphDrawer.drawLine(graph_values.line, this._mainGroup, this.statics.ID_COLOR,
            this.statics.ID_FILL_COLOR,this._chartLayout.useCrispEdges);
      }
      this._graphDrawer.graphShapes["idFill"] = shapes[0];
      if (shapes[1]) this._graphDrawer.graphShapes["idLineUp"] = shapes[1];
      if (shapes[2]) this._graphDrawer.graphShapes["idLineDown"] = shapes[2];

      this._axisGridDrawer.drawAxis(values.id, false,
          this._isIntraday && !this._chartLayout.horizontalGrid,
          {'GRID_COLOR':this.statics.GRID_COLOR, 'DARK_GRID_COLOR':this.statics.DARK_GRID_COLOR,'GRID_BACKGROUND':this.statics.GRID_BACKGROUND});
      if (this._isIntraday && graph_values.oldClosePy) {
        var oldCloseShape = this._axisGridDrawer.drawOldClosing(graph_values.oldClosePy,this.statics.OLD_CLOSING_COLOR);
        this._graphDrawer.graphShapes["oldClose"] = oldCloseShape;
      }
      // hide the info layer in case it was visible
      dojo.style(this._infoLayer.getNode(), "visibility", "hidden");
    }
  },

  drawAdvanced: function(graph_values, values) {
    // Clean old stuff
    this.clean();
    // Remove preview line
    this.endPreview();

    // redraw the nomal idline (with new gMin and gMax)
    var shapes = this._graphDrawer.drawLine(graph_values.id.line,
                      this._mainGroup,
                      this.statics.ID_COLOR,
                      this.statics.ID_FILL_COLOR,
                      this._chartLayout.useCrispEdges);
    this._graphDrawer.graphShapes.idFill = shapes[0];
    this._graphDrawer.graphShapes.idLineUp = shapes[1];

    // hide line if not in line mode
    if (this.renderStyle != this.statics.LINE_STYLE) {
      this.hideLine();
    }

    var drawPercent = false;

    // redraw the first comparison line
    if (graph_values.idx1) {
      this._graphDrawer.graphShapes.idx1 =
          this._graphDrawer.drawLine(graph_values.idx1.line, this._mainGroup,
              this._getColorForLine("idx1"), null, this._chartLayout.useCrispEdges)[0];
      drawPercent = true;
    }
    // redraw the 2nd comparison line
    if (graph_values.idx2) {
      this._graphDrawer.graphShapes.idx2 =
          this._graphDrawer.drawLine(graph_values.idx2.line, this._mainGroup,
              this._getColorForLine("idx2"), null, this._chartLayout.useCrispEdges)[0];
      drawPercent = true;
    }
    // redraw the technical Indicator
    for (var i=1; i<=10; i++) {
      if (graph_values["ti"+i]) {
        if (graph_values["ti"+i].isAddon) {
          this._graphDrawer.graphShapes["ti"+i] =
              this._addonDrawer.drawTechnicalIndicator(graph_values["ti"+i]);
        } else {
          this._graphDrawer.graphShapes["ti"+i] =
              this._graphDrawer.drawLine(graph_values["ti"+i].line,
                              this._mainGroup, this._getColorForLine("ti"+i), null, this._chartLayout.useCrispEdges)[0];
        }
      }
    }

    // redraw the axis
    this._axisGridDrawer.drawAxis(values.id, drawPercent, false,
        {'GRID_COLOR':this.statics.GRID_COLOR, 'DARK_GRID_COLOR':this.statics.DARK_GRID_COLOR,'GRID_BACKGROUND':this.statics.GRID_BACKGROUND});

    if (!drawPercent && "1d" == selectedTimeRange) {
      var oldCloseShape = this._axisGridDrawer.drawOldClosing(graph_values.id.oldClosePy,this.statics.OLD_CLOSING_COLOR);
      this._graphDrawer.graphShapes["oldClose"] = oldCloseShape;
    }

    // redraw bars or candlesticks
    if (this.renderStyle == this.statics.BARS_STYLE) {
      this._graphDrawer.drawBars(graph_values.id.points);
    } else if (this.renderStyle == this.statics.CANDLESTICKS_STYLE) {
      this._graphDrawer.drawCandles(graph_values.id.points);
    }

    if (values.id.yMin) { // draw max and min only if they exist! (maybe zoomed in between two trades)
      this._graphDrawer.graphShapes.maxMin = this._axisGridDrawer.drawMaxMinPoints(values.id);
    }

    // hide the info layer in case it was visible
    dojo.style(this._infoLayer.getNode(), "visibility", "hidden");

    connectTrackBall(); // connect mouse to trackball
    this.sliderHelper.trackMovedToPosition(0); // set to first pixel
  },

  clean: function() {
    this._graphDrawer.cleanForDraw();
    this._axisGridDrawer.cleanForDraw();
    if (this._addonDrawer) {
      this._addonDrawer.clean();
    }
    if (this._eventDrawer) {
      this._eventDrawer.clean();
    }
  },

  _getColorForLine: function(lineName) {
    if (lineName == "idx1") {
      return this.statics.IDX1_COLOR;
    } else if (lineName == "idx2") {
      return this.statics.IDX2_COLOR;
    } else if (lineName == "ti1") {
      return this.statics.TI1_COLOR;
    } else if (lineName == "ti2") {
      return this.statics.TI2_COLOR;
    } else if (lineName == "ti3") {
      return this.statics.TI3_COLOR;
    } else if (lineName == "ti4") {
      return this.statics.TI4_COLOR;
    } else if (lineName.indexOf("ti") == 0) {
      return this.statics.TI_COLOR;
    }
  },

  preparePreview: function(keepZoomGraph) {

    // disconnect mouse from trackball
    disConnectTrackBall();

    dojo.style(this._previewGroup.getNode(), "visibility", "");
    this.clean();
    if (!keepZoomGraph) {
      dojo.style("zoom_holder_gray", "visibility", "hidden");
      dojo.style("zoom_holder_color", "visibility", "hidden");
    }
  },

  endPreview: function() {
    dojo.style(this._previewGroup.getNode(), "visibility", "hidden");
    dojo.style("zoom_holder_gray", "visibility", "");
    dojo.style("zoom_holder_color", "visibility", "");
  },

  drawSmallChart: function(values) {
    this._graphDrawer.drawSmallChart(values, this._zsurface_gray, this._zsurface_color);
  },

  drawVolume: function(graph_values, values) {
    if (this._addonDrawer.isVolumeVisible) {
      this._addonDrawer.drawVolume(graph_values, values);
    }
  },

  showVolume: function() {
    this._addonDrawer.showVolume();
  },

  hideVolume: function() {
    this._addonDrawer.hideVolume();
  },

  isVolumeVisible: function() {
    return this._addonDrawer.isVolumeVisible;
  },

  drawBars: function(values) {
    this._graphDrawer.drawBars( values );
  },

  hideLine: function() {
    dojo.style(this._graphDrawer.graphShapes.idLineUp.getNode(), "visibility", "hidden");
  },

  showLine: function() {
    dojo.style(this._graphDrawer.graphShapes.idLineUp.getNode(), "visibility", "");
  },

  hideBars: function() {
    dojo.style(this._graphDrawer.graphShapes.idBars.getNode(), "visibility", "hidden");
  },

  showBars: function() {
    dojo.style(this._graphDrawer.graphShapes.idBars.getNode(), "visibility", "");
  },

  drawCandles: function(values) {
    this._graphDrawer.drawCandles( values );
  },

  hideCandles: function() {
    dojo.style(this._graphDrawer.graphShapes.idCandles.getNode(), "visibility", "hidden");
  },

  showCandles: function() {
    dojo.style(this._graphDrawer.graphShapes.idCandles.getNode(), "visibility", "");
  },

  drawPreview: function(preview_graph_values) {
    if (this._graphDrawer.graphShapes.previewLine != null) {
      this._previewGroup.remove(this._graphDrawer.graphShapes.previewLine);
    }
    this._graphDrawer.graphShapes.previewLine = this._graphDrawer.drawLine(preview_graph_values,
       this._previewGroup, this.statics.PREVIEW_COLOR,null, this._chartLayout.useCrispEdges)[0];

  },

  drawEvents: function(values, events, showedEvents) {
    if (this._eventDrawer.visible) {
      this._eventDrawer.drawEvents(values, events, showedEvents);
    }
  },

  showEvents: function() {
    this._eventDrawer.show();
  },

  hideEvents: function() {
    this._eventDrawer.hide();
  },

  areEventsVisible: function() {
    return this._eventDrawer.visible;
  }

});

