const {
  createAliasMap,
  createEnumTypeMap,
  findEnums,
  findStruct,
  findStructs,
  normalizeLookupName,
  normalizeTypeText,
  parseDeclarator,
  splitDeclarators,
  stripComments
} = require('./struct-c-syntax.js')
const {
  parseStructFields
} = require('./struct-layout.js')

const STRUCT_VARIABLE_QUALIFIERS = {
  code: true,
  const: true,
  data: true,
  extern: true,
  idata: true,
  pdata: true,
  static: true,
  volatile: true,
  xdata: true
}

function parseStructDefinition(sourceText) {
  const source = stripComments(sourceText)
  const enums = findEnums(source)
  const enumTypes = createEnumTypeMap(enums)
  const aliases = createAliasMap(source, enums)
  const structInfo = findStruct(source)
  if (!structInfo) {
    throw new Error('未找到结构体定义')
  }

  const registers = parseStructFields(structInfo.body, aliases, enumTypes)
  if (!registers.length) {
    throw new Error('结构体中没有可识别的变量定义')
  }

  return {
    name: structInfo.name || 'Struct',
    registers,
    structName: structInfo.name || 'Struct'
  }
}

function getStructNameAliases(structInfo, aliases) {
  const names = [structInfo.name]

  if (structInfo.tagName) names.push(`struct ${structInfo.tagName}`)

  Object.keys(aliases || {}).forEach((aliasName) => {
    if (aliases[aliasName] === structInfo.name) names.push(aliasName)
  })

  return names.filter(Boolean)
}

function normalizeVariableTypeText(typeText, aliases) {
  const normalized = normalizeTypeText(typeText)
  if (!normalized) return ''

  const tokens = normalized
    .split(/\s+/)
    .filter((token) => !STRUCT_VARIABLE_QUALIFIERS[token])

  const compact = tokens.join(' ')
  if (aliases && aliases[compact]) return aliases[compact]

  return compact
}

function parseStructVariables(source, structs, aliases) {
  const variablesByName = {}

  structs.forEach((structInfo) => {
    const structNames = getStructNameAliases(structInfo, aliases)

    structNames.forEach((structName) => {
      const escaped = structName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
      const variablePattern = new RegExp(`(^|[;\\n{}])\\s*([A-Za-z_][\\w\\s]*?\\s+)?${escaped}\\s+([^;{}()]+);`, 'g')
      let match

      while ((match = variablePattern.exec(source))) {
        const prefix = normalizeVariableTypeText(match[2] || '', aliases)
        if (prefix) continue

        splitDeclarators(match[3]).forEach((declaratorText) => {
          const field = parseDeclarator(declaratorText)
          if (!field) return

          const variableInfo = {
            arrayDimensions: field.arrayDimensions,
            name: field.name,
            registers: structInfo.registers,
            structName: structInfo.name
          }
          variablesByName[field.name] = variableInfo
          variablesByName[field.name.replace(/^_+/, '').toLowerCase()] = variableInfo
          variablesByName[normalizeLookupName(field.name)] = variableInfo
        })
      }
    })
  })

  return variablesByName
}

function getEnumVariableTypeNames(enumInfo = {}) {
  return (Array.isArray(enumInfo.typeNames) ? enumInfo.typeNames : [])
    .concat(enumInfo.name, enumInfo.typedefName, enumInfo.tagName ? `enum ${enumInfo.tagName}` : '')
    .filter(Boolean)
}

function parseEnumVariables(source, enums, aliases) {
  const variablesByName = {}

  enums.forEach((enumInfo) => {
    getEnumVariableTypeNames(enumInfo).forEach((enumTypeName) => {
      const escaped = enumTypeName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
      const variablePattern = new RegExp(`(^|[;\\n{}])\\s*([A-Za-z_][\\w\\s]*?\\s+)?${escaped}\\s+([^;{}()]+);`, 'g')
      let match

      while ((match = variablePattern.exec(source))) {
        const prefix = normalizeVariableTypeText(match[2] || '', aliases)
        if (prefix) continue

        splitDeclarators(match[3]).forEach((declaratorText) => {
          const field = parseDeclarator(declaratorText)
          if (!field) return

          variablesByName[field.name] = enumInfo
          variablesByName[field.name.replace(/^_+/, '').toLowerCase()] = enumInfo
          variablesByName[normalizeLookupName(field.name)] = enumInfo
        })
      }
    })
  })

  return variablesByName
}

function parseStructCatalog(sourceText) {
  const source = stripComments(sourceText)
  const enums = findEnums(source)
  const enumTypes = createEnumTypeMap(enums)
  const aliases = createAliasMap(source, enums)
  const structs = findStructs(source).map((structInfo) => ({
    ...structInfo,
    registers: parseStructFields(structInfo.body, aliases, enumTypes)
  })).filter((structInfo) => structInfo.registers.length)

  if (!structs.length && !enums.length) {
    throw new Error('未找到可识别的结构体或枚举定义')
  }

  return {
    enums,
    enumVariablesByName: parseEnumVariables(source, enums, aliases),
    structs,
    variablesByName: parseStructVariables(source, structs, aliases)
  }
}

module.exports = {
  parseStructCatalog,
  parseStructDefinition,
  stripComments
}
