/**
 * GigLez RF Protocol Database
 * JavaScript port of Python protocol_database.py
 *
 * Contains timing signatures for 18 known RF protocols
 * @module protocol-database
 */

export class ProtocolSignature {
  constructor({
    name,
    category,
    manufacturer = null,
    model = null,
    frequency = 433920000,
    frequencyTolerance = 100000,
    shortPulseUs = 500,
    longPulseUs = 1000,
    timingTolerance = 0.2,
    preamblePattern = null,
    syncPattern = null,
    minBits = 24,
    maxBits = 64,
    typicalPulseCount = 100,
    minConfidence = 0.6
  }) {
    this.name = name;
    this.category = category;
    this.manufacturer = manufacturer;
    this.model = model;
    this.frequency = frequency;
    this.frequencyTolerance = frequencyTolerance;
    this.shortPulseUs = shortPulseUs;
    this.longPulseUs = longPulseUs;
    this.timingTolerance = timingTolerance;
    this.preamblePattern = preamblePattern;
    this.syncPattern = syncPattern;
    this.minBits = minBits;
    this.maxBits = maxBits;
    this.typicalPulseCount = typicalPulseCount;
    this.minConfidence = minConfidence;
  }

  /**
   * Check if observed timing matches this protocol
   * @param {number} shortUs - Observed short pulse width (microseconds)
   * @param {number} longUs - Observed long pulse width (microseconds)
   * @returns {boolean}
   */
  matchesTiming(shortUs, longUs) {
    const shortMin = this.shortPulseUs * (1 - this.timingTolerance);
    const shortMax = this.shortPulseUs * (1 + this.timingTolerance);
    const longMin = this.longPulseUs * (1 - this.timingTolerance);
    const longMax = this.longPulseUs * (1 + this.timingTolerance);

    return (shortMin <= shortUs && shortUs <= shortMax) &&
           (longMin <= longUs && longUs <= longMax);
  }

  /**
   * Check if frequency matches this protocol
   * @param {number} freq - Observed frequency (Hz)
   * @returns {boolean}
   */
  matchesFrequency(freq) {
    return Math.abs(freq - this.frequency) <= this.frequencyTolerance;
  }
}

// Weather Sensor Protocols
export const WEATHER_SENSORS = [
  new ProtocolSignature({
    name: 'Oregon Scientific v2.1',
    category: 'Weather Sensor',
    manufacturer: 'Oregon Scientific',
    shortPulseUs: 488,
    longPulseUs: 976,
    preamblePattern: '10101010101010101010101010101010', // 32-bit preamble
    syncPattern: '1000',
    minBits: 64,
    maxBits: 128,
    typicalPulseCount: 200
  }),
  new ProtocolSignature({
    name: 'Oregon Scientific v3.0',
    category: 'Weather Sensor',
    manufacturer: 'Oregon Scientific',
    shortPulseUs: 500,
    longPulseUs: 1000,
    preamblePattern: '101010101010101010101010101010101010101010101010', // 48-bit preamble
    syncPattern: '1000',
    minBits: 64,
    maxBits: 128,
    typicalPulseCount: 250
  }),
  new ProtocolSignature({
    name: 'Acurite Tower Sensor',
    category: 'Weather Sensor',
    manufacturer: 'Acurite',
    shortPulseUs: 220,
    longPulseUs: 440,
    syncPattern: '10',
    minBits: 56,
    maxBits: 64,
    typicalPulseCount: 130
  }),
  new ProtocolSignature({
    name: 'Acurite 5n1 Weather Station',
    category: 'Weather Sensor',
    manufacturer: 'Acurite',
    shortPulseUs: 220,
    longPulseUs: 440,
    minBits: 64,
    maxBits: 80,
    typicalPulseCount: 160
  }),
  new ProtocolSignature({
    name: 'LaCrosse TX141TH-Bv2',
    category: 'Weather Sensor',
    manufacturer: 'LaCrosse',
    shortPulseUs: 500,
    longPulseUs: 1000,
    preamblePattern: '10101010', // 8-bit preamble
    minBits: 40,
    maxBits: 48,
    typicalPulseCount: 100
  }),
  new ProtocolSignature({
    name: 'Nexus Temperature/Humidity',
    category: 'Weather Sensor',
    manufacturer: 'Nexus',
    shortPulseUs: 500,
    longPulseUs: 1000,
    preamblePattern: '11111111', // 8-bit preamble
    minBits: 36,
    maxBits: 40,
    typicalPulseCount: 90
  }),
  new ProtocolSignature({
    name: 'Ambient Weather F007TH',
    category: 'Weather Sensor',
    manufacturer: 'Ambient Weather',
    shortPulseUs: 500,
    longPulseUs: 1000,
    minBits: 64,
    maxBits: 72,
    typicalPulseCount: 150
  })
];

