content: import Dthree from 'd3';
  import React from 'react';
  
  export default class SilverSeriesColumn extends React.Component {
  
    // PROP TYPES
    static get propTypes() {
      return {
        config: React.PropTypes.object,
        onPassColumnClick: React.PropTypes.func,
      };
    }
  
    // COMPONENT DID MOUNT
    componentDidMount() {
      // To guarantee that we only update on 2nd render:
      if (!this.props.config.firstRender) {
        this.updateColumns();
        // const context = this.props.config.className.split(' ')[1]
        // const context = 'zeroline-group';
        // ChartUtilities.updateAnyZeroLine(this.props.config, context, true);
        // this.updateZeroLine()
      }
    }
  
    // COMPONENT DID UPDATE
    componentDidUpdate() {
      // To guarantee that we only update on 2nd render:
      if (!this.props.config.firstRender) {
        this.updateColumns();
        // const context = this.props.config.className.split(' ')[1]
        // const context = 'zeroline-group';
        // ChartUtilities.updateAnyZeroLine(this.props.config, context, true);
        // this.updateZeroLine()
      }
    }
  
    // ======= Event handler ======
  
    // COLUMN CLICK
    // Handles column click event. Params are data (cat and value)
    // and index in overall data.
    // NOTE: This event currently gets passed back up to
    // ColumnChart, where I do a console.log. Long-term, I might
    // use this to set 'emphasis' on the column...
    columnClick(colData, index) {
      const clickObj = { colData, index };
      this.props.onPassColumnClick(clickObj);
    }
    // BAR CLICK ends
  
    // GET COLOURS
    // Called from updateColumns to map colours by series
    /*
    NOTE: moved to chart-utilities, Sep 2016
    NOTE: delete eventually (along with call from updateColumns)...
    getColours(seriesNames, colourSet) {
      // Colours from config file
      // const colourSet = [ '#004D64', '#6995A8', '#009FD8', '#ACADB0' ];
      const colourScale = Dthree.scale.ordinal()
        .domain(seriesNames)
        .range(colourSet);
      return colourScale;
    }
    */
    // GET COLOURS ends
  
    // UPDATE COLUMNS
    updateColumns() {
      const config = this.props.config;
      // Context (parent group created in render) and duration
      // (NOTE: In the long term, we'd need more than one group...)
      const className = config.className.split(' ')[1];
      const barGroup = Dthree.select(`.${ className }`);
      // NOTE: duration is still up in the air...
      const duration = config.duration;
      // Passed scales:
      const yScale = config.yScale;
      const xMainScale = config.xMainScale;
      const xClusterScale = config.xClusterScale;
      // Data
      const chartdata = config.chartdata;
      const accum = config.accum;
      // First header string is key to category strings
      const catStr = config.catHead;
      // Colours
      // const colourSet = config.colourSet;
      // const colours = this.getColours(seriesHeads, colourSet);
      const colours = config.colourMap;
      // As far as I can see, the data is in the right format:
      // an array of objects with header:value properties
      // Map data (iii is a series index):
      const mappedData = colours.domain().map((header, iii) => {
        const objA = chartdata.map((ddd) => {
          const objB = { val: Number(ddd[header]), category: ddd[catStr], fill: colours(header), header, seriesNo: iii };
          return objB;
        });
        return objA;
      });
      // Array of +/– base vals for 'opposing' charts
      const baseVals = [];
      for (let aPt = 0; aPt < config.pointCount; aPt++) {
        baseVals.push({
          negBase: 0,
          posBase: 0,
        });
      }
      // mappedData is an array of arrays, each of which represents a series
      // Each series sub-array consists of <pointCount> objects
      // defining one data point and with properties...
      //    category: the category string
      //    fill: fill colour
      //    val: the 'internal', *unscaled* value of THIS point
      const groupBinding = barGroup.selectAll('.series-group')
        .data(mappedData);
      // Enter, appending class
      groupBinding.enter()
        .append('g')
        .attr('class', (ddd, iii) => `series-group series-${ iii }`)
        ;
      // No update
      // Exit
      groupBinding.exit()
        .selectAll('.d3-column-rect')
        .transition().duration(duration)
        .attr('width', 0)
        ;
      // Exit
      groupBinding.exit()
        .transition().delay(duration)
        .remove()
        ;
  
      // Bind inner (points) data
      const rectBinding = groupBinding.selectAll('.d3-column-rect')
        .data((ddd) => ddd);
      // Enter appends rect on zero, at zero height
      rectBinding.enter()
        .append('rect')
          .attr({
            'class': 'd3-column-rect',
            'x': (ddd) => xMainScale(ddd.category),
            'width': Math.max(xMainScale.rangeBand(), 0),
            'y': yScale(0),
            'height': 0,
          })
          // Set click event on rect
          .on('click', (ddd, iii) => this.columnClick(ddd, iii))
          // Crude tooltip (populated in update)
          // NOTE: can't use '=>' because D3 needs to select 'this'
          /* eslint-disable func-names, no-invalid-this */
          .each(function () {
            Dthree.select(this).append('svg:title')
              .attr('class', 'd3-tooltip')
              ;
          })
        ;
  
      // Update
      // NOTE: this can handle +/– values, but (for now) insists upon a 'default'
      // anchorage to zero (ie, it can't handle broken scales...)
      rectBinding
        .transition().duration(duration)
          .attr({
            // Vert ('y') origin
            'y': (ddd, iii) => {
              // iii is point-counter here
              // By default, since SVG draws from top, set y to val:
              // (assumes val is positive; overwrites negative below)
              let yPos = Number(ddd.val);
              if (accum) {
                // Stacked bars
                const val = yPos;
                if (val < 0) {
                  // If val is negative, subtract it from previous loop's
                  // baseline. Baseline increments negatively for next
                  // neg value
                  const baseVal = baseVals[iii].negBase;
                  yPos = baseVal;
                  baseVals[iii].negBase += val;
                } else {
                  // + val. Use prev baseline, then increment for next +
                  const baseVal = baseVals[iii].posBase;
                  yPos += baseVal;
                  baseVals[iii].posBase += val;
                }
              } else if (yPos <= 0) {
                // But non-accum draw neg vals *from* zero
                yPos = 0;
              }
              return yScale(yPos);
            },
            // Width: force to positive value, subtracting
            // scaled zero...
            'height': (ddd) => {
              // Don't allow neg height!
              const hgt = yScale(0) - yScale(Math.abs(Number(ddd.val)));
              return Math.max(hgt, 0);
            },
            // X position
            'x': (ddd) => {
              let xPos = 0;
              if (accum) {
                // Stacked bars...
                xPos = xMainScale(ddd.category);
              } else {
                // Unstacked are in clusters. Get the overall cluster position:
                xPos = xMainScale(ddd.category);
                // ...then add internal cluster scaling:
                xPos += xClusterScale(ddd.header);
              }
              return xPos;
            },
            // Column width
            'width': () => {
              // Default is non-accum width...
              let wid = xClusterScale.rangeBand();
              if (accum) {
                // Explicitly subtract gap from col width
                // (non-accum: handled in columnchart when rangeBand set)
                wid = xMainScale.rangeBand() - config.padding;
              }
              // Don't be less than zero!
              return Math.max(wid, 0);
            },
          })
          .style('fill', (ddd) => ddd.fill)
          // Populate tooltip (set up by 'enter')
          .each(function (ddd) {
            const myBar = Dthree.select(this);
            myBar.select('title').text(`Header: ${ ddd.header }; category: ${ ddd.category }; value: ${ ddd.val }`);
          })
      ;
  
      // NOTE: EXIT isn't right yet...
      rectBinding.exit()
        .transition().duration(duration)
        .attr('height', 0);
      rectBinding.exit()
        .transition().delay(duration * 2)
          .remove();
    }
    // UPDATE COLUMNS ends
  
    // NOTE: ZERO LINE HANDLING
    // I moved this to the x-axis component; but actually it may want
    // to be here, since I'll probably want to draw the zero line
    // separately, *above* the actual series...
  
    // UPDATE ZERO LINE
    // Handles any zero line
    updateZeroLine() {
      const config = this.props.config;
      const zPrefs = config.zeroPrefs;
      const className = config.className.split(' ')[1];
      // NOTE: next isn't right -- for initial revamp style
      // I need to go right to the edge of the 'original' IB. This is
      // OK for now, but if style change, what's the flag that tells
      // me to behave differently...?
      const width = config.originalBounds.width;
      // // Context and duration
      // // (NOTE: In the long term, we'd need more than one group...)
      const columnGroup = Dthree.select(`.${ className }`);
      const duration = config.duration;
      // Scale:
      const yScale = config.yScale;
      // How will the zero line appear?
      let zColour = zPrefs.simple;
      if (config.mixedVals) {
        zColour = zPrefs.mixed;
      }
      const zClass = 'd3-col-zero';
      const zWidth = zPrefs.width;
      // Bind data (needs *some* value)
      const zBinding = columnGroup.selectAll('line')
        .data([ 0 ]);
      //
      // ENTER
      zBinding.enter()
        .append('line');
      // NOTE. This can handle +/– values, but insists upon a 'default'
      // anchorage to zero (ie, it can't handle broken scales... yet)
      // (Although if scale breaks, the zero line will vanish somewhere off-chart...)
      zBinding
        .transition().duration(duration)
        .attr({
          'class': zClass,
          'y1': yScale(0),
          'x1': 0,
          'y2': yScale(0),
          'x2': width,
        })
        .style({
          'stroke-width': zWidth,
          'stroke': zColour,
        })
        ;
  
      zBinding.exit()
          .remove();
    }
    // UPDATE ZERO LINE ends
  
    // RENDER all-series parent group:
    render() {
      return (
        < g className={this.props.config.className} ref="columnSeriesGroup" />
      );
    }
  }
  