import { checkIsNodeStyleDataKey } from '../../../utils/index'
import { ThemeConfig } from "../../../theme"
const backgroundStyleProps = [
  'backgroundColor',
  'backgroundImage',
  'backgroundRepeat',
  'backgroundPosition',
  'backgroundSize'
]

//  样式类
class Style {
  private static cacheStyle: Record<string, string> | null = null;
  private ctx: any;
  private _markerPath: any;
  private _marker: any;
  private _gradient: any;

  static setBackgroundStyle(el: HTMLElement, themeConfig: ThemeConfig): void {
    if (!el) return;

    // Cache container element's original style
    if (!Style.cacheStyle) {
      Style.cacheStyle = {};
      const style = window.getComputedStyle(el);
      backgroundStyleProps.forEach(prop => {
        Style.cacheStyle![prop] = style[prop as any];
      });
    }

    const {
      backgroundColor,
      backgroundImage,
      backgroundRepeat,
      backgroundPosition,
      backgroundSize
    } = themeConfig;

    el.style.backgroundColor = backgroundColor;
    if (backgroundImage && backgroundImage !== 'none') {
      el.style.backgroundImage = `url(${backgroundImage})`;
      el.style.backgroundRepeat = backgroundRepeat || '';
      el.style.backgroundPosition = backgroundPosition || '';
      el.style.backgroundSize = backgroundSize || '';
    } else {
      el.style.backgroundImage = 'none';
    }
  }

  static removeBackgroundStyle(el: HTMLElement): void {
    if (!Style.cacheStyle) return;
    backgroundStyleProps.forEach(prop => {
      el.style[prop as any] = Style.cacheStyle![prop];
    });
    Style.cacheStyle = null;
  }

  constructor(ctx: any) {
    this.ctx = ctx;
    this._markerPath = null;
    this._marker = null;
    this._gradient = null;
  }

  merge(prop: string, root?: boolean): any {
    const themeConfig = this.ctx.mindMap.themeConfig;
    let defaultConfig: any = null;
    const useRoot = root;

    if (useRoot) {
      defaultConfig = themeConfig;
    } else if (this.ctx.isGeneralization) {
      defaultConfig = themeConfig.generalization;
    } else if (this.ctx.layerIndex === 0) {
      defaultConfig = themeConfig.root;
    } else if (this.ctx.layerIndex === 1) {
      defaultConfig = themeConfig.second;
    } else {
      defaultConfig = themeConfig.node;
    }

    let value: any = '';
    if (this.getSelfStyle(prop) !== undefined) {
      value = this.getSelfStyle(prop);
    } else if (defaultConfig[prop] !== undefined) {
      value = defaultConfig[prop];
    } else {
      value = themeConfig[prop];
    }

    if (!useRoot) {
      this.addToEffectiveStyles({
        [prop]: value
      });
    }
    return value;
  }

  getStyle(prop: string, root?: boolean): any {
    return this.merge(prop, root);
  }

  getSelfStyle(prop: string): any {
    return this.ctx.getData(prop);
  }

  addToEffectiveStyles(styles: Record<string, any>): void {
    this.ctx.effectiveStyles = {
      ...this.ctx.effectiveStyles,
      ...styles
    };
  }

  rect(node: any): void {
    this.shape(node);
    node.radius(this.merge('borderRadius'));
  }

  shape(node: any): void {
    const styles = {
      gradientStyle: this.merge('gradientStyle'),
      startColor: this.merge('startColor'),
      endColor: this.merge('endColor'),
      startDir: this.merge('startDir'),
      endDir: this.merge('endDir'),
      fillColor: this.merge('fillColor'),
      borderColor: this.merge('borderColor'),
      borderWidth: this.merge('borderWidth'),
      borderDasharray: this.merge('borderDasharray')
    };

    if (styles.gradientStyle) {
      if (!this._gradient) {
        this._gradient = this.ctx.nodeDraw.gradient('linear');
      }
      this._gradient.update((add: any) => {
        add.stop(0, styles.startColor);
        add.stop(1, styles.endColor);
      });
      this._gradient.from(...styles.startDir).to(...styles.endDir);
      node.fill(this._gradient);
    } else {
      node.fill({
        color: styles.fillColor
      });
    }

    node.stroke({
      color: styles.borderColor,
      width: styles.borderWidth,
      dasharray: styles.borderDasharray
    });
  }

  text(node: any): void {
    const styles = {
      color: this.merge('color'),
      fontFamily: this.merge('fontFamily'),
      fontSize: this.merge('fontSize'),
      fontWeight: this.merge('fontWeight'),
      fontStyle: this.merge('fontStyle'),
      textDecoration: this.merge('textDecoration')
    };

    node
      .fill({
        color: styles.color
      })
      .css({
        'font-family': styles.fontFamily,
        'font-size': `${styles.fontSize}px`,
        'font-weight': styles.fontWeight,
        'font-style': styles.fontStyle,
        'text-decoration': styles.textDecoration
      });
  }


  // 生成内联样式
  createStyleText(customStyle = {}) {
    const styles = {
      color: this.merge('color'),
      fontFamily: this.merge('fontFamily'),
      fontSize: this.merge('fontSize'),
      fontWeight: this.merge('fontWeight'),
      fontStyle: this.merge('fontStyle'),
      textDecoration: this.merge('textDecoration'),
      ...customStyle
    }
    return `
      color: ${styles.color};
      font-family: ${styles.fontFamily};
      font-size: ${styles.fontSize + 'px'};
      font-weight: ${styles.fontWeight};
      font-style: ${styles.fontStyle};
      text-decoration: ${styles.textDecoration}
    `
  }

