/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import measure from '@ohos.measure';
import Curves from '@ohos.curves';
import { ColorMetrics, LengthMetrics, LengthUnit } from '@ohos.arkui.node';

export enum ArcButtonPosition {
  TOP_EDGE = 0,
  BOTTOM_EDGE = 1
}

export enum ArcButtonStyleMode {
  EMPHASIZED_LIGHT = 0,
  EMPHASIZED_DARK = 1,
  NORMAL_LIGHT = 2,
  NORMAL_DARK = 3,
  CUSTOM = 4
}

export enum ArcButtonStatus {
  NORMAL = 0,
  PRESSED = 1,
  DISABLED = 2
}

interface CommonArcButtonOptions {
  /**
   * 弧形按钮位置类型属性,默认为下弧形按钮
   */
  position?: ArcButtonPosition
  /**
   *强调、普通01、普通02、警告、自定义  默认是强调状态
   */
  styleMode?: ArcButtonStyleMode
  /**
   *初始态、按压、不可用形态,默认为初始态
   */
  status?: ArcButtonStatus
  /**
   *文字
   */
  label?: ResourceStr
  /**
   *背景模糊能力
   */
  backgroundBlurStyle?: BlurStyle
  /**
   *按钮背景色
   */
  backgroundColor?: ColorMetrics
  /**
   *按钮阴影色
   */
  shadowColor?: ColorMetrics
  /**
   *打开关闭弧形按钮阴影
   */
  shadowEnabled?: boolean
  /**
   *字体大小
   */
  fontSize?: LengthMetrics
  /**
   *字体颜色
   */
  fontColor?: ColorMetrics
  /**
   *字体按压颜色
   */
  pressedFontColor?: ColorMetrics
  /**
   *文字样式
   */
  fontStyle?: FontStyle
  /**
   *文字字体族
   */
  fontFamily?: string | Resource
  /**
   *文字到边框的距离
   */
  fontMargin?: LocalizedMargin
  /**
   * TouchEvent
   */
  onTouch?: Callback<TouchEvent>
  /**
   * ClickEvent
   */
  onClick?: Callback<ClickEvent>
  /**
   * ArcButtonProgress属性
   */
  progressConfig?: ArcButtonProgressConfig;
}


class Constants {
  /**
   * 最大文字大小
   */
  public static readonly MAX_FONT_SIZE = 19;
  /**
   * 最小文字大小
   */
  public static readonly MIN_FONT_SIZE = 13;
  /**
   * 阴影半径
   */
  public static readonly SHADOW_BLUR = 4;
  /**
   * Y偏移
   */
  public static readonly SHADOW_OFFSET_Y = 3;
  /**
   * 按钮与边框距离
   */
  public static readonly DISTANCE_FROM_BORDER = 1;
  /**
   * 文本间距
   */
  public static readonly TEXT_HORIZONTAL_MARGIN = 24;
  public static readonly TEXT_MARGIN_TOP = 10;
  public static readonly TEXT_MARGIN_BOTTOM = 16;
  public static readonly EMPHASIZED_NORMAL_BTN_COLOR = $r('sys.color.comp_background_emphasize');
  public static readonly EMPHASIZED_TEXT_COLOR = '#FFFFFF';
  public static readonly EMPHASIZED_PRESSED_BTN_COLOR = '#357FFF';
  public static readonly EMPHASIZED_DISABLE_BTN_COLOR = '#1F71FF';
  public static readonly EMPHASIZED_DISABLE_TEXT_COLOR = '#FFFFFF';
  public static readonly NORMAL_LIGHT_NORMAL_BTN_COLOR = '#17273F';
  public static readonly NORMAL_LIGHT_TEXT_COLOR = '#5EA1FF';
  public static readonly NORMAL_LIGHT_PRESSED_BTN_COLOR = '#2E3D52';
  public static readonly NORMAL_LIGHT_DISABLE_BTN_COLOR = '#17273F';
  public static readonly NORMAL_LIGHT_DISABLE_TEXT_COLOR = '#995ea1ff';
  public static readonly NORMAL_DARK_NORMAL_BTN_COLOR = '#252525';
  public static readonly NORMAL_DARK_TEXT_COLOR = '#5EA1FF';
  public static readonly NORMAL_DARK_PRESSED_BTN_COLOR = '#3B3B3B';
  public static readonly NORMAL_DARK_DISABLE_BTN_COLOR = '#262626';
  public static readonly NORMAL_DARK_DISABLE_TEXT_COLOR = '#995ea1ff';
  public static readonly EMPHASIZEWARN_NORMAL_BTN_COLOR = '#BF2629';
  public static readonly EMPHASIZEWARN_TEXT_COLOR = '#FFFFFF';
  public static readonly EMPHASIZEWARN_PRESSED_BTN_COLOR = '#C53C3E';
  public static readonly EMPHASIZEWARN_DISABLE_BTN_COLOR = '#4C0f10';
  public static readonly EMPHASIZEWARN_DISABLE_TEXT_COLOR = '#99FFFFFF';
  public static readonly PRESS_MERGE_COLOR = '#1AFFFFFF';
  public static readonly DEFAULT_TRANSPARENCY = 0.4;
}

