Source: animation/AnimationNameMapper.js

/**
 * @fileoverview Multi-scheme animation name mapper for Owen Animation System
 * @module animation/AnimationNameMapper
 */

/**
 * Multi-scheme animation name mapper for Owen Animation System
 * Supports legacy, artist-friendly, and hierarchical naming schemes
 * @class
 */
export class AnimationNameMapper {
  constructor () {
    // Mapping between different naming schemes
    this.schemeMappings = new Map()
    this.reverseMappings = new Map()
    this.patterns = new Map()

    this.initializeMappings()
  }

  /**
     * Initialize all naming scheme mappings and patterns
     * @private
     */
  initializeMappings () {
    // Core animation definitions with all naming scheme variants
    const animations = [
      // Wait state animations
      {
        legacy: 'wait_idle_L',
        artist: 'Owen_WaitIdle',
        hierarchical: 'owen.state.wait.idle.loop',
        semantic: 'OwenWaitIdleLoop',
        state: 'wait',
        emotion: '',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'wait_pickNose_Q',
        artist: 'Owen_PickNose',
        hierarchical: 'owen.quirk.wait.picknose',
        semantic: 'OwenQuirkPickNose',
        state: 'wait',
        emotion: '',
        type: 'quirk',
        category: 'quirk'
      },
      {
        legacy: 'wait_wave_Q',
        artist: 'Owen_Wave',
        hierarchical: 'owen.quirk.wait.wave',
        semantic: 'OwenQuirkWave',
        state: 'wait',
        emotion: '',
        type: 'quirk',
        category: 'quirk'
      },
      // React state animations
      {
        legacy: 'react_idle_L',
        artist: 'Owen_ReactIdle',
        hierarchical: 'owen.state.react.idle.loop',
        semantic: 'OwenReactIdleLoop',
        state: 'react',
        emotion: '',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'react_an_L',
        artist: 'Owen_ReactAngry',
        hierarchical: 'owen.state.react.emotion.angry.loop',
        semantic: 'OwenReactAngryLoop',
        state: 'react',
        emotion: 'angry',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'react_sh_L',
        artist: 'Owen_ReactShocked',
        hierarchical: 'owen.state.react.emotion.shocked.loop',
        semantic: 'OwenReactShockedLoop',
        state: 'react',
        emotion: 'shocked',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'react_ha_L',
        artist: 'Owen_ReactHappy',
        hierarchical: 'owen.state.react.emotion.happy.loop',
        semantic: 'OwenReactHappyLoop',
        state: 'react',
        emotion: 'happy',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'react_sa_L',
        artist: 'Owen_ReactSad',
        hierarchical: 'owen.state.react.emotion.sad.loop',
        semantic: 'OwenReactSadLoop',
        state: 'react',
        emotion: 'sad',
        type: 'loop',
        category: 'state'
      },
      // Type state animations
      {
        legacy: 'type_idle_L',
        artist: 'Owen_TypeIdle',
        hierarchical: 'owen.state.type.idle.loop',
        semantic: 'OwenTypeIdleLoop',
        state: 'type',
        emotion: '',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'type_an_L',
        artist: 'Owen_TypeAngry',
        hierarchical: 'owen.state.type.emotion.angry.loop',
        semantic: 'OwenTypeAngryLoop',
        state: 'type',
        emotion: 'angry',
        type: 'loop',
        category: 'state'
      },
      {
        legacy: 'type_sh_L',
        artist: 'Owen_TypeShocked',
        hierarchical: 'owen.state.type.emotion.shocked.loop',
        semantic: 'OwenTypeShockedLoop',
        state: 'type',
        emotion: 'shocked',
        type: 'loop',
        category: 'state'
      },
      // Sleep state animations
      {
        legacy: 'sleep_idle_L',
        artist: 'Owen_SleepIdle',
        hierarchical: 'owen.state.sleep.idle.loop',
        semantic: 'OwenSleepIdleLoop',
        state: 'sleep',
        emotion: '',
        type: 'loop',
        category: 'state'
      },
      // Transition animations
      {
        legacy: 'wait_2react_T',
        artist: 'Owen_WaitToReact',
        hierarchical: 'owen.transition.wait.to.react',
        semantic: 'OwenTransitionWaitToReact',
        fromState: 'wait',
        toState: 'react',
        emotion: '',
        type: 'transition',
        category: 'transition'
      },
      {
        legacy: 'react_2type_T',
        artist: 'Owen_ReactToType',
        hierarchical: 'owen.transition.react.to.type',
        semantic: 'OwenTransitionReactToType',
        fromState: 'react',
        toState: 'type',
        emotion: '',
        type: 'transition',
        category: 'transition'
      },
      {
        legacy: 'react_an2type_T',
        artist: 'Owen_ReactAngryToType',
        hierarchical: 'owen.transition.react.to.type.emotion.angry',
        semantic: 'OwenTransitionReactToTypeAngry',
        fromState: 'react',
        toState: 'type',
        emotion: 'angry',
        type: 'transition',
        category: 'transition'
      },
      {
        legacy: 'type_2wait_T',
        artist: 'Owen_TypeToWait',
        hierarchical: 'owen.transition.type.to.wait',
        semantic: 'OwenTransitionTypeToWait',
        fromState: 'type',
        toState: 'wait',
        emotion: '',
        type: 'transition',
        category: 'transition'
      },
      {
        legacy: 'sleep_2wait_T',
        artist: 'Owen_SleepToWait',
        hierarchical: 'owen.transition.sleep.to.wait',
        semantic: 'OwenTransitionSleepToWait',
        fromState: 'sleep',
        toState: 'wait',
        emotion: '',
        type: 'transition',
        category: 'transition'
      }
    ]

    // Build bidirectional mappings
    animations.forEach(anim => {
      const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']

      schemes.forEach(scheme1 => {
        schemes.forEach(scheme2 => {
          if (scheme1 !== scheme2) {
            this.schemeMappings.set(anim[scheme1], anim[scheme2])
          }
        })

        // Also map to animation definition
        this.schemeMappings.set(anim[scheme1], anim)
      })
    })

    // Initialize pattern matchers for auto-detection
    this.initializePatterns()
  }

