import Color from 'color';
import { Subject } from 'rxjs';
import { SvgGroup } from './d3.utils';

export interface Station {
  x: number;
  y: number;
  labelX: number;
  labelY: number;
  name: string;
  endpoint: string;
  type: 'complex' | 'simple';
  // 如果是两行文字，使用 subLabel
  subLabel?: {
    x: number;
    y: number;
    text: string;
  };
  disabled?: boolean;
}

export class StationD3Model {
  meta: Station;
  type: 'complex' | 'simple';
  color: string;
  private stationGroup!: SvgGroup;
  private parentGroup: SvgGroup;
  private stationEventSubject: Subject<{ type: 'click'; station: StationD3Model }>;
  // 箭头偏移量
  private readonly arrowOffset = 5;

  shiningDom: SvgGroup | null;

  private _selected = false;

  get name() {
    return this.meta.name + (this.meta.subLabel ? this.meta.subLabel.text : '');
  }

  get selected() {
    return this._selected;
  }
  set selected(value: boolean) {
    this._selected = value;
    this.stationGroup.classed('selected', value);
  }

  hazardIds: number[] = [];
  riskItemIds: number[] = [];

  constructor(
    meta: Station,
    parentGroup: SvgGroup,
    stationEventSubject: Subject<{ type: 'click'; station: StationD3Model }>
  ) {
    this.meta = meta;
    this.type = meta.type;
    this.color = '#FD6D1F';
    this.parentGroup = parentGroup;
    this.shiningDom = null;
    this.stationEventSubject = stationEventSubject;
  }

  get isShining() {
    return this.shiningDom !== null;
  }
  set isShining(value: boolean) {
    value ? this.shining() : this.unShining();
  }

