import { TradeHandle } from './TradeHandle'

export class FdItem {
  Code: string = ''
  Type: string = ''
  Length: number = 0
  DotLength: number = 0
  Description: string = ''
  FieldType: string = ''
  MaxLength: number = 0
  Children: FdItem[] = []
  MetaData: Record<string, any> = {}

  private static charType = ['0', '5', '6']
  private static numberType = ['1', '2', '4']

  getItemType(): 'string' | 'number' | null {
    if (FdItem.charType.includes(this.Type)) return 'string'
    if (FdItem.numberType.includes(this.Type)) return 'number'
    return null
  }

  formatValue(value: any): string {
    if (this.Children && this.Children.length > 0) {
      throw new Error(`域[${this.Code}]存在子域，不允许执行formatValue方法！`)
    }

    let result = ''
    switch (this.FieldType) {
      case '-1':
        // 该域被禁用
        break
      case '0':
        // 定长域
        if (FdItem.charType.includes(this.Type)) {
          // 字符型域
          const data = new Uint8Array(this.Length).fill(' '.charCodeAt(0))
          const source = new TextEncoder().encode(value?.toString() || '')
          const copyLength = Math.min(source.length, data.length)
          data.set(source.subarray(0, copyLength))
          result = new TextDecoder().decode(data)
        } else if (FdItem.numberType.includes(this.Type)) {
          // 数字型域
          const data = new Uint8Array(this.Length).fill('0'.charCodeAt(0))
          let newValue = parseFloat(value?.toString() || '0') || 0
          
          // 处理负数
          if (newValue < 0) data[0] = '-'.charCodeAt(0)
          
          // 处理小数位
          newValue = newValue * Math.pow(10, this.DotLength)
          newValue = Math.abs(Math.trunc(newValue))
          
          const source = new TextEncoder().encode(newValue.toString())
          const len = source.length - data.length
          
          if (len < 0) {
            data.set(source, -len)
          } else {
            data.set(source.subarray(len))
          }
          result = new TextDecoder().decode(data)
        }
        break
      default:
        // 变长域
        if (FdItem.charType.includes(this.Type)) {
          // 字符型域
          const source = new TextEncoder().encode(value?.toString() || '')
          const data = source.subarray(0, Math.min(source.length, this.MaxLength))
          result = new TextDecoder().decode(data)
        } else if (FdItem.numberType.includes(this.Type)) {
          // 数字型域
          let newValue = parseFloat(value?.toString() || '0') || 0
          newValue = newValue * Math.pow(10, this.DotLength)
          const source = new TextEncoder().encode(newValue.toString())
          const data = source.subarray(0, Math.min(source.length, this.MaxLength))
          result = new TextDecoder().decode(data)
        }
        break
    }
    return result
  }
}

export class MsgBitMap {
  private static _instance_Server: MsgBitMap
  private static _instance_Agn: MsgBitMap
  private static _instance_Cnaps2: MsgBitMap

  FdBitMap: Record<string, FdItem> = {}

  private constructor() {}

  static get Instance_Server(): MsgBitMap {
    if (!this._instance_Server) {
      this._instance_Server = new MsgBitMap()
      this._instance_Server.FdBitMap = this.InitFdBitMap('Server')
    }
    return this._instance_Server
  }

  static get Instance_Agn(): MsgBitMap {
    if (!this._instance_Agn) {
      this._instance_Agn = new MsgBitMap()
      this._instance_Agn.FdBitMap = this.InitFdBitMap('Agn')
    }
    return this._instance_Agn
  }

  static get Instance_Cnaps2(): MsgBitMap {
    if (!this._instance_Cnaps2) {
      this._instance_Cnaps2 = new MsgBitMap()
      this._instance_Cnaps2.FdBitMap = this.InitFdBitMap('Cnaps2')
    }
    return this._instance_Cnaps2
  }

  private static async InitFdBitMap(type: string): Promise<Record<string, FdItem>> {
    const dict: Record<string, FdItem> = {}
    const data = await TradeHandle.GetFdItemMap(type)

    data.forEach((item: any) => {
      const fdItem = new FdItem()
      fdItem.Code = item.Code
      fdItem.Type = item.Type
      fdItem.Length = parseInt(item.Length)
      fdItem.DotLength = parseInt(item.DotLength)
      fdItem.Description = item.Description
      fdItem.FieldType = item.FieldType
      fdItem.MaxLength = parseInt(item.MaxLength)
      fdItem.Children = []
      fdItem.MetaData = {}

      dict[fdItem.Code] = fdItem
      if (!fdItem.Code.endsWith('0')) {
        const code = fdItem.Code.substring(0, 3) + '0'
        if (dict[code]) {
          dict[code].Children.push(fdItem)
        }
      }
    })

    return dict
  }

  static async CreateMessageCode(): Promise<string> {
    const fdInfo: string[] = []
    const fds = Object.values(MsgBitMap.Instance_Server.FdBitMap)

    for (const item of fds) {
      if (!item.Children || item.Children.length === 0) {
        fdInfo.push(this.CreateFdCode(item))
      } else {
        fdInfo.push(`#region ${item.Code}域定义`)
        for (const child of item.Children) {
          fdInfo.push(this.CreateFdCode(child))
        }
        fdInfo.push('#endregion\n')
      }
    }

    return fdInfo.join('\n')
  }

  private static CreateFdCode(fdItem: FdItem): string {
    const fdNo = fdItem.Code.replace(/^0+/, '').slice(0, -1)
    const fdIndex = fdItem.Code.slice(-1)
    const isParent = fdIndex === '0'

    const codeLines: string[] = []
    codeLines.push(`#region ${fdItem.Code}域定义`)
    codeLines.push('/**')
    codeLines.push(` * ${isParent ? `第${fdNo}域` : `第${fdNo}域${fdIndex}子域`}`)
    codeLines.push(' */')
    codeLines.push(`public get ${isParent ? `Fd${fdNo}` : `Fd${fdNo}_${fdIndex}`}(): string {`)
    codeLines.push(`  return this.GetFdVaule("${fdItem.Code}").toString()`)
    codeLines.push('}')
    codeLines.push('')
    codeLines.push(`public set ${isParent ? `Fd${fdNo}` : `Fd${fdNo}_${fdIndex}`}(value: string) {`)
    codeLines.push(`  this.SetFdValue("${fdItem.Code}", value)`)
    codeLines.push('}')
    codeLines.push('#endregion')
    
    return codeLines.join('\n')
  }
}