  /**
     * Initialize pattern matchers for different naming schemes
     * @private
     */
  initializePatterns () {
    // Pattern matchers for different naming schemes
    this.patterns.set('legacy', [
      {
        regex: /^(\w+)_(\w+)_([LTQ])$/,
        extract: (match) => ({
          state: match[1],
          action: match[2],
          type: match[3] === 'L' ? 'loop' : match[3] === 'T' ? 'transition' : 'quirk'
        })
      },
      {
        regex: /^(\w+)_(\w{2})_([LTQ])$/,
        extract: (match) => ({
          state: match[1],
          emotion: this.mapEmotionCode(match[2]),
          type: match[3] === 'L' ? 'loop' : match[3] === 'T' ? 'transition' : 'quirk'
        })
      },
      {
        regex: /^(\w+)_(\w{2})?2(\w+)_T$/,
        extract: (match) => ({
          fromState: match[1],
          emotion: match[2] ? this.mapEmotionCode(match[2]) : '',
          toState: match[3],
          type: 'transition'
        })
      },
      {
        regex: /^(\w+)_2(\w+)_T$/,
        extract: (match) => ({
          fromState: match[1],
          toState: match[2],
          type: 'transition'
        })
      }
    ])

    this.patterns.set('artist', [
      {
        regex: /^Owen_(\w+)$/,
        extract: (match) => ({
          action: match[1],
          scheme: 'artist'
        })
      },
      {
        regex: /^Owen_(\w+)To(\w+)$/,
        extract: (match) => ({
          fromState: match[1].toLowerCase(),
          toState: match[2].toLowerCase(),
          type: 'transition'
        })
      },
      {
        regex: /^Owen_(\w+)(Angry|Happy|Sad|Shocked)$/,
        extract: (match) => ({
          state: match[1].toLowerCase(),
          emotion: match[2].toLowerCase(),
          type: 'loop'
        })
      },
      {
        regex: /^Owen_(\w+)(Angry|Happy|Sad|Shocked)To(\w+)$/,
        extract: (match) => ({
          fromState: match[1].toLowerCase(),
          emotion: match[2].toLowerCase(),
          toState: match[3].toLowerCase(),
          type: 'transition'
        })
      }
    ])

    this.patterns.set('hierarchical', [
      {
        regex: /^owen\.(\w+)\.(\w+)\.(\w+)(?:\.(\w+))?(?:\.(\w+))?$/,
        extract: (match) => ({
          category: match[1],
          subcategory: match[2],
          action: match[3],
          modifier: match[4],
          type: match[5] || match[4]
        })
      }
    ])

    this.patterns.set('semantic', [
      {
        regex: /^Owen(\w+)(\w+)(\w+)$/,
        extract: (match) => ({
          category: match[1].toLowerCase(),
          action: match[2].toLowerCase(),
          type: match[3].toLowerCase()
        })
      }
    ])
  }

