// 定义旋转方向枚举
export enum Direction {
  CLOCKWISE,
  ANTICLOCKWISE
}

// 定义辐条结构体
interface Spoke {
  StartPoint: { x: number; y: number };
  EndPoint: { x: number; y: number };
}

export class SprocketControl {
  // 常量
  private static readonly DEFAULT_INTERVAL = 60;
  private static readonly DEFAULT_TICK_COLOR = { r: 58, g: 58, b: 58, a: 255 };
  private static readonly DEFAULT_TICK_WIDTH = 3;
  private static readonly DEFAULT_TICK_COUNT = 12;
  private static readonly MINIMUM_INNER_RADIUS = 5;
  private static readonly MINIMUM_OUTER_RADIUS = 8;
  private static readonly MINIMUM_CONTROL_SIZE = { width: 28, height: 28 };
  private static readonly MINIMUM_PEN_WIDTH = 2;
  private static readonly DEFAULT_START_ANGLE = 270;
  private static readonly INNER_RADIUS_FACTOR = 0.175;
  private static readonly OUTER_RADIUS_FACTOR = 0.3125;
  private static readonly ALPHA_UPPER_LIMIT = 250;
  private static readonly ALPHA_LOWER_LIMIT = 0;
  private static readonly ALPHA_TICK_PERCENTAGE_LOWER_LIMIT = 10;
  private static readonly DEFAULT_PROGRESS_ALPHA = 10;
  private static readonly DEFAULT_PROGRESS = 0.0;

  // 字段
  private centerPoint = { x: 0, y: 0 };
  private innerRadius = 0;
  private outerRadius = 0;
  private alphaChange = 0;
  private angleIncrement = 0;
  private renderStartAngle = 0;
  private renderTimer: number | null = null;
  private spokes: Spoke[] = [];

  // 依赖属性
  private _interval = SprocketControl.DEFAULT_INTERVAL;
  private _isIndeterminate = true;
  private _progress = SprocketControl.DEFAULT_PROGRESS;
  private _rotation = Direction.CLOCKWISE;
  private _startAngle = SprocketControl.DEFAULT_START_ANGLE;
  private _tickColor = SprocketControl.DEFAULT_TICK_COLOR;
  private _tickCount = SprocketControl.DEFAULT_TICK_COUNT;
  private _tickStyle: CanvasLineCap = 'round';
  private _tickWidth = SprocketControl.DEFAULT_TICK_WIDTH;
  private _lowestAlpha = SprocketControl.ALPHA_LOWER_LIMIT;
  private _alphaTicksPercentage = 100.0;

  // 构造函数
  constructor(private canvas: HTMLCanvasElement) {
    this.calculateSpokesPoints();
    this.canvas.addEventListener('resize', () => this.onRenderSizeChanged());
  }

  // 属性访问器
  get Interval() {
    return this._interval;
  }
  set Interval(value: number) {
    const oldValue = this._interval;
    this._interval = value;
    this.onIntervalChanged(oldValue, value);
  }

  get IsIndeterminate() {
    return this._isIndeterminate;
  }
  set IsIndeterminate(value: boolean) {
    const oldValue = this._isIndeterminate;
    this._isIndeterminate = value;
    this.onIsIndeterminateChanged(oldValue, value);
  }

  get Progress() {
    return this._progress;
  }
  set Progress(value: number) {
    const coercedValue = this.coerceProgress(value);
    const oldValue = this._progress;
    this._progress = coercedValue;
    this.onProgressChanged(oldValue, coercedValue);
  }

  get Rotation() {
    return this._rotation;
  }
  set Rotation(value: Direction) {
    const oldValue = this._rotation;
    this._rotation = value;
    this.onRotationChanged(oldValue, value);
  }

  get StartAngle() {
    return this._startAngle;
  }
  set StartAngle(value: number) {
    const oldValue = this._startAngle;
    this._startAngle = value;
    this.onStartAngleChanged(oldValue, value);
  }

  get TickColor() {
    return this._tickColor;
  }
  set TickColor(value: { r: number; g: number; b: number; a: number }) {
    const oldValue = this._tickColor;
    this._tickColor = value;
    this.onTickColorChanged(oldValue, value);
  }