// Garage Door Opener Protocols
export const GARAGE_DOOR_OPENERS = [
  new ProtocolSignature({
    name: 'Princeton',
    category: 'Garage Door Opener',
    manufacturer: null,
    shortPulseUs: 400,
    longPulseUs: 1200,
    preamblePattern: '1111', // 4-bit preamble
    syncPattern: '10',
    minBits: 24,
    maxBits: 32,
    typicalPulseCount: 60
  }),
  new ProtocolSignature({
    name: 'Chamberlain/LiftMaster',
    category: 'Garage Door Opener',
    manufacturer: 'Chamberlain',
    shortPulseUs: 300,
    longPulseUs: 900,
    minBits: 32,
    maxBits: 40,
    typicalPulseCount: 80,
    frequency: 315000000 // 315 MHz
  }),
  new ProtocolSignature({
    name: 'Linear MegaCode',
    category: 'Garage Door Opener',
    manufacturer: 'Linear',
    shortPulseUs: 250,
    longPulseUs: 500,
    minBits: 32,
    maxBits: 32,
    typicalPulseCount: 70,
    frequency: 318000000 // 318 MHz
  })
];

// Doorbell Protocols
export const DOORBELLS = [
  new ProtocolSignature({
    name: 'Honeywell Doorbell',
    category: 'Doorbell',
    manufacturer: 'Honeywell',
    shortPulseUs: 175,
    longPulseUs: 340,
    minBits: 48,
    maxBits: 48,
    typicalPulseCount: 100
  })
];

// TPMS Protocols
export const TIRE_PRESSURE = [
  new ProtocolSignature({
    name: 'Toyota TPMS',
    category: 'TPMS',
    manufacturer: 'Toyota',
    shortPulseUs: 50,
    longPulseUs: 100,
    minBits: 64,
    maxBits: 80,
    typicalPulseCount: 160,
    frequency: 315000000 // 315 MHz
  }),
  new ProtocolSignature({
    name: 'Schrader TPMS',
    category: 'TPMS',
    manufacturer: 'Schrader',
    shortPulseUs: 50,
    longPulseUs: 100,
    minBits: 64,
    maxBits: 80,
    typicalPulseCount: 160,
    frequency: 433920000 // 433.92 MHz
  })
];

// Security Sensor Protocols
export const SECURITY_SENSORS = [
  new ProtocolSignature({
    name: 'Magellan',
    category: 'Security Sensor',
    manufacturer: 'Paradox',
    shortPulseUs: 250,
    longPulseUs: 500,
    minBits: 32,
    maxBits: 48,
    typicalPulseCount: 80,
    frequency: 433920000
  })
];