  /**
     * Map emotion codes to full names
     * @private
     * @param {string} code - Emotion code
     * @returns {string} Full emotion name
     */
  mapEmotionCode (code) {
    const emotionMap = {
      an: 'angry',
      sh: 'shocked',
      ha: 'happy',
      sa: 'sad',
      '': 'neutral'
    }
    return emotionMap[code] || code
  }

  /**
     * Convert any animation name to any other scheme
     * @param {string} fromName - Source animation name
     * @param {string} targetScheme - Target naming scheme ('legacy', 'artist', 'hierarchical', 'semantic')
     * @returns {string} Converted animation name
     */
  convert (fromName, targetScheme = 'hierarchical') {
    // Direct lookup first
    const directMapping = this.schemeMappings.get(fromName)
    if (directMapping && typeof directMapping === 'object') {
      return directMapping[targetScheme] || fromName
    }

    // Pattern-based conversion
    const detected = this.detectScheme(fromName)
    if (detected) {
      return this.generateName(detected.info, targetScheme)
    }

    console.warn(`Could not convert animation name: ${fromName}`)
    return fromName
  }

  /**
     * Detect which naming scheme is being used
     * @param {string} name - Animation name to analyze
     * @returns {Object|null} Detection result with scheme and extracted info
     */
  detectScheme (name) {
    for (const [scheme, patterns] of this.patterns) {
      for (const pattern of patterns) {
        const match = name.match(pattern.regex)
        if (match) {
          return {
            scheme,
            info: pattern.extract(match),
            originalName: name
          }
        }
      }
    }
    return null
  }

  /**
     * Generate animation name in target scheme
     * @private
     * @param {Object} info - Animation information
     * @param {string} targetScheme - Target naming scheme
     * @returns {string} Generated animation name
     */
  generateName (info, targetScheme) {
    switch (targetScheme) {
      case 'legacy':
        return this.generateLegacyName(info)
      case 'artist':
        return this.generateArtistName(info)
      case 'hierarchical':
        return this.generateHierarchicalName(info)
      case 'semantic':
        return this.generateSemanticName(info)
      default:
        return null
    }
  }

  /**
     * Generate legacy format name
     * @private
     * @param {Object} info - Animation information
     * @returns {string} Legacy format name
     */
  generateLegacyName (info) {
    const typeMap = { loop: 'L', transition: 'T', quirk: 'Q' }
    const emotionMap = { angry: 'an', shocked: 'sh', happy: 'ha', sad: 'sa' }

    if (info.type === 'transition' && info.fromState && info.toState) {
      const emotionPart = info.emotion ? emotionMap[info.emotion] || '' : ''
      return emotionPart
        ? `${info.fromState}_${emotionPart}2${info.toState}_T`
        : `${info.fromState}_2${info.toState}_T`
    }

    const state = info.state || info.fromState || 'wait'
    const action = info.action || (info.emotion ? emotionMap[info.emotion] : 'idle')
    const type = typeMap[info.type] || 'L'

    return `${state}_${action}_${type}`
  }

