import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import SVGIcon, { getSVGSize } from './SVGIcon';
import AccessZoneIcon from './SVGFlag.AccessZoneIcon';
import styles from './SVGFlag.styl';

const FLAG_HEIGHT_ONE_LINE = 26;
const FLAG_HEIGHT_TWO_LINES = 38;
const FLAG_OFFSET_TOP = 1;
const FLAG_OFFSET_LEFT = 2;
const ICON_OFFSET_LEFT = 2;

const ACCESS_ZONE_WIDTH = 14;
const PADDING = 4;

// We need to calculate the width of the svg text element before rendering.
// If we calculate the width after using a ref, the component will briefly flicker
// before and after ending a drag.
// This is because the dragged component is a different instance in a different layer,
// and so briefly the component without textWidth will be shown after it is instantiated.
// The calculation works by creating and measuring a temporary text node with the correct text.
function calculateTextWidth(text) {
  const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  textElement.setAttribute('opacity', 0);
  textElement.appendChild(document.createTextNode(text));
  svgElement.appendChild(textElement);
  document.body.appendChild(svgElement);
  const textWidth = textElement.getBBox().width;
  svgElement.remove();
  return textWidth;
}

export default class SVGFlag extends React.Component {
  constructor(props) {
    super(props);
    // SVG is not able to wrap a box around a text, so we need to calculate the box width ourselves.
    this.textWidth = 0;
    if (props.label) {
      this.textWidth = calculateTextWidth(props.label) + PADDING;
    }
    this.subtextWidth = 0;
    if (props.secondLabel) {
      this.subtextWidth = calculateTextWidth(props.secondLabel) + PADDING;
    }
    this.iconWidth = 0;
    if (props.iconType) {
      this.iconWidth = getSVGSize(props.iconType)[2];
    }
    this.setElement = this.setElement.bind(this);
  }

  setElement(element) {
    this.element = element;
    if (this.props.setElement) this.props.setElement(element);
  }

  calculateWidth(compact = false) {
    const { iconType, slots } = this.props;
    // this offset is needed for the shadow
    let w = FLAG_OFFSET_LEFT;
    // add icon width
    w += iconType ? (this.iconWidth + PADDING) : 0;
    if (!compact) {
      w += this.textWidth > this.subtextWidth ? this.textWidth : this.subtextWidth;
    }
    // add access zone width
    w += slots ? ACCESS_ZONE_WIDTH : 2;
    return w;
  }

  render() {
    const { iconType, filter, label, secondLabel, transform, accessZones, iconFill, isCompact = false, isMapping = false, isMapped = false, isFaded = false, slots = 0 } = this.props;

    // height is fixed for (one liner) or (two liner flags)
    let height = FLAG_HEIGHT_ONE_LINE;
    if (secondLabel) {
      height = FLAG_HEIGHT_TWO_LINES;
    }

    const classContainer = classNames({
      [styles.containerIsFaded]: isFaded
    });

    const classPolygon = classNames({
      [styles.polygonDefault]: !isMapping,
      [styles.polygonIsMapping]: isMapping
    });

    // only apply class in case no iconFill has been set
    const classIcon = !iconFill ? classNames({
      [styles.iconDefault]: !isMapped,
      [styles.iconMapped]: isMapped,
      [styles.iconIsMapping]: isMapping
    }) : null;

    const classText = classNames({
      [styles.textIsMapping]: isMapping
    });

    const drawCompact = isCompact ? isCompact({ fullWidth: this.calculateWidth() }) : false;
    const width = this.calculateWidth(drawCompact);
    const offsetXText = FLAG_OFFSET_LEFT + ICON_OFFSET_LEFT + (this.iconWidth ? this.iconWidth + PADDING : 0);

    // The ref needs to be on a non-translated g, to get the right offset coordinates when
    // calling `.getBBox()` on the element.
    // 'url(#dropshadow)'
    return (
      <g ref={this.setElement}>
        <g
          transform={transform}
          className={classContainer}
          style={{
            filter,
            cursor: 'move'
          }}
        >
          <polygon
            x="0"
            y="0"
            points={`${FLAG_OFFSET_LEFT},${FLAG_OFFSET_TOP} ${width},${FLAG_OFFSET_TOP} ${width},${height} 10,${height} 6,${height + 4} ${FLAG_OFFSET_LEFT},${height}`}
            className={classPolygon}
          />
          { iconType && (
            <svg x={FLAG_OFFSET_LEFT + ICON_OFFSET_LEFT} y="6">
              <SVGIcon type={iconType} withoutContainer className={classIcon} fill={iconFill} />
            </svg>
          )}
          {!drawCompact && label && (
            <text
              x={offsetXText}
              y="18"
              className={classText}
            >
              {label}
            </text>
          )}
          {!drawCompact && secondLabel && (
            <text
              x={offsetXText}
              y="32"
              className={classText}
            >
              {secondLabel}
            </text>
          )}
          { slots && (
            <g transform={`translate(${width - 11}, 0)`}>
              <AccessZoneIcon componentAccessZones={accessZones} slots={slots} />
            </g>
          )}
        </g>
      </g>
    );
  }
}

SVGFlag.AccessZoneIcon = AccessZoneIcon;

SVGFlag.propTypes = {
  iconType: PropTypes.string.isRequired,
  iconFill: PropTypes.string,
  label: PropTypes.string,
  filter: PropTypes.string,
  secondLabel: PropTypes.string,
  transform: PropTypes.string,
  // eslint-disable-next-line react/forbid-prop-types
  accessZones: PropTypes.object,
  isCompact: PropTypes.func,
  isFaded: PropTypes.bool,
  isMapping: PropTypes.bool,
  isMapped: PropTypes.bool,
  slots: PropTypes.number
};

SVGFlag.defaultProps = {
  iconFill: undefined,
  label: undefined,
  filter: undefined,
  secondLabel: undefined,
  transform: undefined,
  accessZones: undefined,
  isCompact: undefined,
  isFaded: undefined,
  isMapping: undefined,
  isMapped: undefined,
  slots: undefined
};