  render() {
    this.stationGroup = this.parentGroup
      .append('g')
      .attr('class', `station-group ${this.meta.type}-station ${this.meta.disabled ? 'disabled' : ''}`);
    if (this.meta.type === 'complex') {
      this.renderComplexStation(this.stationGroup, this.meta);
    } else {
      this.renderSimpleStation(this.stationGroup, this.meta);
    }
    this.renderStationLabel(this.stationGroup, this.meta);
    this.addEvent();
  }
  addEvent() {
    this.stationGroup.on('click', () => {
      // this.selected = !this.selected;
      this.stationEventSubject.next({ type: 'click', station: this });
    });
  }
  shining() {
    const color = this.color || '#FD6D1F';
    if (this.type === 'complex') {
      this.shiningDom = this.renderComplexShining(color);
    } else {
      this.shiningDom = this.renderSimpleShining(color);
    }
  }
  unShining() {
    this.shiningDom?.remove();
    this.shiningDom = null;
  }
  renderComplexStation(stationGroup: SvgGroup, meta: Station) {
    stationGroup.append('circle').attr('cx', meta.x).attr('cy', meta.y).attr('class', 'station-circle').attr('r', 13);
    stationGroup
      .append('circle')
      .attr('cx', meta.x)
      .attr('cy', meta.y)
      .attr('r', 12)
      .attr('style', 'fill: none; stroke: #b2bbc2; stroke-width: 2px');
    stationGroup
      .append('path')
      .attr('d', this.getTopArrowPath(meta.x, meta.y))
      .attr('class', 'arrow-path')
      .attr('style', 'fill-rule: evenodd');
    stationGroup
      .append('path')
      .attr('d', this.getBottomArrowPath(meta.x, meta.y))
      .attr('class', 'arrow-path')
      .attr('style', 'fill-rule: evenodd');
  }
  renderComplexShining(color: string) {
    const group = this.stationGroup;
    const shine = group
      .append('g')
      .attr('class', 'pointer-events-none')
      .attr('transform', `translate(${this.meta.x - 23}, ${this.meta.y - 23})`); // 将动画中心点移到站点位置

    // const circle0 = shine.append('circle');
    // circle0.attr('cx', 23).attr('cy', 23).attr('r', 26).attr('fill', color).attr('fill-opacity', 0.2);
    // this.addAnimation(circle0, 'r', 24, 28, '1.5s', 'indefinite');
    // this.addAnimation(circle0, 'fill-opacity', 0.2, 0, '1.5s', 'indefinite');

    const circle1 = shine.append('circle');
    circle1.attr('cx', 23).attr('cy', 23).attr('r', 23).attr('fill', color).attr('fill-opacity', 0.3);
    this.addAnimation(circle1, 'r', 20, 24, '1.5s', 'indefinite');
    this.addAnimation(circle1, 'fill-opacity', 0.3, 0, '1.5s', 'indefinite');

    const circle2 = shine.append('circle');
    circle2.attr('cx', 23).attr('cy', 23).attr('r', 18).attr('fill', color).attr('fill-opacity', 0.4);
    this.addAnimation(circle2, 'r', 15, 20, '1.5s', 'indefinite');
    this.addAnimation(circle2, 'fill-opacity', 0.4, 0, '1.5s', 'indefinite');

    const circle3 = shine.append('circle');
    circle3
      .attr('cx', 23)
      .attr('cy', 23)
      .attr('r', 18)
      .attr('fill', 'url(#gradient-opacity-complex)')
      .attr('fill-opacity', 0.5);
    this.addAnimation(circle3, 'fill-opacity', 0.5, 0, '1.5s', 'indefinite');

    const circle4 = shine.append('circle');
    circle4.attr('cx', 23).attr('cy', 23).attr('r', 13).attr('fill', color).attr('fill-opacity', 1);
    this.addAnimation(circle4, 'fill-opacity', 1, 0.8, '1.5s', 'indefinite');

    const circle5 = shine.append('circle');
    circle5
      .attr('cx', 23)
      .attr('cy', 23)
      .attr('r', 12)
      .attr('fill-opacity', 0)
      .attr('stroke-opacity', 0.5)
      .attr('stroke', Color(color).lighten(0.4).hex())
      .attr('fill', 'none')
      .attr('stroke-width', 2);
    this.addAnimation(circle5, 'stroke-opacity', 0.5, 1, '1.5s', 'indefinite');
    group
      .append('path')
      .attr('d', this.getTopArrowPath(this.meta.x, this.meta.y))
      .attr('style', 'fill: #FFFFFF; fill-rule: evenodd');
    group
      .append('path')
      .attr('d', this.getBottomArrowPath(this.meta.x, this.meta.y))
      .attr('style', 'fill: #FFFFFF; fill-rule: evenodd');

    return shine;
  }
  renderSimpleShining(color: string) {
    const group = this.stationGroup;
    const shine = group
      .append('g')
      .attr('class', 'pointer-events-none')
      .attr('transform', `translate(${this.meta.x - 20}, ${this.meta.y - 20})`); // 将动画中心点移到站点位置

    const circle1 = shine.append('circle');
    circle1.attr('cx', 20).attr('cy', 20).attr('r', 20).attr('fill', color).attr('fill-opacity', 0.2);
    this.addAnimation(circle1, 'r', 18, 28, '1.5s', 'indefinite');
    this.addAnimation(circle1, 'fill-opacity', 0.2, 0, '1.5s', 'indefinite');

    const circle2 = shine.append('circle');
    circle2.attr('cx', 20).attr('cy', 20).attr('r', 15).attr('fill', color).attr('fill-opacity', 0.5);
    this.addAnimation(circle2, 'r', 13, 20, '1.5s', 'indefinite');
    this.addAnimation(circle2, 'fill-opacity', 0.5, 0, '1.5s', 'indefinite');

    const circle3 = shine.append('circle');
    circle3
      .attr('cx', 20)
      .attr('cy', 20)
      .attr('r', 15)
      .attr('fill', 'url(#gradient-opacity-simple)')
      .attr('fill-opacity', 0.5);
    this.addAnimation(circle3, 'fill-opacity', 0.5, 0.2, '1.5s', 'indefinite');

    const circle4 = shine.append('circle');
    circle4.attr('cx', 20).attr('cy', 20).attr('r', 10).attr('fill', color).attr('fill-opacity', 1);
    this.addAnimation(circle4, 'fill-opacity', 1, 0.8, '1.5s', 'indefinite');

    const circle5 = shine.append('circle');
    circle5
      .attr('cx', 20)
      .attr('cy', 20)
      .attr('r', 8)
      .attr('fill-opacity', 0)
      .attr('stroke-opacity', 0.5)
      .attr('stroke', Color(color).lighten(0.4).hex())
      .attr('fill', 'none')
      .attr('stroke-width', 4);
    this.addAnimation(circle5, 'stroke-opacity', 0.5, 1, '1.5s', 'indefinite');

    return shine;
  }
  renderSimpleStation(stationGroup: SvgGroup, meta: Station) {
    stationGroup
      .append('circle')
      .attr('cx', meta.x)
      .attr('cy', meta.y)
      .attr('r', 8)
      .attr('class', `station-circle ${meta.disabled ? 'disabled' : ''}`);

    stationGroup.append('path').attr('d', this.getSimpleDotPath(meta.x, meta.y)).attr('class', 'arrow-path');
  }
  renderStationLabel(stationGroup: SvgGroup, meta: Station) {
    const labelGroup = stationGroup.append('g').attr('class', `station-label-group ${meta.disabled ? 'disabled' : ''}`);
    labelGroup
      .append('text')
      .attr('x', meta.labelX)
      .attr('y', meta.labelY)
      .attr('class', `station-label ${meta.disabled ? 'disabled' : ''}`)
      .text(meta.name);
    if (meta.subLabel) {
      labelGroup
        .append('text')
        .attr('x', meta.subLabel.x)
        .attr('y', meta.subLabel.y)
        .attr('class', `station-label ${meta.disabled ? 'disabled' : ''}`)
        .text(meta.subLabel.text);
    }
  }