interface ArcButtonThemeInterface {
  /**
   * 弧形按钮高度
   */
  BUTTON_HEIGHT: number,

  /**
   * 辅助圆半径
   */
  ARC_CIRCLE_DIAMETER: number,

  /**
   * 表盘直径
   */
  DIAL_CIRCLE_DIAMETER: number,

  /**
   * 弧形按钮倒角圆半径
   */
  CHAMFER_CIRCLE_RADIUS: number,
}

@ObservedV2
export class ArcButtonProgressConfig {
  @Trace public value: number;
  @Trace public total?:number;
  @Trace public color?: ResourceColor;

  constructor(options: ArcButtonProgressConfig) {
    this.value = options.value;
    this.total = options.total;
    this.color = options.color;
  }
}

@ObservedV2
export class ArcButtonOptions {
  @Trace public position: ArcButtonPosition;
  @Trace public styleMode: ArcButtonStyleMode;
  @Trace public status: ArcButtonStatus;
  @Trace public label: ResourceStr;
  @Trace public backgroundBlurStyle: BlurStyle;
  @Trace public backgroundColor: ColorMetrics;
  @Trace public shadowColor: ColorMetrics;
  @Trace public shadowEnabled: boolean;
  @Trace public fontSize: LengthMetrics;
  @Trace public fontColor: ColorMetrics;
  @Trace public pressedFontColor: ColorMetrics;
  @Trace public fontStyle: FontStyle;
  @Trace public fontFamily: string | Resource;
  @Trace public fontMargin: LocalizedMargin;
  @Trace public onTouch?: Callback<TouchEvent>;
  @Trace public onClick?: Callback<ClickEvent>;
  @Trace public progressConfig?: ArcButtonProgressConfig;

  constructor(options: CommonArcButtonOptions) {
    this.position = options.position ?? ArcButtonPosition.BOTTOM_EDGE;
    this.styleMode = options.styleMode ?? ArcButtonStyleMode.EMPHASIZED_LIGHT;
    this.status = options.status ?? ArcButtonStatus.NORMAL;
    this.label = options.label ?? '';
    this.backgroundBlurStyle = options.backgroundBlurStyle ?? BlurStyle.NONE;
    this.backgroundColor = options.backgroundColor ?? ColorMetrics.resourceColor(Color.Black);
    this.shadowColor = options.shadowColor ?? ColorMetrics.resourceColor('#000000');
    this.shadowEnabled = options.shadowEnabled ?? false;
    this.fontSize = options.fontSize!;
    this.fontColor = options.fontColor ?? ColorMetrics.resourceColor(Color.White);
    this.pressedFontColor = options.pressedFontColor ?? ColorMetrics.resourceColor(Color.White);
    this.fontStyle = options.fontStyle ?? FontStyle.Normal;
    this.fontFamily = options.fontFamily ?? '';
    this.fontMargin = options.fontMargin ?? {
      start: LengthMetrics.vp(Constants.TEXT_HORIZONTAL_MARGIN),
      top: LengthMetrics.vp(Constants.TEXT_MARGIN_TOP),
      end: LengthMetrics.vp(Constants.TEXT_HORIZONTAL_MARGIN),
      bottom: LengthMetrics.vp(Constants.TEXT_MARGIN_BOTTOM)
    }
    this.onTouch = options.onTouch ?? (() => {
    })
    this.onClick = options.onClick ?? (() => {
    })
    if(options.progressConfig) {
      this.backgroundColor = options.backgroundColor ?? ColorMetrics.resourceColor(
        Constants.EMPHASIZED_DISABLE_BTN_COLOR);
      this.progressConfig = new ArcButtonProgressConfig(options.progressConfig);
    } else {
      this.progressConfig = undefined;
    }
  }
}