// Remote Control Protocols
export const REMOTE_CONTROLS = [
  new ProtocolSignature({
    name: 'PT2262',
    category: 'Remote Control',
    manufacturer: null,
    shortPulseUs: 350,
    longPulseUs: 1050,
    preamblePattern: '1111', // 4-bit preamble
    minBits: 24,
    maxBits: 24,
    typicalPulseCount: 50
  }),
  new ProtocolSignature({
    name: 'PT2260',
    category: 'Remote Control',
    manufacturer: null,
    shortPulseUs: 300,
    longPulseUs: 900,
    minBits: 24,
    maxBits: 24,
    typicalPulseCount: 50
  }),
  new ProtocolSignature({
    name: 'EV1527',
    category: 'Remote Control',
    manufacturer: null,
    shortPulseUs: 300,
    longPulseUs: 900,
    minBits: 24,
    maxBits: 24,
    typicalPulseCount: 50
  }),
  new ProtocolSignature({
    name: 'HCS301',
    category: 'Remote Control',
    manufacturer: 'Microchip',
    shortPulseUs: 400,
    longPulseUs: 800,
    minBits: 66,
    maxBits: 66,
    typicalPulseCount: 140
  })
];

// Combined protocol list
export const ALL_PROTOCOLS = [
  ...WEATHER_SENSORS,
  ...GARAGE_DOOR_OPENERS,
  ...DOORBELLS,
  ...TIRE_PRESSURE,
  ...SECURITY_SENSORS,
  ...REMOTE_CONTROLS
];

/**
 * Protocol Database class for querying and indexing protocols
 */
export class ProtocolDatabase {
  constructor() {
    this.protocols = ALL_PROTOCOLS;
    this._byCategory = {};
    this._byFrequency = {};
    this._indexProtocols();
  }

  /**
   * Build indexes for fast lookup
   * @private
   */
  _indexProtocols() {
    for (const proto of this.protocols) {
      // Index by category
      if (!this._byCategory[proto.category]) {
        this._byCategory[proto.category] = [];
      }
      this._byCategory[proto.category].push(proto);

      // Index by frequency (rounded to MHz)
      const freqMHz = Math.round(proto.frequency / 1_000_000);
      if (!this._byFrequency[freqMHz]) {
        this._byFrequency[freqMHz] = [];
      }
      this._byFrequency[freqMHz].push(proto);
    }
  }

  /**
   * Find protocols matching timing characteristics
   * @param {number} shortUs - Short pulse width (microseconds)
   * @param {number} longUs - Long pulse width (microseconds)
   * @param {number} [frequency] - Optional frequency filter (Hz)
   * @returns {ProtocolSignature[]}
   */
  findByTiming(shortUs, longUs, frequency = null) {
    let candidates = this.protocols;

    if (frequency) {
      const freqMHz = Math.round(frequency / 1_000_000);
      candidates = this._byFrequency[freqMHz] || this.protocols;
    }

    const matches = [];
    for (const proto of candidates) {
      if (proto.matchesTiming(shortUs, longUs)) {
        if (!frequency || proto.matchesFrequency(frequency)) {
          matches.push(proto);
        }
      }
    }

    return matches;
  }

  /**
   * Find protocols by category
   * @param {string} category - Category name
   * @returns {ProtocolSignature[]}
   */
  findByCategory(category) {
    return this._byCategory[category] || [];
  }

  /**
   * Find protocols by frequency
   * @param {number} frequency - Frequency in Hz
   * @returns {ProtocolSignature[]}
   */
  findByFrequency(frequency) {
    const matches = [];
    for (const proto of this.protocols) {
      if (proto.matchesFrequency(frequency)) {
        matches.push(proto);
      }
    }
    return matches;
  }

  /**
   * Get all protocols
   * @returns {ProtocolSignature[]}
   */
  getAll() {
    return this.protocols;
  }

  /**
   * Get database statistics
   * @returns {Object}
   */
  getStatistics() {
    return {
      totalProtocols: this.protocols.length,
      categories: Object.keys(this._byCategory),
      byCategory: Object.fromEntries(
        Object.entries(this._byCategory).map(([cat, protos]) => [cat, protos.length])
      ),
      frequencyBands: [...new Set(
        this.protocols.map(p => Math.round(p.frequency / 1_000_000))
      )].sort((a, b) => a - b)
    };
  }
}

// Export singleton instance
export const protocolDb = new ProtocolDatabase();