  get TickCount() {
    return this._tickCount;
  }
  set TickCount(value: number) {
    const coercedValue = this.coerceTickCount(value);
    const oldValue = this._tickCount;
    this._tickCount = coercedValue;
    this.onTickCountChanged(oldValue, coercedValue);
  }

  get TickStyle() {
    return this._tickStyle;
  }
  set TickStyle(value: CanvasLineCap) {
    const oldValue = this._tickStyle;
    this._tickStyle = value;
    this.onTickStyleChanged(oldValue, value);
  }

  get TickWidth() {
    return this._tickWidth;
  }
  set TickWidth(value: number) {
    const coercedValue = this.coerceTickWidth(value);
    const oldValue = this._tickWidth;
    this._tickWidth = coercedValue;
    this.onTickWidthChanged(oldValue, coercedValue);
  }

  get LowestAlpha() {
    return this._lowestAlpha;
  }
  set LowestAlpha(value: number) {
    const coercedValue = this.coerceLowestAlpha(value);
    const oldValue = this._lowestAlpha;
    this._lowestAlpha = coercedValue;
    this.onLowestAlphaChanged(oldValue, coercedValue);
  }

  get AlphaTicksPercentage() {
    return this._alphaTicksPercentage;
  }
  set AlphaTicksPercentage(value: number) {
    const coercedValue = this.coerceAlphaTicksPercentage(value);
    const oldValue = this._alphaTicksPercentage;
    this._alphaTicksPercentage = coercedValue;
    this.onAlphaTicksPercentageChanged(oldValue, coercedValue);
  }

  // 辅助方法
  private start() {
    if (!this.renderTimer) {
      this.renderTimer = window.setInterval(() => this.onRenderTimerElapsed(), this._interval);
    }
  }

  private stop() {
    if (this.renderTimer) {
      window.clearInterval(this.renderTimer);
      this.renderTimer = null;
    }
  }

  private convertDegreesToRadians(degrees: number) {
    return (Math.PI / 180) * degrees;
  }

  private calculateSpokesPoints() {
    this.spokes = [];
    this.angleIncrement = 360 / this._tickCount;
    this.alphaChange = (255 - this._lowestAlpha) / ((this._alphaTicksPercentage / 100) * this._tickCount);
    this.renderStartAngle = this._startAngle;

    const width = Math.min(this.canvas.width, this.canvas.height);
    this.centerPoint = { x: this.canvas.width / 2, y: this.canvas.height / 2 };
    this.innerRadius = width * SprocketControl.INNER_RADIUS_FACTOR;
    if (this.innerRadius < SprocketControl.MINIMUM_INNER_RADIUS) {
      this.innerRadius = SprocketControl.MINIMUM_INNER_RADIUS;
    }
    this.outerRadius = width * SprocketControl.OUTER_RADIUS_FACTOR;
    if (this.outerRadius < SprocketControl.MINIMUM_OUTER_RADIUS) {
      this.outerRadius = SprocketControl.MINIMUM_OUTER_RADIUS;
    }

    let angle = 0;
    for (let i = 0; i < this._tickCount; i++) {
      const pt1 = {
        x: this.innerRadius * Math.cos(this.convertDegreesToRadians(angle)),
        y: this.innerRadius * Math.sin(this.convertDegreesToRadians(angle))
      };
      const pt2 = {
        x: this.outerRadius * Math.cos(this.convertDegreesToRadians(angle)),
        y: this.outerRadius * Math.sin(this.convertDegreesToRadians(angle))
      };
      this.spokes.push({ StartPoint: pt1, EndPoint: pt2 });

      if (!this._isIndeterminate) {
        angle += this.angleIncrement;
      } else {
        if (this._rotation === Direction.CLOCKWISE) {
          angle -= this.angleIncrement;
        } else {
          angle += this.angleIncrement;
        }
      }
    }
  }

  // 重写方法
  private onRenderSizeChanged() {
    this.calculateSpokesPoints();
    this.render();
  }