@ComponentV2
export struct ArcButton {
  @Require @Param options: ArcButtonOptions;
  @Local private canvasWidth: number = 0;
  @Local private canvasHeight: number = 0;
  @Local private scaleX: number = 1;
  @Local private scaleY: number = 1;
  @Local private btnColor: ColorMetrics = ColorMetrics.resourceColor(Color.Black);
  @Local private textWidth: number = 0;
  @Local private textHeight: number = 0;
  @Local private fontColor: ColorMetrics = ColorMetrics.resourceColor(Color.White);
  @Local private isExceed: boolean = false;
  @Local private pathString: string = '';
  @Local private fontSize: string = '';
  @Local private progressValue:number = 0;
  @Local private progressTotal:number = 100;
  @Local private progressColor: ColorMetrics = ColorMetrics.resourceColor(Constants.EMPHASIZED_DISABLE_BTN_COLOR);
  private btnNormalColor: ColorMetrics = ColorMetrics.resourceColor(Color.Black);
  private btnPressColor: ColorMetrics = ColorMetrics.resourceColor(Color.Black);
  private btnDisableColor: ColorMetrics = ColorMetrics.resourceColor(Color.Black);
  private textNormalColor: ColorMetrics = ColorMetrics.resourceColor(Color.White);
  private textDisableColor: ColorMetrics = ColorMetrics.resourceColor(Color.White);
  private isUp: boolean = false;
  private curves: ICurve = Curves.interpolatingSpring(10, 1, 350, 35);
  private scaleValue: number = 1;
  private textPressColor: ColorMetrics = ColorMetrics.resourceColor(Color.White);
  private arcButtonTheme: ArcButtonThemeInterface = {
    BUTTON_HEIGHT: this.getArcButtonThemeVpValue($r('sys.float.arc_button_height')),
    ARC_CIRCLE_DIAMETER: this.getArcButtonThemeVpValue($r('sys.float.arc_button_auxiliary_circle_diameter')),
    DIAL_CIRCLE_DIAMETER: this.getArcButtonThemeVpValue($r('sys.float.arc_button_dial_circle_diameter')),
    CHAMFER_CIRCLE_RADIUS: this.getArcButtonThemeVpValue($r('sys.float.arc_button_chamfer_radius'))
  }
  private dataProcessUtil: DataProcessUtil = new DataProcessUtil(this.arcButtonTheme);

  @Monitor('options.label', 'options.type', 'options.fontSize', 'options.styleMode', 'options.status',
  'options.backgroundColor', 'options.fontColor', 'options.progressConfig.color', 'options.progressConfig.total',
  'options.progressConfig.value')
  optionsChange() {
    this.fontSize = this.cover(this.options.fontSize ?? new LengthMetrics(Constants.MAX_FONT_SIZE))
    this.judgeTextWidth()
    this.changeStatus()
    this.progressOptionsChange()
  }