  /**
     * Generate artist-friendly format name
     * @private
     * @param {Object} info - Animation information
     * @returns {string} Artist format name
     */
  generateArtistName (info) {
    const parts = ['Owen']

    if (info.type === 'transition') {
      const from = this.capitalize(info.fromState || info.state)
      const to = this.capitalize(info.toState)
      if (info.emotion) {
        parts.push(`${from}${this.capitalize(info.emotion)}To${to}`)
      } else {
        parts.push(`${from}To${to}`)
      }
    } else {
      if (info.state) parts.push(this.capitalize(info.state))
      if (info.action && info.action !== 'idle') parts.push(this.capitalize(info.action))
      if (info.emotion) parts.push(this.capitalize(info.emotion))
    }

    return parts.join('_')
  }

  /**
     * Generate hierarchical format name
     * @private
     * @param {Object} info - Animation information
     * @returns {string} Hierarchical format name
     */
  generateHierarchicalName (info) {
    const parts = ['owen']

    if (info.category) {
      parts.push(info.category)
    } else if (info.type === 'transition') {
      parts.push('transition')
    } else if (info.type === 'quirk') {
      parts.push('quirk')
    } else {
      parts.push('state')
    }

    if (info.fromState && info.toState) {
      // Transition
      parts.push(info.fromState, 'to', info.toState)
    } else if (info.state) {
      parts.push(info.state)
    }

    if (info.action && info.action !== 'idle') parts.push(info.action)
    if (info.emotion) parts.push('emotion', info.emotion)
    if (info.type) parts.push(info.type)

    return parts.join('.')
  }

  /**
     * Generate semantic format name
     * @private
     * @param {Object} info - Animation information
     * @returns {string} Semantic format name
     */
  generateSemanticName (info) {
    const parts = ['Owen']

    if (info.type === 'transition') {
      parts.push('Transition')
      if (info.fromState) parts.push(this.capitalize(info.fromState))
      parts.push('To')
      if (info.toState) parts.push(this.capitalize(info.toState))
      if (info.emotion) parts.push(this.capitalize(info.emotion))
    } else {
      if (info.type === 'quirk') parts.push('Quirk')
      if (info.state) parts.push(this.capitalize(info.state))
      if (info.action && info.action !== 'idle') parts.push(this.capitalize(info.action))
      if (info.emotion) parts.push(this.capitalize(info.emotion))
      if (info.type && info.type !== 'quirk') parts.push(this.capitalize(info.type))
    }

    return parts.join('')
  }

  /**
     * Capitalize first letter of string
     * @private
     * @param {string} str - String to capitalize
     * @returns {string} Capitalized string
     */
  capitalize (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
  }

  /**
     * Get all possible names for an animation
     * @param {string} animationName - Source animation name
     * @returns {Object} Object with all naming scheme variants
     */
  getAllNames (animationName) {
    const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']
    const names = {}

    schemes.forEach(scheme => {
      names[scheme] = this.convert(animationName, scheme)
    })

    return names
  }

  /**
     * Batch convert multiple animations
     * @param {string[]} animations - Array of animation names
     * @param {string} targetScheme - Target naming scheme
     * @returns {Object} Mapping of original names to converted names
     */
  convertBatch (animations, targetScheme) {
    const converted = {}
    animations.forEach(name => {
      converted[name] = this.convert(name, targetScheme)
    })
    return converted
  }

  /**
     * Validate animation name format
     * @param {string} name - Animation name to validate
     * @returns {Object} Validation result with issues and suggestions
     */
  validateAnimationName (name) {
    const issues = []
    const suggestions = []

    // Check for common issues
    if (name.includes(' ')) {
      issues.push(`❌ "${name}" contains spaces - may cause issues`)
      suggestions.push(`💡 Suggestion: "${name.replace(/ /g, '_')}"`)
    }

    if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
      issues.push(`❌ "${name}" contains invalid characters`)
      suggestions.push('💡 Use only letters, numbers, dots, underscores, and hyphens')
    }

    if (name.length > 50) {
      issues.push(`⚠️ "${name}" is very long (${name.length} chars)`)
      suggestions.push('💡 Consider shortening the name')
    }

    const detected = this.detectScheme(name)
    if (!detected) {
      issues.push(`⚠️ "${name}" doesn't match any known naming pattern`)
      suggestions.push('💡 Consider using one of: legacy, artist, hierarchical, or semantic format')
    } else {
      suggestions.push(`✅ Detected as ${detected.scheme} scheme`)
    }

    return { issues, suggestions, detected }
  }
}