import React, { Component } from 'react';
import PropTypes from 'prop-types';
import reduceCSSCalc from 'reduce-css-calc';
import getStringWidth from './util/getStringWidth';

class Text extends Component {

  static propTypes = {
    scaleToFit: PropTypes.bool,
    angle: PropTypes.number,
    textAnchor: PropTypes.oneOf(['start', 'middle', 'end', 'inherit']),
    verticalAnchor: PropTypes.oneOf(['start', 'middle', 'end']),
    style: PropTypes.object,
  };

  static defaultProps = {
    x: 0,
    y: 0,
    dx: 0,
    dy: 0,
    lineHeight: '1em',
    capHeight: '0.71em', // Magic number from d3
    scaleToFit: false,
    textAnchor: 'start',
    verticalAnchor: 'end', // default SVG behavior
  };

  state = {
    wordsByLines: [],
  };

  componentWillMount() {
    this.updateWordsByLines(this.props, true);
  }

  componentWillReceiveProps(nextProps) {
    const needCalculate = (
      this.props.children !== nextProps.children ||
      this.props.style !== nextProps.style
    );
    this.updateWordsByLines(nextProps, needCalculate);
  }

  updateWordsByLines(props, needCalculate) {
    // Only perform calculations if using features that require them (multiline, scaleToFit)
    if ((props.width || props.scaleToFit)) {
      if (needCalculate) {
        const words = props.children ? props.children.toString().split(/\s+/) : [];
    
        this.wordsWithComputedWidth = words.map(word => (
          { word, width: getStringWidth(word, props.style) }
        ));
        this.spaceWidth = getStringWidth('\u00A0', props.style);
      }

      const wordsByLines = this.calculateWordsByLines(
        this.wordsWithComputedWidth,
        this.spaceWidth,
        props.width
      );
      this.setState({ wordsByLines });
    } else {
      this.updateWordsWithoutCalculate(props);
    }
  }

  updateWordsWithoutCalculate(props) {
    const words = props.children ? props.children.toString().split(/\s+/) : [];
    this.setState({ wordsByLines: [{ words }] });
  }

  calculateWordsByLines(wordsWithComputedWidth, spaceWidth, lineWidth) {
    const { scaleToFit } = this.props;
    return wordsWithComputedWidth.reduce((result, { word, width }) => {
      const currentLine = result[result.length - 1];

      if (currentLine && (lineWidth == null || scaleToFit ||
        (currentLine.width + width + spaceWidth) < lineWidth)) {
        // Word can be added to an existing line
        currentLine.words.push(word);
        currentLine.width += width + spaceWidth;
      } else {
        // Add first word to line or word is too long to scaleToFit on existing line
        const newLine = { words: [word], width };
        result.push(newLine);
      }

      return result;
    }, []);
  }

  render() {
    const {
      dx,
      dy,
      textAnchor,
      verticalAnchor,
      scaleToFit,
      angle,
      lineHeight,
      capHeight,
      ...textProps
    } = this.props;
    const { wordsByLines } = this.state;

    const x = textProps.x;
    const y = textProps.y;

    let startDy;
    switch (verticalAnchor) {
      case 'start':
        startDy = reduceCSSCalc(`calc(${capHeight})`);
        break;
      case 'middle':
        startDy = reduceCSSCalc(
          `calc(${(wordsByLines.length - 1) / 2} * -${lineHeight} + (${capHeight} / 2))`
        );
        break;
      default:
        startDy = reduceCSSCalc(`calc(${wordsByLines.length - 1} * -${lineHeight})`);
        break;
    }

    const transforms = [];
    if (scaleToFit && wordsByLines.length) {
      const lineWidth = wordsByLines[0].width;
      const sx = this.props.width / lineWidth;
      const sy = sx;
      const originX = x - sx * x;
      const originY = y - sy * y;
      transforms.push(`matrix(${sx}, 0, 0, ${sy}, ${originX}, ${originY})`);
    }
    if (angle) {
      transforms.push(`rotate(${angle}, ${x}, ${y})`);
    }
    if (transforms.length) {
      textProps.transform = transforms.join(' ');
    }

    return (
      <text
        {...textProps}
        textAnchor={textAnchor}
        style={{
          transform: `translate(${dx}, ${dy})`,
          ...textProps.style
        }}
      >
        {
        wordsByLines.map((line, index) => (
          <tspan x={x} dy={index === 0 ? startDy : lineHeight} key={index}>
            {line.words.join(' ')}
          </tspan>
        ))
      }
      </text>
    );
  }
}

export default Text;
@@ -3953,132 +3953,102 @@
    %3C
-text%0A        %7B...textProps%7D%0A        textAnchor=%7BtextAnchor%7D%0A        style=%7B%7B%0A          transform: %60translate($%7Bdx%7D, $%7Bdy%7D)%60,
+svg x=%7Bdx%7D y=%7Bdy%7D fontSize=%7BtextProps.fontSize%7D style=%7B%7B overflow: 'visible' %7D%7D%3E%0A        %3Ctext
 %0A   
@@ -4050,24 +4050,25 @@
 t%0A          
+%7B
 ...textProps
@@ -4063,30 +4063,25 @@
 ...textProps
-.style
+%7D
 %0A        %7D%7D%0A
@@ -4081,11 +4081,36 @@
     
-%7D%7D%0A
+  textAnchor=%7BtextAnchor%7D%0A  
     
@@ -4121,18 +4121,22 @@
         
-%7B%0A
+  %7B%0A  
         
@@ -4181,16 +4181,18 @@
         
+ 
+ 
 %3Ctspan x
@@ -4261,16 +4261,18 @@
         
+  
 %7Bline.wo
@@ -4296,16 +4296,18 @@
         
+  
 %3C/tspan%3E
@@ -4307,16 +4307,18 @@
 /tspan%3E%0A
+  
         
@@ -4322,26 +4322,30 @@
    ))%0A      
-%7D%0A
+  %7D%0A  
       %3C/text
@@ -4346,16 +4346,29 @@
 %3C/text%3E%0A
+      %3C/svg%3E%0A
     );%0A 
@@ -4393,8 +4393,9 @@
 lt Text;
+%0A

use wrapping svg for dx and dy