  progressOptionsChange(){
    if(this.options.progressConfig) {
      this.progressValue = this.options.progressConfig.value;
      if(this.options.progressConfig.color) {
        this.progressColor = ColorMetrics.resourceColor(this.options.progressConfig.color);
      } else if (this.options.backgroundColor !== undefined) {
        this.progressColor = this.options.backgroundColor;
      } else {
        this.progressColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_DISABLE_BTN_COLOR);
      }
      if(this.options.progressConfig.total) {
        this.progressTotal = this.options.progressConfig.total;
      } else {
        this.progressTotal = 100;
      }
    }
  }

  changeStatus() {
    switch (this.options.styleMode) {
      case ArcButtonStyleMode.EMPHASIZED_LIGHT:
        this.btnNormalColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_NORMAL_BTN_COLOR);
        this.textNormalColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_TEXT_COLOR);
        this.btnPressColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_NORMAL_BTN_COLOR).blendColor(
          ColorMetrics.resourceColor(Constants.PRESS_MERGE_COLOR));
        this.btnDisableColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_DISABLE_BTN_COLOR);
        this.textDisableColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_DISABLE_TEXT_COLOR);
        this.textPressColor = ColorMetrics.resourceColor(Constants.EMPHASIZED_TEXT_COLOR);
        break;

      case ArcButtonStyleMode.NORMAL_LIGHT:
        this.btnNormalColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_NORMAL_BTN_COLOR);
        this.textNormalColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_TEXT_COLOR);
        this.btnPressColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_NORMAL_BTN_COLOR).blendColor(
          ColorMetrics.resourceColor(Constants.PRESS_MERGE_COLOR));
        this.btnDisableColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_DISABLE_BTN_COLOR);
        this.textDisableColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_DISABLE_TEXT_COLOR);
        this.textPressColor = ColorMetrics.resourceColor(Constants.NORMAL_LIGHT_TEXT_COLOR);
        break;

      case ArcButtonStyleMode.NORMAL_DARK:
        this.btnNormalColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_NORMAL_BTN_COLOR);
        this.textNormalColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_TEXT_COLOR);
        this.btnPressColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_NORMAL_BTN_COLOR).blendColor(
          ColorMetrics.resourceColor(Constants.PRESS_MERGE_COLOR));
        this.btnDisableColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_DISABLE_BTN_COLOR);
        this.textDisableColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_DISABLE_TEXT_COLOR);
        this.textPressColor = ColorMetrics.resourceColor(Constants.NORMAL_DARK_TEXT_COLOR);
        break;

      case ArcButtonStyleMode.EMPHASIZED_DARK:
        this.btnNormalColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_NORMAL_BTN_COLOR);
        this.textNormalColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_TEXT_COLOR);
        this.btnPressColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_NORMAL_BTN_COLOR).blendColor(
          ColorMetrics.resourceColor(Constants.PRESS_MERGE_COLOR));
        this.btnDisableColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_DISABLE_BTN_COLOR);
        this.textDisableColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_DISABLE_TEXT_COLOR);
        this.textPressColor = ColorMetrics.resourceColor(Constants.EMPHASIZEWARN_TEXT_COLOR);
        break;

      default:
        this.btnNormalColor = this.options.backgroundColor;
        this.textNormalColor = this.options.fontColor;
        this.btnPressColor = this.options.backgroundColor.blendColor(
          ColorMetrics.resourceColor(Constants.PRESS_MERGE_COLOR));
        this.textPressColor = this.options.pressedFontColor;
        break;
    }
    if (this.options.status === ArcButtonStatus.DISABLED) {
      this.btnColor = this.btnDisableColor;
      this.fontColor = this.textDisableColor;
    } else if (this.options.status === ArcButtonStatus.PRESSED) {
      this.btnColor = this.btnPressColor;
      this.fontColor = this.textPressColor;
    } else {
      this.btnColor = this.btnNormalColor;
      this.fontColor = this.textNormalColor;
    }
  }

  /**
   * 初始化数据
   */
  private initValues() {
    this.isUp = this.options.position == ArcButtonPosition.TOP_EDGE;
    this.btnColor = this.options.backgroundColor;
    this.fontColor = this.options.fontColor;
    this.curves = Curves.interpolatingSpring(10, 1, 350, 35);
    this.scaleValue = 1;
    this.progressOptionsChange();
    this.changeStatus();
  }

  private getArcButtonThemeVpValue(res: Resource): number {
    if (!res) {
      return 0
    }
    let metrics = LengthMetrics.resource(res)
    let value = metrics.value
    switch (metrics.unit) {
      case LengthUnit.PX:
        return px2vp(value)

      case LengthUnit.LPX:
        return px2vp(lpx2px(value))

      case LengthUnit.FP:
        return px2vp(fp2px(value))
    }
    return value
  }

  /**
   * 判断是否超出文本框宽度
   */
  private judgeTextWidth() {
    const measureTextWidth = measure.measureText({
      textContent: this.options.label,
      fontStyle: this.options.fontStyle,
      fontFamily: this.options.fontFamily,
      fontWeight: FontWeight.Medium,
      maxLines: 1,
      fontSize: `${Constants.MIN_FONT_SIZE}fp`
    })
    this.isExceed = measureTextWidth > this.getUIContext().vp2px(this.textWidth);
  }

  aboutToAppear() {
    if (this.arcButtonTheme.BUTTON_HEIGHT === 0) {
      console.error("arcbutton can't obtain sys float value.")
      return
    }
    this.initValues();
    this.dataProcessUtil.initData();
    const pathData = this.dataProcessUtil.calculate();
    this.generatePath(pathData);
  }

  private calculateActualPosition(pos: ArcButtonPoint, canvasTopPos: ArcButtonPoint): ArcButtonPoint {
    const x = this.getUIContext().vp2px(pos.x - canvasTopPos.x);
    const y = this.getUIContext().vp2px(pos.y - canvasTopPos.y);
    return new ArcButtonPoint(x, y);
  }

  private generatePath(data: AllPoints | null) {
    if (data === null) {
      return
    }
    this.canvasWidth = data.btnWidth + Constants.SHADOW_BLUR * 2;
    this.canvasHeight = data.btnHeight + Constants.DISTANCE_FROM_BORDER * 2;

    const margin = this.options.fontMargin;
    const start = margin?.start?.value ?? 0;
    const end = margin?.end?.value ?? 0;
    const top = margin?.top?.value ?? 0;
    const bottom = margin?.bottom?.value ?? 0;
    this.textWidth = data.btnWidth - start - end;
    this.textHeight = data.btnHeight - top - bottom;
    this.judgeTextWidth();
    const canvasLeftTopPoint = data.canvasLeftTop;
    canvasLeftTopPoint.x -= Constants.SHADOW_BLUR;
    canvasLeftTopPoint.y -= Constants.DISTANCE_FROM_BORDER;

    const leftTopPoint = this.calculateActualPosition(data.leftTopPoint, canvasLeftTopPoint);
    const upperArcCircleR: number = this.getUIContext().vp2px(this.arcButtonTheme.ARC_CIRCLE_DIAMETER / 2);

    const rightTopPoint = this.calculateActualPosition(data.rightTopPoint, canvasLeftTopPoint);
    const chamferCircleR: number = this.getUIContext().vp2px(this.arcButtonTheme.CHAMFER_CIRCLE_RADIUS);

    const rightBottomPoint = this.calculateActualPosition(data.rightBottomPoint, canvasLeftTopPoint);
    const lowerArcCircleR: number = this.getUIContext().vp2px(this.arcButtonTheme.DIAL_CIRCLE_DIAMETER / 2);

    const leftBottomPoint = this.calculateActualPosition(data.leftBottomPoint, canvasLeftTopPoint);

    const pathStr = `M ${leftTopPoint.x} ${leftTopPoint.y} A ${upperArcCircleR} ${upperArcCircleR}, 0, 0, 0,
       ${rightTopPoint.x} ${rightTopPoint.y}` +
      `Q ${rightTopPoint.x - chamferCircleR * 1.2} ${rightTopPoint.y +
        chamferCircleR * 0.6} ${rightBottomPoint.x} ${rightBottomPoint.y}` +
      `A ${lowerArcCircleR} ${lowerArcCircleR}, 0, 0, 0, ${leftBottomPoint.x}
       ${leftBottomPoint.y}` +
      `Q ${leftTopPoint.x + chamferCircleR * 1.2} ${leftTopPoint.y +
        chamferCircleR * 0.6} ${leftTopPoint.x} ${leftTopPoint.y}`

    this.pathString = pathStr
  }

  @Builder
  TextBuilderIsExceed() {
    Text(this.options.label)
      .width(this.textWidth)
      .height(this.textHeight)
      .fontColor(this.fontColor.color)
      .fontSize(this.fontSize)
      .maxLines(1)
      .textAlign(TextAlign.Center)
      .fontWeight(FontWeight.Medium)
      .fontStyle(this.options.fontStyle)
      .fontFamily(this.options.fontFamily)
      .backgroundColor(Color.Transparent)
      .textOverflow({ overflow: TextOverflow.MARQUEE })
      .margin({
        start: this.options.fontMargin.start,
        top: this.isUp ? this.options.fontMargin.bottom : this.options.fontMargin.top,
        end: this.options.fontMargin.end,
        bottom: this.options.fontMargin.bottom
      })
  }

  @Builder
  TextBuilderNormal() {
    Text(this.options.label)
      .width(this.textWidth)
      .height(this.textHeight)
      .textAlign(TextAlign.Center)
      .fontColor(this.fontColor.color)
      .maxFontSize(this.options.fontSize ? undefined : `${Constants.MAX_FONT_SIZE}fp`)
      .minFontSize(this.options.fontSize ? undefined : `${Constants.MIN_FONT_SIZE}fp`)
      .fontSize(this.options.fontSize ? this.cover(this.options.fontSize) : undefined)
      .fontWeight(FontWeight.Medium)
      .fontStyle(this.options.fontStyle)
      .fontFamily(this.options.fontFamily)
      .maxLines(1)
      .margin({
        start: this.options.fontMargin.start,
        top: this.isUp ? this.options.fontMargin.bottom : this.options.fontMargin.top,
        end: this.options.fontMargin.end,
        bottom: this.options.fontMargin.bottom
      })
  }

  private cover(params: LengthMetrics): string {
    switch (params.unit) {
      case LengthUnit.VP:
        return `${params.value}vp`;
      case LengthUnit.PX:
        return `${params.value}px`;
      case LengthUnit.FP:
        return `${params.value}fp`;
      case LengthUnit.LPX:
        return `${params.value}lpx`;
      case LengthUnit.PERCENT:
        return `${params.value}%`;
    }
  }

  private getShadow(): ShadowOptions | undefined {
    if (!this.options.shadowEnabled) {
      return undefined;
    }
    return {
      radius: Constants.SHADOW_BLUR,
      color: this.options.shadowColor.color,
      offsetY: Constants.SHADOW_OFFSET_Y
    }
  }

  build() {
    Stack({ alignContent: Alignment.Center }) {
      if (this.options.progressConfig) {
        Progress({value: this.progressValue, total: this.progressTotal, type: ProgressType.Capsule})
          .width('100%')
          .height('100%')
          .rotate({ angleX: !this.isUp ? 0 : 180 })
          .clipShape(new Path({ commands: this.pathString }))
          .backgroundColor(
            ColorMetrics.rgba(this.progressColor.red,this.progressColor.green,this.progressColor.blue, 0.25))
          .color(this.progressColor.color)
          .backgroundBlurStyle(this.options.backgroundBlurStyle, undefined, { disableSystemAdaptation: true })
          .shadow(this.getShadow())
      } else  {
        Button({ type: ButtonType.Normal, stateEffect: false })
          .width('100%')
          .height('100%')
          .rotate({ angle: !this.isUp ? 0 : 180 })
          .clipShape(new Path({ commands: this.pathString }))
          .backgroundColor(this.btnColor.color)
          .backgroundBlurStyle(this.options.backgroundBlurStyle, undefined, { disableSystemAdaptation: true })
          .shadow(this.getShadow())
      }

      if (this.isExceed) {
        this.TextBuilderIsExceed()
      } else {
        this.TextBuilderNormal()
      }
    }
    .enabled(this.options.status !== ArcButtonStatus.DISABLED)
    .opacity((this.options.styleMode === ArcButtonStyleMode.EMPHASIZED_LIGHT &&
      this.options.status === ArcButtonStatus.DISABLED) ? Constants.DEFAULT_TRANSPARENCY : 1)
    .animation({ curve: this.curves })
    .width(this.canvasWidth)
    .height(this.canvasHeight)
    .scale({ x: this.scaleX, y: this.scaleY, centerY: this.isUp ? 0 : this.canvasHeight })
    .onTouch((event: TouchEvent) => {
      this.dealTouchEvent(event)
    })
    .onClick((event: ClickEvent) => {
      if (this.options.onClick) {
        this.options.onClick(event)
      }
    })
  }

  private dealTouchEvent(event: TouchEvent) {
    const x = event.touches[0].windowX;
    const y = event.touches[0].windowY;
    if (this.options.onTouch) {
      this.options.onTouch(event);
    }
    if (this.options.status === ArcButtonStatus.PRESSED) {
      this.scaleX = this.scaleValue;
      this.scaleY = this.scaleValue;
      this.btnColor = this.btnPressColor;
      this.fontColor = this.textPressColor;
    } else {
      switch (event.type) {
        case TouchType.Down:
          this.scaleX = this.scaleValue;
          this.scaleY = this.scaleValue;
          this.btnColor = this.btnPressColor;
          this.fontColor = this.textPressColor;
          break;
        case TouchType.Up:
          this.scaleX = 1;
          this.scaleY = 1;
          this.btnColor = this.btnNormalColor;
          this.fontColor = this.textNormalColor;
          break;
        default:
          break;
      }
    }
  }
}

