content: // Disable prefer-reflect, for D3 axis.call()
  /* eslint-disable prefer-reflect */
  import React from 'react';
  import Dthree from 'd3';
  
  export default class SilverXaxisOrdinal extends React.Component {
  
    // PROP TYPES
    // I think the same as linear
    static get propTypes() {
      return {
        config: React.PropTypes.object,
        // Not passed in; declared here as default prop
        primaryAxis: React.PropTypes.func,
        secondaryAxis: React.PropTypes.func,
        // Callback after test on stringwidth(s)
        onReturnRevisedInnerBox: React.PropTypes.func,
      };
    }
  
    // DEFAULT PROPS
    // I think the same as linear
    static get defaultProps() {
      return {
        primaryAxis: Dthree.svg.axis(),
        secondaryAxis: Dthree.svg.axis(),
      };
    }
  
    // COMPONENT DID MOUNT
    // I think the same as linear
    componentDidMount() {
      const config = this.props.config;
      if (config.testFlag) {
        // margin tests, at the end of which we return updated innerbox bounds...
        this.doStringTests();
      } else {
        const primaryXaxis = this.setPrimaryXaxisConfig();
        this.updatePrimaryXaxis(primaryXaxis);
        // Now check for any secondary axis:
        if (config.categoryType === 'time') {
          if (config.timeFormats.secondRow) {
            const secondaryXaxis = this.setSecondaryXaxisConfig();
            this.updateSecondaryXaxis(secondaryXaxis);
          }
        }
      }
    }
  
    // COMPONENT DID UPDATE
    // I think the same as linear
    componentDidUpdate() {
      const config = this.props.config;
      if (config.testFlag) {
        // margin tests, at the end of which we return updated innerbox bounds...
        this.doStringTests();
      } else {
        const primaryXaxis = this.setPrimaryXaxisConfig();
        this.updatePrimaryXaxis(primaryXaxis);
        // Now check for any secondary axis:
        if (this.hasSecondaryAxis(config)) {
          const secondaryXaxis = this.setSecondaryXaxisConfig();
          this.updateSecondaryXaxis(secondaryXaxis);
        }
      }
    }
  
    // HAS SECONDARY AXIS
    // Checks config for 2ndary axis markers
    // We're looking for a 'time' axis with 2 rows
    hasSecondaryAxis(config) {
      if (config.categoryType === 'time') {
        if (config.timeFormats.secondRow) {
          return true;
        }
      }
      // Still here?
      return false;
    }
  
    // GET TICK FORMAT
    // Returns
    getTickFormat(config, isPrimary) {
      // By default, use what you got (string, number)
      // NOTE: this still doesn't address the yyyy/yy issue
      function defaultFormat(ddd) {
        return ddd;
      }
      let tickFormat = defaultFormat;
      // But time axis, imposes D3 format from lookup
      if (config.categoryType === 'time') {
        if (isPrimary) {
          tickFormat = Dthree.time.format(config.timeFormats.format);
        } else {
          tickFormat = Dthree.time.format(config.timeFormats.secondFormat);
        }
      }
      return tickFormat;
    }
  
    // DO STRING TESTS
    // Called if testFlag=true, to drop text on the wrapper
    // and see what extra margins we have to allow for.
    // Xaxis ordinal tests--
    //  - Width of first and last axis label
    //    But there are all sorts of issues regarding WHAT that last label is...
    //    Should I draw the entire axis with all labels and then see how far it
    //    projects left and right?
    //    NOTE: and what about any secondary axis...?
    doStringTests() {
      const config = this.props.config;
      // Context: just use the specific (indexed) axis group
      const className = config.className.split(' ')[1];
      const axisGroup = Dthree.select(`.${ className }`);
      // Text object
      const testText = axisGroup.append('text')
        .attr('id', 'testText')
        .style({
          'font-family': config.textPrefs.font,
          'font-size': config.textPrefs.size,
          'fill': config.textPrefs.fill,
        })
        ;
      const bounds = config.bounds;
      // Extract first and last values
      const domain = config.scale.domain();
      const primaryValA = domain[0];
      const primaryValZ = domain[domain.length - 1];
      // Now format and plonk
      // Primary axis:
      let timeFormat = this.getTickFormat(config, true);
      testText.text(timeFormat(primaryValA));
      let leftTweak = testText.node().getComputedTextLength();
      testText.text(timeFormat(primaryValZ));
      let rightTweak = testText.node().getComputedTextLength();
      // Now test for secondary strings...
      let sWidthA = 0;
      let sWidthZ = 0;
      timeFormat = this.getTickFormat(config, false);
      const fArray = config.secondaryAxisFilter;
      if (fArray[0].draw) {
        testText.text(timeFormat(primaryValA));
        sWidthA = testText.node().getComputedTextLength();
        if (sWidthA > leftTweak) {
          leftTweak = sWidthA;
        }
      }
      if (fArray[fArray.length - 1].draw) {
        testText.text(timeFormat(primaryValZ));
        sWidthZ = testText.node().getComputedTextLength();
        if (sWidthZ > rightTweak) {
          rightTweak = sWidthZ;
        }
      }
      leftTweak /= 2;
      rightTweak /= 2;
      // Now, consider clusterwidth, if this is a column chart...
      const halfCluster = config.halfClusterWidth;
      if (typeof halfCluster !== 'undefined') {
        if (leftTweak < halfCluster) {
          leftTweak = 0;
        } else {
          leftTweak -= halfCluster;
        }
        if (rightTweak < halfCluster) {
          rightTweak = 0;
        } else {
          rightTweak -= halfCluster;
        }
      }
      bounds.x += leftTweak;
      bounds.width -= (leftTweak + rightTweak);
      //
      // HEIGHT adjustment is based on number of 'lines' of labels.
      // NOTE: initial assumption is that the scale is at bottom of chart
      // NOTE: and that there's only 1 possible extra row...
      let labelLineCount = 1;
      if (this.hasSecondaryAxis(config)) {
        labelLineCount++;
      }
      const tHeight = labelLineCount * config.textPrefs.rowheight;
      bounds.height -= tHeight;
      // Clear the text object...
      testText.remove();
      // And fire off the callback
      this.props.onReturnRevisedInnerBox(bounds);
    }
  
    // SET X-AXIS CONFIG
    setPrimaryXaxisConfig() {
      const xAxis = this.props.primaryAxis;
      const config = this.props.config;
      // Scale function:
      const xScale = Object.assign({}, config.scale);
      // Number of ticks
      const tickCount = config.tickPrefs.tickCount;
      // Padding between labels and IB is rowheight minus
      // the calculated height of the text
      let tickPadding = config.textPrefs.rowheight;
      tickPadding -= (config.textPrefs.size * config.textPrefs.emVal);
      // But I have also to allow for tickLength...
      // Top or bottom:
      const orient = config.orient;
      // Tick length
      const tlPrefs = config.tickPrefs.length;
      // At this stage, it's a straight choice between
      // "across" and the default
      let tickLength = 0;
      if (tlPrefs.across) {
        tickLength = config.bounds.height;
      } else {
        // Use default here. May be overwritten in post-render
        // I'm setting just length. If start !== 0, that'll
        // (hopefully) get fixed post-render
        tickLength = tlPrefs.lengths.default.end;
        // And adjust tickPadding (between tick ends and labels)
        tickPadding -= tickLength;
        tickLength -= tlPrefs.lengths.default.start;
      }
      if (orient === 'top') {
        tickLength = -tickLength;
      }
      // Label format
      const tickFormat = this.getTickFormat(config, true);
      //
      xAxis
        .scale(xScale)
        .orient(orient)
        // Gap between labels and ticks
        .tickPadding(tickPadding)
        // Number of ticks
        .ticks(tickCount)
        // Tick length
        .tickSize(tickLength)
        // Number format
        .tickFormat(tickFormat)
        ;
      return xAxis;
    }
    // SET X-AXIS CONFIG ends
  
    // SET SECONDARY X-AXIS CONFIG
    setSecondaryXaxisConfig() {
      const xAxis = this.props.secondaryAxis;
      const config = this.props.config;
      // Scale function:
      const xScale = Object.assign({}, config.scale);
      // Number of ticks
      const tickCount = config.tickPrefs.tickCount;
      // Padding between labels and IB is rowheight minus
      // the calculated height of the text
      // NOTE: revisit this after ticks are in on primary axis...
      const tickPadding = config.textPrefs.rowheight;
      // tickPadding -= (config.textPrefs.size * config.textPrefs.emVal);
      // Top or bottom:
      const orient = config.orient;
      // Secondary axis has zero tickLength
      // NOTE: do they get drawn? Actually, I don't want them AT ALL!
      const tickLength = 0;
      // Label format
      const tickFormat = this.getTickFormat(config, false);
      //
      xAxis
        .scale(xScale)
        .orient(orient)
        // Gap between labels and ticks
        .tickPadding(tickPadding)
        // Number of ticks
        .ticks(tickCount)
        // Tick length
        .tickSize(tickLength)
        // Number format
        .tickFormat(tickFormat)
        ;
      return xAxis;
    }
    // SET X-AXIS CONFIG ends
  
    // GET AXIS GROUP TRANSFORM STRING
    // Called from updateXAxis. Returns string that determines
    // whether axis is drawn top/bottom
    getAxisGroupTransformString() {
      let height = 0;
      if (this.props.config.orient === 'bottom') {
        height = this.props.config.bounds.height;
      }
      return `translate(0,${ height })`;
    }
    // GET AXIS GROUP TRANSFORM STRING ends
  
    // UPDATE X-AXIS
    // Called directly on the DOM to update the axis
    updatePrimaryXaxis(xAxis) {
      const config = this.props.config;
      // Context: just use the specific (indexed) axis group
      // const className = config.className.split(' ')[1];
      const chartindex = config.chartindex;
      const className = `axis-labels-primary-${ chartindex }`;
      const axisGroup = Dthree.select(`.${ className }`);
      const duration = config.duration;
      const transform = this.getAxisGroupTransformString();
      const anchor = config.textPrefs.anchor[config.chartType];
      axisGroup
        // One transition on scale values
        .transition().duration(duration)
        .call(xAxis)
          // And another transition on scale top/bottom
          .transition().duration(duration)
            .attr('transform', transform)
          .selectAll('line')
            .style({
              'stroke-width': config.tickPrefs.width,
              'stroke': config.tickPrefs.stroke,
            })
          ;
      // NOTE: With multiple rows I'll probably need separate groups for each
      // NOTE: ...even separate axes? (cf Poll Tracker)
      axisGroup.selectAll('text')
        .style({
          'font-family': config.textPrefs.font,
          'font-size': config.textPrefs.size,
          'fill': config.textPrefs.fill,
          'text-anchor': anchor,
        })
      ;
      // NOTE: this is a f'rinstance of how I'd handle post-render
      // mods to ticks, allowing for, say, year change. This example
      // assumes that it's a monthly scale and won't look right until
      // we've also got 'between' ticks...
      /*
      axisGroup.selectAll('line')
        .filter((ddd) => {
          let result = false;
          if (ddd.getMonth() === 0) {
            result = true;
          }
          return result;
        })
        .transition().delay(duration * 1.5).duration(duration)
        .attr({
          'y1': -1,
          'y2': 5
        })
        .style({
          stroke: '#000',
        })
      ;
      */
    }
    // UPDATE PRIMARY X-AXIS ends
  
    // UPDATE SECONDARY X-AXIS
    updateSecondaryXaxis(secondXaxis) {
      const config = this.props.config;
      const filterArray = config.secondaryAxisFilter;
      // const className = config.className.split(' ')[1];
      const chartindex = config.chartindex;
      const className = `axis-labels-secondary-${ chartindex }`;
      const axisGroup = Dthree.select(`.${ className }`);
      const duration = config.duration;
      const transform = this.getAxisGroupTransformString();
      const anchor = config.textPrefs.anchor[config.chartType];
      const yShift = config.textPrefs.rowheight;
      const xShift = config.xShift;
      axisGroup
        // One transition on scale values
        .transition().duration(duration)
        .call(secondXaxis)
          // And another transition on scale top/bottom
  //        .filter((ddd) => (ddd.getMonth() === 0))
          .transition().duration(duration)
            .attr('transform', transform)
            // Tick projection
            .selectAll('text')
            // Set the x-shift on ALL elements before filter, since
            // that resets the element index ('iii') to count filtered
            // elements only...
            .attr('x', (ddd, iii) => {
              let val = 0;
              if (filterArray[iii].shift) {
                val = xShift;
              }
              return val;
            })
            // Filter to draw only flagged labels
            .filter((ddd, iii) => filterArray[iii].draw)
            .style({
              'font-family': config.textPrefs.font,
              'font-size': config.textPrefs.size,
              'fill': config.textPrefs.fill,
              'text-anchor': anchor,
            })
            .attr('dy', yShift)
      ;
    }
    // UPDATE SECONDARY X-AXIS ends
  
    // RENDER axis group
    // Just draw the axis group
    render() {
      const config = this.props.config;
      const gName = config.className;
      const chartindex = config.chartindex;
      // Setting no fill prevents the SVG convertor from generating a path
      // outlineing the group
      const gStyle = { fill: 'none' };
      // Axis groups are indexed, to prevent contamination between panels
      const pName = `axis-labels-primary-${ chartindex }`;
      const sName = `axis-labels-secondary-${ chartindex }`;
      // Inner groups: one or two?
      let groupJSX = (
        < g className={gName} id={'xaxis-ordinal-main-group'} style={gStyle} >
          <g className={pName} style={gStyle} />
        </g>
      );
      if (this.hasSecondaryAxis(config)) {
        groupJSX = (
          < g className={gName} id={'xaxis-ordinal-main-group'} style={gStyle} >
            <g className={pName} style={gStyle} />
            <g className={sName} style={gStyle} />
          </g>
        );
      }
      return (
        groupJSX
      );
    }
  }
  