  addAnimation(
    circle: d3.Selection<SVGCircleElement, unknown, null, undefined>,
    attributeName: string,
    from: number,
    to: number,
    dur: string,
    repeatCount: string
  ) {
    circle
      .append('animate')
      .attr('attributeName', attributeName)
      .attr('from', from)
      .attr('to', to)
      // .attr('values', `${from};${to};${from}${to}`)
      .attr('dur', dur)
      .attr('begin', dur)
      // .attr('keyTimes', '0;0.8;1')
      .attr('repeatCount', repeatCount);
  }

  getTopArrowPath(x: number, y: number): string {
    const baseX = x - 5.2 + this.arrowOffset;
    const baseY = y - 1.8;
    return `M${baseX + 5.2},${baseY - 1.8}l-1.5-.8-.9,1.8,4.7,2.3v-1.6c0-1-.2-2-.6-2.9-.4-.9-.9-1.7-1.6-2.4-.7-.7-1.5-1.2-2.4-1.6-.9-.4-1.9-.6-2.9-.6-1,0-2,.2-2.9.6-.9.4-1.7.9-2.4,1.6-.7.7-1.2,1.5-1.6,2.4l1.8.8c.3-.7.7-1.3,1.2-1.8.5-.5,1.1-.9,1.8-1.2.7-.3,1.4-.4,2.1-.4.7,0,1.4.1,2.1.4.7.3,1.3.7,1.8,1.2.5.5.9,1.1,1.2,1.8,0,.1,0,.2.1.3Z`;
  }

  getBottomArrowPath(x: number, y: number): string {
    const baseX = x - 5.2 + this.arrowOffset;
    const baseY = y + 1.7;
    return `M${baseX - 5.2},${baseY + 1.7}l1.5.8.9-1.8-4.7-2.3v1.6c0,1,.2,2,.6,2.9.4.9.9,1.7,1.6,2.4.7.7,1.5,1.2,2.4,1.6.9.4,1.9.6,2.9.6s2-.2,2.9-.6,1.7-.9,2.4-1.6c.7-.7,1.2-1.5,1.6-2.4l-1.8-.8c-.3.7-.7,1.3-1.2,1.8s-1.1.9-1.8,1.2c-.7.3-1.4.4-2.1.4s-1.4-.1-2.1-.4c-.7-.3-1.3-.7-1.8-1.2-.5-.5-.9-1.1-1.2-1.8,0-.1,0-.2-.1-.3Z`;
  }

  getSimpleDotPath(x: number, y: number): string {
    return `M${x},${y - 6}c3.3,0,6,2.7,6,6s-2.7,6-6,6-6-2.7-6-6,2.7-6,6-6M${x},${y - 10}c-5.5,0-10,4.5-10,10s4.5,10,10,10,10-4.5,10-10-4.5-10-10-10h0Z`;
  }
}