class DataProcessUtil {
  private dial: ArcButtonCircle = new ArcButtonCircle(0, 0, 0);
  private arc: ArcButtonCircle = new ArcButtonCircle(0, 0, 0);
  private height: number = 0;
  private width: number = 0;
  private arcButtonTheme: ArcButtonThemeInterface | undefined = undefined

  constructor(theme: ArcButtonThemeInterface) {
    this.arcButtonTheme = theme
  }

  initData() {
    const dialRadius = this.arcButtonTheme!.DIAL_CIRCLE_DIAMETER / 2;
    this.dial = new ArcButtonCircle(dialRadius, dialRadius, dialRadius);

    const arcRadius = this.arcButtonTheme!.ARC_CIRCLE_DIAMETER / 2;
    this.height = this.arcButtonTheme!.BUTTON_HEIGHT;
    const arcX = this.dial.center.x;
    const arcY = this.dial.center.y + dialRadius + arcRadius - this.height;
    this.arc = new ArcButtonCircle(arcRadius, arcX, arcY);
  }

  calculate(): AllPoints {
    const chamferCircleR = this.arcButtonTheme!.CHAMFER_CIRCLE_RADIUS;
    const innerDial = new ArcButtonCircle(this.dial.radius - chamferCircleR, this.dial.center.x, this.dial.center.y);
    const innerArc = new ArcButtonCircle(this.arc.radius - chamferCircleR, this.arc.center.x, this.arc.center.y);
    const intersections = this.findCircleIntersections(innerArc, innerDial);
    const tp1 = this.calculateIntersection(this.arc.center, this.arc.radius, intersections[0]);
    const tp2 = this.calculateIntersection(this.arc.center, this.arc.radius, intersections[1]);
    const tp3 = this.calculateIntersection(this.dial.center, this.dial.radius, intersections[1]);
    const tp4 = this.calculateIntersection(this.dial.center, this.dial.radius, intersections[0]);

    this.width = this.calculateDistance(intersections[0], intersections[1]) + chamferCircleR * 2;
    const canvasLeftTop = new ArcButtonPoint(intersections[0].x - chamferCircleR, this.dial.center.y +
    this.dial.radius - this.height);

    return new AllPoints(this.width, this.height, tp2, tp1, tp3, tp4, canvasLeftTop);
  }