  // 获取文本样式
  getTextFontStyle() {
    const styles = {
      color: this.merge('color'),
      fontFamily: this.merge('fontFamily'),
      fontSize: this.merge('fontSize'),
      fontWeight: this.merge('fontWeight'),
      fontStyle: this.merge('fontStyle'),
      textDecoration: this.merge('textDecoration')
    }
    return {
      italic: styles.fontStyle === 'italic',
      bold: styles.fontWeight,
      fontSize: styles.fontSize,
      fontFamily: styles.fontFamily
    }
  }

  //  html文字节点
  domText(node: HTMLElement, fontSizeScale: number = 1): void {
    const styles = {
      color: this.merge('color'),
      fontFamily: this.merge('fontFamily'),
      fontSize: this.merge('fontSize'),
      fontWeight: this.merge('fontWeight'),
      fontStyle: this.merge('fontStyle'),
      textDecoration: this.merge('textDecoration')
    }
    node.style.color = styles.color
    node.style.textDecoration = styles.textDecoration
    node.style.fontFamily = styles.fontFamily
    node.style.fontSize = `${styles.fontSize * fontSizeScale}px`
    node.style.fontWeight = styles.fontWeight || 'normal'
    node.style.fontStyle = styles.fontStyle
  }

  tagText(node: any, style: { fontSize: number }): void {
    node
      .fill({
        color: '#fff'
      })
      .css({
        'font-size': `${style.fontSize}px`
      })
  }

  tagRect(node: any, style: { fill: string; radius?: number }): void {
    node.fill({
      color: style.fill
    })
    if (style.radius) {
      node.radius(style.radius)
    }
  }

  iconNode(node: any, color?: string): void {
    node.attr({
      fill: color || this.merge('color')
    })
  }

  line(
    line: any,
    { width, color, dasharray }: { width?: number; color?: string; dasharray?: string } = {},
    enableMarker?: boolean,
    childNode?: any
  ): void {
    const { customHandleLine } = this.ctx.mindMap.opt
    if (typeof customHandleLine === 'function') {
      customHandleLine(this.ctx, line, { width, color, dasharray })
    }
    line.stroke({ color, dasharray, width }).fill({ color: 'none' })

    if (enableMarker) {
      const showMarker = this.merge('showLineMarker', true)
      const childNodeStyle = childNode.style

      if (showMarker) {
        childNodeStyle._marker = childNodeStyle._marker || childNodeStyle.createMarker()
        childNodeStyle._markerPath.stroke({ color }).fill({ color })
        line.attr('marker-start', '')
        line.attr('marker-end', '')
        const dir = childNodeStyle.merge('lineMarkerDir')
        line.marker(dir, childNodeStyle._marker)
      } else if (childNodeStyle._marker) {
        line.attr('marker-start', '')
        line.attr('marker-end', '')
        childNodeStyle._marker.remove()
        childNodeStyle._marker = null
      }
    }
  }

  createMarker(): any {
    return this.ctx.lineDraw.marker(20, 20, (add: any) => {
      add.ref(8, 5)
      add.size(20, 20)
      add.attr('markerUnits', 'userSpaceOnUse')
      add.attr('orient', 'auto-start-reverse')
      this._markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z')
    })
  }

  generalizationLine(node: any): void {
    node
      .stroke({
        width: this.merge('generalizationLineWidth', true),
        color: this.merge('generalizationLineColor', true)
      })
      .fill({ color: 'none' })
  }

  iconBtn(node: any, node2: any, fillNode: any): void {
    const { color, fill, fontSize, fontColor } = this.ctx.mindMap.opt
      .expandBtnStyle || {
      color: '#808080',
      fill: '#fff',
      fontSize: 12,
      strokeColor: '#333333',
      fontColor: '#333333'
    }
    node.fill({ color: color })
    node2.fill({ color: color })
    fillNode.fill({ color: fill })
    if (this.ctx.mindMap.opt.isShowExpandNum) {
      node.attr({ 'font-size': `${fontSize}px`, 'font-color': fontColor })
    }
  }

  hasCustomStyle(): boolean {
    let res = false
    Object.keys(this.ctx.getData()).forEach(item => {
      if (checkIsNodeStyleDataKey(item)) {
        res = true
      }
    })
    return res
  }

  getCustomStyle(): Record<string, any> {
    const customStyle: Record<string, any> = {}
    Object.keys(this.ctx.getData()).forEach(item => {
      if (checkIsNodeStyleDataKey(item)) {
        customStyle[item] = this.ctx.getData(item)
      }
    })
    return customStyle
  }

  hoverNode(node: any): void {
    const hoverRectColor =
      this.merge('hoverRectColor') || this.ctx.mindMap.opt.hoverRectColor
    const hoverRectRadius = this.merge('hoverRectRadius')
    node.radius(hoverRectRadius).fill('none').stroke({
      color: hoverRectColor
    })
  }

  onRemove(): void {
    if (this._marker) {
      this._marker.remove()
      this._marker = null
    }
    if (this._markerPath) {
      this._markerPath.remove()
      this._markerPath = null
    }
    if (this._gradient) {
      this._gradient.remove()
      this._gradient = null
    }
  }


}

export default Style