  private render() {
    const ctx = this.canvas.getContext('2d');
    if (!ctx || !this.spokes) return;

    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    ctx.translate(this.centerPoint.x, this.centerPoint.y);
    ctx.rotate(this.convertDegreesToRadians(this.renderStartAngle));

    let alpha = 255;
    const progressSpokes = Math.floor((this._progress * this._tickCount) / 100);

    for (let i = 0; i < this._tickCount; i++) {
      if (!this._isIndeterminate) {
        if (progressSpokes > 0) {
          alpha = i < progressSpokes ? 255 : SprocketControl.DEFAULT_PROGRESS_ALPHA;
        } else {
          alpha = SprocketControl.DEFAULT_PROGRESS_ALPHA;
        }
      }

      ctx.strokeStyle = `rgba(${this._tickColor.r}, ${this._tickColor.g}, ${this._tickColor.b}, ${alpha / 255})`;
      ctx.lineWidth = this._tickWidth;
      ctx.lineCap = this._tickStyle;
      ctx.beginPath();
      ctx.moveTo(this.spokes[i].StartPoint.x, this.spokes[i].StartPoint.y);
      ctx.lineTo(this.spokes[i].EndPoint.x, this.spokes[i].EndPoint.y);
      ctx.stroke();

      if (this._isIndeterminate) {
        alpha -= this.alphaChange;
        if (alpha < this._lowestAlpha) {
          alpha = this._lowestAlpha;
        }
      }
    }

    ctx.setTransform(1, 0, 0, 1, 0, 0);
  }

  // 事件处理方法
  private onRenderTimerElapsed() {
    if (this._rotation === Direction.CLOCKWISE) {
      this.renderStartAngle += this.angleIncrement;
      if (this.renderStartAngle >= 360) {
        this.renderStartAngle -= 360;
      }
    } else {
      this.renderStartAngle -= this.angleIncrement;
      if (this.renderStartAngle <= -360) {
        this.renderStartAngle += 360;
      }
    }
    this.render();
  }

  private onVisibilityChanged(isVisible: boolean) {
    if (this._isIndeterminate) {
      if (isVisible) {
        this.start();
      } else {
        this.stop();
      }
    }
  }

  // 属性更改回调方法
  private onIntervalChanged(oldValue: number, newValue: number) {
    if (this.renderTimer) {
      this.stop();
      this.start();
    }
  }

  private onIsIndeterminateChanged(oldValue: boolean, newValue: boolean) {
    if (oldValue !== newValue) {
      if (newValue) {
        this.start();
      } else {
        this.stop();
        this.render();
      }
    }
  }

  private onProgressChanged(oldValue: number, newValue: number) {
    this.render();
  }

  private onRotationChanged(oldValue: Direction, newValue: Direction) {
    this.calculateSpokesPoints();
  }

  private onStartAngleChanged(oldValue: number, newValue: number) {
    this.calculateSpokesPoints();
  }

  private onTickColorChanged(oldValue: { r: number; g: number; b: number; a: number }, newValue: { r: number; g: number; b: number; a: number }) {
    this.render();
  }

  private onTickCountChanged(oldValue: number, newValue: number) {
    this.calculateSpokesPoints();
  }

  private onTickStyleChanged(oldValue: CanvasLineCap, newValue: CanvasLineCap) {
    this.render();
  }

  private onTickWidthChanged(oldValue: number, newValue: number) {
    this.render();
  }

  private onLowestAlphaChanged(oldValue: number, newValue: number) {
    // 可根据需求添加逻辑
  }

  private onAlphaTicksPercentageChanged(oldValue: number, newValue: number) {
    // 可根据需求添加逻辑
  }

  // 强制值回调方法
  private coerceProgress(value: number) {
    if (value < 0) return 0;
    if (value > 100) return 100;
    return value;
  }

  private coerceTickCount(value: number) {
    if (value <= 0) return SprocketControl.DEFAULT_TICK_COUNT;
    return value;
  }

  private coerceTickWidth(value: number) {
    if (value < 0) return SprocketControl.DEFAULT_TICK_WIDTH;
    return value;
  }

  private coerceLowestAlpha(value: number) {
    if (value < SprocketControl.ALPHA_LOWER_LIMIT) return SprocketControl.ALPHA_LOWER_LIMIT;
    if (value > SprocketControl.ALPHA_UPPER_LIMIT) return SprocketControl.ALPHA_UPPER_LIMIT;
    return value;
  }

  private coerceAlphaTicksPercentage(value: number) {
    if (value > 100) return 100;
    if (value < SprocketControl.ALPHA_TICK_PERCENTAGE_LOWER_LIMIT) return SprocketControl.ALPHA_TICK_PERCENTAGE_LOWER_LIMIT;
    return value;
  }

  // 销毁方法
  dispose() {
    this.stop();
  }
}