  /**
   * 计算两点间距离
   * @param point1 点1
   * @param point2 点2
   * @returns 距离
   */
  calculateDistance(point1: ArcButtonPoint, point2: ArcButtonPoint): number {
    return Math.sqrt((point2.x - point1.x) ** 2 + (point2.y - point1.y) ** 2);
  }

  calculateIntersection(circleCenter: ArcButtonPoint, circleRadius: number, point: ArcButtonPoint): ArcButtonPoint {
    const h = circleCenter.x;
    const k = circleCenter.y;
    const x = point.x;
    const y = point.y;

    //计算直线斜率
    let m: number = 0;
    if (x !== h) {
      m = (y - k) / (x - h);
    } else {
      m = -1;
    }

    //计算截距
    let intercept: number = 0;
    if (m !== -1) {
      intercept = y - m * x;
    }

    //保存焦点位置
    let resultPoint: ArcButtonPoint[] = []

    //判断斜率
    if (m !== -1) {
      const a = Math.pow(m, 2) + 1;
      const b = 2 * (m * intercept - m * k - h);
      const c = k ** 2 - circleRadius ** 2 + h ** 2 - 2 * intercept * k + intercept ** 2;

      const x1 = (-b + (b ** 2 - 4 * a * c) ** 0.5) / (2 * a);
      const x2 = (-b - (b ** 2 - 4 * a * c) ** 0.5) / (2 * a);
      const y1: number = m * x1 + intercept;
      const y2: number = m * x2 + intercept;

      resultPoint = [new ArcButtonPoint(x1, y1), new ArcButtonPoint(x2, y2)];
    } else {
      const x1 = h;
      const y1 = k + circleRadius;
      const y2 = k - circleRadius;
      resultPoint = [new ArcButtonPoint(x1, y1), new ArcButtonPoint(x1, y2)];
    }

    const d1 = this.calculateDistance(resultPoint[0], point);
    const d2 = this.calculateDistance(resultPoint[1], point);
    if (d1 < d2) {
      return resultPoint[0];
    } else {
      return resultPoint[1];
    }
  }

