#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2018 Takashi Sakamoto

import sys
import errno
from pprint import PrettyPrinter
from struct import unpack

from hinawa_utils.ieee1212.config_rom_lexer import Ieee1212ConfigRomLexer
from hinawa_utils.ieee1394.config_rom_parser import Ieee1394ConfigRomParser

import gi
gi.require_version('Hinawa', '4.0')
from gi.repository import Hinawa

if len(sys.argv) < 2:
    print('At least one argument is required for firewire character device.')
    sys.exit(errno.EINVAL)
path = sys.argv[1]

ops = ('parse', 'lex')
if len(sys.argv) < 3:
    op = 'parse'
elif sys.argv[2] in ops:
    op = sys.argv[2]
else:
    print('Invalid operation: {0}'.format(sys.argv[2]))
    sys.exit()

# For Echo Audio Fireworks series.


def handle_echoaudio_keys(key_id, type_name, data):
    OUI_COMPANIES = {
        0x000ff2: 'Loud Technologies Inc.',
        0x001486: 'Echo Digital Audio Corporation',
    }
    if key_id == 0x08 and type_name == 'IMMEDIATE':
        if data in OUI_COMPANIES:
            name = OUI_COMPANIES[data]
        else:
            name = data
        return ['MANUFACTURER', name]

    return None

# For Apple iSight.


def handle_iidc_v1_30_keys(key_id, type_name, data):
    if key_id == 0x00 and type_name == 'CSR_OFFSET':
        return ['IIDC_COMMAND_REGS_BASE', data]
    return None


def handle_isight_audio_keys(key_id, type_name, data):
    if key_id == 0x00 and type_name == 'CSR_OFFSET':
        return ['ISIGHT_AUDIO_REG_BASE', data]
    return None


def handle_isight_factory_keys(key_id, type_name, data):
    LABELS = {
        0x01: 'ISIGHT_FACTORY_REG_A',
        0x02: 'ISIGHT_FACTORY_REG_B',
        0x04: 'ISIGHT_FACTORY_REG_C',
        0x05: 'ISIGHT_FACTORY_REG_D',
    }
    if type_name == 'CSR_OFFSET' and key_id in LABELS:
        return [LABELS[key_id], data]
    elif key_id == 0x38 and type_name == 'IMMEDIATE':
        return ['ISIGHT_FACTORY_DATA', data]
    return None


def handle_isight_iris_keys(key_id, type_name, data):
    if key_id == 0x3c and type_name == 'IMMEDIATE':
        return ['ISIGHT_IRIS_DATA', data]
    elif key_id == 0x00 and type_name == 'CSR_OFFSET':
        return ['ISIGHT_IRIS_REGS_BASE', data]
    return None

# For TASCAM FireWire series.


def handle_teac_keys(key_id, type_name, data):
    if key_id == 0x02 and type_name == 'LEAF':
        content = data[8:].decode('US-ASCII') + '\0'
        return ['MODEL_NAME', content[:content.find('\0')]]
    return None

# For Digidesign Digi 00x series.


def handle_microsoft_keys(key_id, type_name, data):
    if key_id == 0x01 and type_name == 'LEAF':
        width = data[0] >> 4
        character_set = ((data[0] & 0x0f) << 8) | data[1]
        language = unpack('>H', data[2:4])[0]

        if character_set != 0x00 or language != 0x00:
            raise ValueError('Invalid data in descriptor leaf.')

        content = data[4:].decode('US-ASCII') + '\0'
        return content[:content.find('\0')]

    return None

# For Cool Stream iSweet.


def handle_ame_root_keys(key_id, type_name, data):
    if key_id == 0x06 and type_name == 'IMMEDIATE':
        return ['DEVICE_ID', data]
    return None


def handle_iidc_v1_04_keys(key_id, type_name, data):
    if key_id == 0x00 and type_name == 'CSR_OFFSET':
        return ['COMMAND_REGS_BASE', data]
    return None


def handle_ame_unit_dep_keys(key_id, type_name, data):
    if key_id == 0x02 and type_name == 'LEAF':
        descriptor_type = data[0]
        specifier_id = (data[1] << 16) | unpack('>H', data[2:4])[0]
        if descriptor_type != 0x00 or specifier_id != 0x00:
            raise ValueError('Invalid data in vendor-dep descriptor leaf.')

        width = data[4] >> 4
        character_set = ((data[4] & 0x0f) << 8) | data[5]
        language = unpack('>H', data[6:8])[0]
        if character_set != 0x00 or language != 0x00:
            raise ValueError('Invalid data in descriptor leaf.')

        content = data[8:].decode('US-ASCII') + '\0'
        return ['MODEL_NAME', content[:content.find('\0')]]
    return None


pp = PrettyPrinter(indent=2, compact=False)

node = Hinawa.FwNode()
node.open(path, 0)

_, data = node.get_config_rom()

if op == 'lex':
    entries = Ieee1212ConfigRomLexer.detect_entries(data)
    pp.pprint(entries)
else:
    parser = Ieee1394ConfigRomParser()
    parser.add_vendor_dep_handle(0x001486, handle_echoaudio_keys)
    parser.add_spec_dep_handle(0x00a02d, 0x000102, handle_iidc_v1_30_keys)
    parser.add_spec_dep_handle(0x000a27, 0x000010, handle_isight_audio_keys)
    parser.add_spec_dep_handle(0x000a27, 0x000011, handle_isight_factory_keys)
    parser.add_spec_dep_handle(0x000a27, 0x000012, handle_isight_iris_keys)
    parser.add_spec_dep_handle(0x00022e, 0x800000, handle_teac_keys)
    parser.add_spec_dep_handle(0x00022e, 0x800003, handle_teac_keys)
    parser.add_spec_dep_handle(0x00022e, 0x800004, handle_teac_keys)
    parser.add_vendor_dep_handle(0x0050f2, handle_microsoft_keys)
    parser.add_spec_dep_handle(0x00a02d, 0x000100, handle_iidc_v1_04_keys)
    parser.add_spec_dep_handle(0x00a02d, 0x000100, handle_ame_unit_dep_keys)
    parser.add_vendor_dep_handle(0x0002f0, handle_ame_root_keys)
    info = parser.parse_rom(data)
    pp.pprint(info)