  /**
   * 查找两圆的交点
   * @param C1 第一个圆
   * @param c2 第二个圆
   * @returns 两圆相交的点的数组
   */
  findCircleIntersections(firstCircus: ArcButtonCircle, secondCircus: ArcButtonCircle): ArcButtonPoint[] {
    const firstCircusR = firstCircus.radius;
    const firstCircusCenterX = firstCircus.center.x;
    const firstCircusCenterY = firstCircus.center.y;

    const secondCircusR = secondCircus.radius;
    const secondCircusCenterX = secondCircus.center.x;
    const secondCircusCenterY = secondCircus.center.y;

    // 计算两个圆心之间的距离
    const distance = Math.sqrt((firstCircusCenterX - secondCircusCenterX) ** 2 + (firstCircusCenterY -
      secondCircusCenterY) ** 2);

    // 检查异常情况
    if (distance > firstCircusR + secondCircusR) {
      //两个圆分离,不相交
      return [];
    } else if (distance < Math.abs(firstCircusR - secondCircusR)) {
      //一个圆包含在另一个圆内,不相交
      return [];
    } else if (distance === 0 && firstCircusR === secondCircusR) {
      //两个圆完全重合,具有无穷多交点
      return [];
    }

    // 计算交点
    const a = (firstCircusR ** 2 - secondCircusR ** 2 + distance ** 2) / (2 * distance);
    const h = Math.sqrt(firstCircusR ** 2 - a ** 2);

    // 中间变量
    const x2 = firstCircusCenterX + a * (secondCircusCenterX - firstCircusCenterX) / distance;
    const y2 = firstCircusCenterY + a * (secondCircusCenterY - firstCircusCenterY) / distance;

    // 交点
    let intersection1 = new ArcButtonPoint(x2 + h * (secondCircusCenterY - firstCircusCenterY) / distance, y2 -
      h * (secondCircusCenterX - firstCircusCenterX) / distance);
    let intersection2 = new ArcButtonPoint(x2 - h * (secondCircusCenterY - firstCircusCenterY) / distance, y2 +
      h * (secondCircusCenterX - firstCircusCenterX) / distance);

    if (intersection1.x > intersection2.x) {
      const mid = intersection1;
      intersection1 = intersection2;
      intersection2 = mid;
    }

    return [intersection1, intersection2];
  }
}

class ArcButtonCircle {
  public radius: number;
  public center: ArcButtonPoint;

  constructor(radius: number, x: number, y: number) {
    this.radius = radius;
    this.center = new ArcButtonPoint(x, y);
  }
}

class ArcButtonPoint {
  public x: number;
  public y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

class AllPoints {
  public btnWidth: number;
  public btnHeight: number;
  public leftTopPoint: ArcButtonPoint;
  public rightTopPoint: ArcButtonPoint;
  public leftBottomPoint: ArcButtonPoint;
  public rightBottomPoint: ArcButtonPoint;
  public canvasLeftTop: ArcButtonPoint;

  constructor(btnWidth: number,
    btnHeight: number,
    leftTopPoint: ArcButtonPoint,
    rightTopPoint: ArcButtonPoint,
    leftBottomPoint: ArcButtonPoint,
    rightBottomPoint: ArcButtonPoint,
    canvasLeftTop: ArcButtonPoint) {
    this.btnWidth = btnWidth;
    this.btnHeight = btnHeight;
    this.leftTopPoint = leftTopPoint;
    this.rightTopPoint = rightTopPoint;
    this.leftBottomPoint = leftBottomPoint;
    this.rightBottomPoint = rightBottomPoint;
    this.canvasLeftTop = canvasLeftTop;
  }
}