using System;
using System.Collections.ObjectModel;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Core;
using Antmicro.Renode.Core.Structure.Registers;
using Antmicro.Renode.Logging;
using System.Collections.Generic;

namespace Antmicro.Renode.Peripherals.IRQControllers
{
    public class ESP32S3 : IIRQController, INumberedGPIOOutput, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
    {
        public GPIO IRQ { get; private set; }
        public DoubleWordRegisterCollection RegistersCollection { get; }
        public long Size => 0x1000; // 0x1a4
        private readonly Machine machine;
        public IReadOnlyDictionary<int, IGPIO> Connections { get; }
        public long NumberOfLines => Connections.Count;
        private Destination[] destinations;
        private readonly bool[] routingTable;

        public ESP32S3(Machine machine)
        {
            this.machine = machine;

            IRQ = new GPIO();

            RegistersCollection = new DoubleWordRegisterCollection(this);
            Connections = new IGPIORedirector(100, HandleIRQConnect, HandleIRQDisconnect);

            DefineRegisters();
            Reset();
        }

        private void DefineRegisters()
        {
            // mac interrupt configuration register
            Registers.PRO_MAC_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "MAC_INTR_MAP") // this register used to map mac interrupt to one of core0's external interrupt
            ;

            // mac_nmi interrupt configuration register
            Registers.MAC_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "MAC_NMI_MAP") // this register used to map_nmi interrupt to one of core0's external interrupt
            ;

            // pwr interrupt configuration register
            Registers.PWR_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PWR_INTR_MAP") // this register used to map pwr interrupt to one of core0's external interrupt
            ;

            // bb interrupt configuration register
            Registers.BB_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "BB_INT_MAP") // this register used to map bb interrupt to one of core0's external interrupt
            ;

            // bb_mac interrupt configuration register
            Registers.BT_MAC_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "BT_MAC_INT_MAP") // this register used to map bb_mac interrupt to one of core0's external interrupt
            ;

            // bt_bb interrupt configuration register
            Registers.BT_BB_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "BT_BB_INT_MAP") // this register used to map bt_bb interrupt to one of core0's external interrupt
            ;

            // bt_bb_nmi interrupt configuration register
            Registers.BT_BB_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "BT_BB_NMI_MAP") // this register used to map bb_bt_nmi interrupt to one of core0's external interrupt
            ;

            // rwbt_irq interrupt configuration register
            Registers.RWBT_IRQ_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RWBT_IRQ_MAP") // this register used to map rwbt_irq interrupt to one of core0's external interrupt
            ;

            // rwble_irq interrupt configuration register
            Registers.RWBLE_IRQ_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RWBLE_IRQ_MAP") // this register used to map rwble_irq interrupt to one of core0's external interrupt
            ;

            // rwbt_nmi interrupt configuration register
            Registers.RWBT_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RWBT_NMI_MAP") // this register used to map mac rwbt_nmi to one of core0's external interrupt
            ;

            // rwble_nmi interrupt configuration register
            Registers.RWBLE_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RWBLE_NMI_MAP") // this register used to map rwble_nmi interrupt to one of core0's external interrupt
            ;

            // i2c_mst interrupt configuration register
            Registers.I2C_MST_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "I2C_MST_INT_MAP") // this register used to map i2c_mst interrupt to one of core0's external interrupt
            ;

            // slc0 interrupt configuration register
            Registers.SLC0_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SLC0_INTR_MAP") // this register used to map slc0 interrupt to one of core0's external interrupt
            ;

            // slc1 interrupt configuration register
            Registers.SLC1_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SLC1_INTR_MAP") // this register used to map slc1 interrupt to one of core0's external interrupt
            ;

            // uhci0 interrupt configuration register
            Registers.UHCI0_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "UHCI0_INTR_MAP") // this register used to map uhci0 interrupt to one of core0's external interrupt
            ;

            // uhci1 interrupt configuration register
            Registers.UHCI1_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "UHCI1_INTR_MAP") // this register used to map uhci1 interrupt to one of core0's external interrupt
            ;

            // gpio_interrupt_pro interrupt configuration register
            Registers.GPIO_INTERRUPT_PRO_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "GPIO_INTERRUPT_PRO_MAP") // this register used to map gpio_interrupt_pro interrupt to one of core0's external interrupt
            ;

            // gpio_interrupt_pro_nmi interrupt configuration register
            Registers.GPIO_INTERRUPT_PRO_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "GPIO_INTERRUPT_PRO_NMI_MAP") // this register used to map gpio_interrupt_pro_nmi interrupt to one of core0's external interrupt
            ;

            // gpio_interrupt_app interrupt configuration register
            Registers.GPIO_INTERRUPT_APP_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "GPIO_INTERRUPT_APP_MAP") // this register used to map gpio_interrupt_app interrupt to one of core0's external interrupt
            ;

            // gpio_interrupt_app_nmi interrupt configuration register
            Registers.GPIO_INTERRUPT_APP_NMI_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "GPIO_INTERRUPT_APP_NMI_MAP") // this register used to map gpio_interrupt_app_nmi interrupt to one of core0's external interrupt
            ;

            // spi_intr_1 interrupt configuration register
            Registers.SPI_INTR_1_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI_INTR_1_MAP") // this register used to map spi_intr_1 interrupt to one of core0's external interrupt
            ;

            // spi_intr_2 interrupt configuration register
            Registers.SPI_INTR_2_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI_INTR_2_MAP") // this register used to map spi_intr_2 interrupt to one of core0's external interrupt
            ;

            // spi_intr_3 interrupt configuration register
            Registers.SPI_INTR_3_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI_INTR_3_MAP") // this register used to map spi_intr_3 interrupt to one of core0's external interrupt
            ;

            // spi_intr_4 interrupt configuration register
            Registers.SPI_INTR_4_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI_INTR_4_MAP") // this register used to map spi_intr_4 interrupt to one of core0's external interrupt
            ;

            // lcd_cam interrupt configuration register
            Registers.LCD_CAM_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "LCD_CAM_INT_MAP") // this register used to map lcd_cam interrupt to one of core0's external interrupt
            ;

            // i2s0 interrupt configuration register
            Registers.I2S0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "I2S0_INT_MAP") // this register used to map i2s0 interrupt to one of core0's external interrupt
            ;

            // i2s1 interrupt configuration register
            Registers.I2S1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "I2S1_INT_MAP") // this register used to map i2s1 interrupt to one of core0's external interrupt
            ;

            // uart interrupt configuration register
            Registers.UART_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "UART_INTR_MAP") // this register used to map uart interrupt to one of core0's external interrupt
            ;

            // uart1 interrupt configuration register
            Registers.UART1_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "UART1_INTR_MAP") // this register used to map uart1 interrupt to one of core0's external interrupt
            ;

            // uart2 interrupt configuration register
            Registers.UART2_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "UART2_INTR_MAP") // this register used to map uart2 interrupt to one of core0's external interrupt
            ;

            // sdio_host interrupt configuration register
            Registers.SDIO_HOST_INTERRUPT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SDIO_HOST_INTERRUPT_MAP") // this register used to map sdio_host interrupt to one of core0's external interrupt
            ;

            // pwm0 interrupt configuration register
            Registers.PWM0_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PWM0_INTR_MAP") // this register used to map pwm0 interrupt to one of core0's external interrupt
            ;

            // pwm1 interrupt configuration register
            Registers.PWM1_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PWM1_INTR_MAP") // this register used to map pwm1 interrupt to one of core0's external interrupt
            ;

            // pwm2 interrupt configuration register
            Registers.PWM2_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PWM2_INTR_MAP") // this register used to map pwm2 interrupt to one of core0's external interrupt
            ;

            // pwm3 interrupt configuration register
            Registers.PWM3_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PWM3_INTR_MAP") // this register used to map pwm3 interrupt to one of core0's external interrupt
            ;

            // ledc interrupt configuration register
            Registers.LEDC_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "LEDC_INT_MAP") // this register used to map ledc interrupt to one of core0's external interrupt
            ;

            // efuse interrupt configuration register
            Registers.EFUSE_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "EFUSE_INT_MAP") // this register used to map efuse interrupt to one of core0's external interrupt
            ;

            // can interrupt configuration register
            Registers.CAN_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CAN_INT_MAP") // this register used to map can interrupt to one of core0's external interrupt
            ;

            // usb interrupt configuration register
            Registers.USB_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "USB_INTR_MAP") // this register used to map usb interrupt to one of core0's external interrupt
            ;

            // rtc_core interrupt configuration register
            Registers.RTC_CORE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RTC_CORE_INTR_MAP") // this register used to map rtc_core interrupt to one of core0's external interrupt
            ;

            // rmt interrupt configuration register
            Registers.RMT_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RMT_INTR_MAP") // this register used to map rmt interrupt to one of core0's external interrupt
            ;

            // pcnt interrupt configuration register
            Registers.PCNT_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PCNT_INTR_MAP") // this register used to map pcnt interrupt to one of core0's external interrupt
            ;

            // i2c_ext0 interrupt configuration register
            Registers.I2C_EXT0_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "I2C_EXT0_INTR_MAP") // this register used to map i2c_ext0 interrupt to one of core0's external interrupt
            ;

            // i2c_ext1 interrupt configuration register
            Registers.I2C_EXT1_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "I2C_EXT1_INTR_MAP") // this register used to map i2c_ext1 interrupt to one of core0's external interrupt
            ;

            // spi2_dma interrupt configuration register
            Registers.SPI2_DMA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI2_DMA_INT_MAP") // this register used to map spi2_dma interrupt to one of core0's external interrupt
            ;

            // spi3_dma interrupt configuration register
            Registers.SPI3_DMA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI3_DMA_INT_MAP") // this register used to map spi3_dma interrupt to one of core0's external interrupt
            ;

            // spi4_dma interrupt configuration register
            Registers.SPI4_DMA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI4_DMA_INT_MAP") // this register used to map spi4_dma interrupt to one of core0's external interrupt
            ;

            // wdg interrupt configuration register
            Registers.WDG_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "WDG_INT_MAP") // this register used to map wdg interrupt to one of core0's external interrupt
            ;

            // timer_int1 interrupt configuration register
            Registers.TIMER_INT1_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TIMER_INT1_MAP") // this register used to map timer_int1 interrupt to one of core0's external interrupt
            ;

            // timer_int2 interrupt configuration register
            Registers.TIMER_INT2_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TIMER_INT2_MAP") // this register used to map timer_int2 interrupt to one of core0's external interrupt
            ;

            // tg_t0 interrupt configuration register
            Registers.TG_T0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG_T0_INT_MAP") // this register used to map tg_t0 interrupt to one of core0's external interrupt
            ;

            // tg_t1 interrupt configuration register
            Registers.TG_T1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG_T1_INT_MAP") // this register used to map tg_t1 interrupt to one of core0's external interrupt
            ;

            // tg_wdt interrupt configuration register
            Registers.TG_WDT_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG_WDT_INT_MAP") // this register used to map rg_wdt interrupt to one of core0's external interrupt
            ;

            // tg1_t0 interrupt configuration register
            Registers.TG1_T0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG1_T0_INT_MAP") // this register used to map tg1_t0 interrupt to one of core0's external interrupt
            ;

            // tg1_t1 interrupt configuration register
            Registers.TG1_T1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG1_T1_INT_MAP") // this register used to map tg1_t1 interrupt to one of core0's external interrupt
            ;

            // tg1_wdt interrupt configuration register
            Registers.TG1_WDT_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "TG1_WDT_INT_MAP") // this register used to map tg1_wdt interrupt to one of core0's external interrupt
            ;

            // cache_ia interrupt configuration register
            Registers.CACHE_IA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CACHE_IA_INT_MAP") // this register used to map cache_ia interrupt to one of core0's external interrupt
            ;

            // systimer_target0 interrupt configuration register
            Registers.SYSTIMER_TARGET0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SYSTIMER_TARGET0_INT_MAP") // this register used to map systimer_target0 interrupt to one of core0's external interrupt
            ;

            // systimer_target1 interrupt configuration register
            Registers.SYSTIMER_TARGET1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SYSTIMER_TARGET1_INT_MAP") // this register used to map systimer_target1 interrupt to one of core0's external interrupt
            ;

            // systimer_target2 interrupt configuration register
            Registers.SYSTIMER_TARGET2_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SYSTIMER_TARGET2_INT_MAP") // this register used to map systimer_target2 interrupt to one of core0's external interrupt
            ;

            // spi_mem_reject interrupt configuration register
            Registers.SPI_MEM_REJECT_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SPI_MEM_REJECT_INTR_MAP") // this register used to map spi_mem_reject interrupt to one of core0's external interrupt
            ;

            // dcache_prelaod interrupt configuration register
            Registers.DCACHE_PRELOAD_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DCACHE_PRELOAD_INT_MAP") // this register used to map dcache_prelaod interrupt to one of core0's external interrupt
            ;

            // icache_preload interrupt configuration register
            Registers.ICACHE_PRELOAD_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "ICACHE_PRELOAD_INT_MAP") // this register used to map icache_preload interrupt to one of core0's external interrupt
            ;

            // dcache_sync interrupt configuration register
            Registers.DCACHE_SYNC_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DCACHE_SYNC_INT_MAP") // this register used to map dcache_sync interrupt to one of core0's external interrupt
            ;

            // icache_sync interrupt configuration register
            Registers.ICACHE_SYNC_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "ICACHE_SYNC_INT_MAP") // this register used to map icache_sync interrupt to one of core0's external interrupt
            ;

            // apb_adc interrupt configuration register
            Registers.APB_ADC_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "APB_ADC_INT_MAP") // this register used to map apb_adc interrupt to one of core0's external interrupt
            ;

            // dma_in_ch0 interrupt configuration register
            Registers.DMA_IN_CH0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_IN_CH0_INT_MAP") // this register used to map dma_in_ch0 interrupt to one of core0's external interrupt
            ;

            // dma_in_ch1 interrupt configuration register
            Registers.DMA_IN_CH1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_IN_CH1_INT_MAP") // this register used to map dma_in_ch1 interrupt to one of core0's external interrupt
            ;

            // dma_in_ch2 interrupt configuration register
            Registers.DMA_IN_CH2_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_IN_CH2_INT_MAP") // this register used to map dma_in_ch2 interrupt to one of core0's external interrupt
            ;

            // dma_in_ch3 interrupt configuration register
            Registers.DMA_IN_CH3_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_IN_CH3_INT_MAP") // this register used to map dma_in_ch3 interrupt to one of core0's external interrupt
            ;

            // dma_in_ch4 interrupt configuration register
            Registers.DMA_IN_CH4_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_IN_CH4_INT_MAP") // this register used to map dma_in_ch4 interrupt to one of core0's external interrupt
            ;

            // dma_out_ch0 interrupt configuration register
            Registers.DMA_OUT_CH0_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_OUT_CH0_INT_MAP") // this register used to map dma_out_ch0 interrupt to one of core0's external interrupt
            ;

            // dma_out_ch1 interrupt configuration register
            Registers.DMA_OUT_CH1_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_OUT_CH1_INT_MAP") // this register used to map dma_out_ch1 interrupt to one of core0's external interrupt
            ;

            // dma_out_ch2 interrupt configuration register
            Registers.DMA_OUT_CH2_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_OUT_CH2_INT_MAP") // this register used to map dma_out_ch2 interrupt to one of core0's external interrupt
            ;

            // dma_out_ch3 interrupt configuration register
            Registers.DMA_OUT_CH3_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_OUT_CH3_INT_MAP") // this register used to map dma_out_ch3 interrupt to one of core0's external interrupt
            ;

            // dma_out_ch4 interrupt configuration register
            Registers.DMA_OUT_CH4_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_OUT_CH4_INT_MAP") // this register used to map dma_out_ch4 interrupt to one of core0's external interrupt
            ;

            // rsa interrupt configuration register
            Registers.RSA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "RSA_INT_MAP") // this register used to map rsa interrupt to one of core0's external interrupt
            ;

            // aes interrupt configuration register
            Registers.AES_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "AES_INT_MAP") // this register used to map aes interrupt to one of core0's external interrupt
            ;

            // sha interrupt configuration register
            Registers.SHA_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "SHA_INT_MAP") // this register used to map sha interrupt to one of core0's external interrupt
            ;

            // cpu_intr_from_cpu_0 interrupt configuration register
            Registers.CPU_INTR_FROM_CPU_0_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CPU_INTR_FROM_CPU_0_MAP") // this register used to map cpu_intr_from_cpu_0 interrupt to one of core0's external interrupt
            ;

            // cpu_intr_from_cpu_1 interrupt configuration register
            Registers.CPU_INTR_FROM_CPU_1_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CPU_INTR_FROM_CPU_1_MAP") // this register used to map cpu_intr_from_cpu_1 interrupt to one of core0's external interrupt
            ;

            // cpu_intr_from_cpu_2 interrupt configuration register
            Registers.CPU_INTR_FROM_CPU_2_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CPU_INTR_FROM_CPU_2_MAP") // this register used to map cpu_intr_from_cpu_2 interrupt to one of core0's external interrupt
            ;

            // cpu_intr_from_cpu_3 interrupt configuration register
            Registers.CPU_INTR_FROM_CPU_3_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CPU_INTR_FROM_CPU_3_MAP") // this register used to map cpu_intr_from_cpu_3 interrupt to one of core0's external interrupt
            ;

            // assist_debug interrupt configuration register
            Registers.ASSIST_DEBUG_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "ASSIST_DEBUG_INTR_MAP") // this register used to map assist_debug interrupt to one of core0's external interrupt
            ;

            // dma_pms_monitor_violatile interrupt configuration register
            Registers.DMA_APBPERI_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_APBPERI_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map dma_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core0_IRam0_pms_monitor_violatile interrupt configuration register
            Registers.CORE_0_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_0_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core0_IRam0_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core0_DRam0_pms_monitor_violatile interrupt configuration register
            Registers.CORE_0_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_0_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core0_DRam0_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core0_PIF_pms_monitor_violatile interrupt configuration register
            Registers.CORE_0_PIF_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_0_PIF_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core0_PIF_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core0_PIF_pms_monitor_violatile_size interrupt configuration register
            Registers.CORE_0_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_0_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP") // this register used to map core0_PIF_pms_monitor_violatile_size interrupt to one of core0's external interrupt
            ;

            // core1_IRam0_pms_monitor_violatile interrupt configuration register
            Registers.CORE_1_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_1_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core1_IRam0_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core1_DRam0_pms_monitor_violatile interrupt configuration register
            Registers.CORE_1_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_1_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core1_DRam0_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core1_PIF_pms_monitor_violatile interrupt configuration register
            Registers.CORE_1_PIF_PMS_MONITOR_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_1_PIF_PMS_MONITOR_VIOLATE_INTR_MAP") // this register used to map core1_PIF_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // core1_PIF_pms_monitor_violatile_size interrupt configuration register
            Registers.CORE_1_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CORE_1_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP") // this register used to map core1_PIF_pms_monitor_violatile_size interrupt to one of core0's external interrupt
            ;

            // backup_pms_monitor_violatile interrupt configuration register
            Registers.BACKUP_PMS_VIOLATE_INTR_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "BACKUP_PMS_VIOLATE_INTR_MAP") // this register used to map backup_pms_monitor_violatile interrupt to one of core0's external interrupt
            ;

            // cache_core0_acs interrupt configuration register
            Registers.CACHE_CORE0_ACS_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CACHE_CORE0_ACS_INT_MAP") // this register used to map cache_core0_acs interrupt to one of core0's external interrupt
            ;

            // cache_core1_acs interrupt configuration register
            Registers.CACHE_CORE1_ACS_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "CACHE_CORE1_ACS_INT_MAP") // this register used to map cache_core1_acs interrupt to one of core0's external interrupt
            ;

            // usb_device interrupt configuration register
            Registers.USB_DEVICE_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "USB_DEVICE_INT_MAP") // this register used to map usb_device interrupt to one of core0's external interrupt
            ;

            // peri_backup interrupt configuration register
            Registers.PERI_BACKUP_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "PERI_BACKUP_INT_MAP") // this register used to map peri_backup interrupt to one of core0's external interrupt
            ;

            // dma_extmem_reject interrupt configuration register
            Registers.DMA_EXTMEM_REJECT_INT_MAP.Define(this, 0x10)
                .WithValueField(0, 5, FieldMode.Read | FieldMode.Write, name: "DMA_EXTMEM_REJECT_INT_MAP") // this register used to map dma_extmem_reject interrupt to one of core0's external interrupt
            ;

            // interrupt status register
            Registers.PRO_INTR_STATUS_0.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read, name: "INTR_STATUS_0") // this register store the status of the first 32 interrupt source
            ;

            // interrupt status register
            Registers.PRO_INTR_STATUS_1.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read, name: "INTR_STATUS_1") // this register store the status of the first 32 interrupt source
            ;

            // interrupt status register
            Registers.PRO_INTR_STATUS_2.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read, name: "INTR_STATUS_2") // this register store the status of the first 32 interrupt source
            ;

            // interrupt status register
            Registers.PRO_INTR_STATUS_3.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read, name: "INTR_STATUS_3") // this register store the status of the first 32 interrupt source
            ;

            // clock gate register
            Registers.CLOCK_GATE.Define(this, 0x1)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "REG_CLK_EN") // this register uesd to control clock-gating interupt martrix
            ;

            // version register
            Registers.DATE.Define(this, 0x2012300)
                .WithValueField(0, 28, FieldMode.Read | FieldMode.Write, name: "INTERRUPT_REG_DATE") // version register
            ;

        }

        public void Reset()
        {
            RegistersCollection.Reset();
            Array.Clear(routingTable, 0, routingTable.Length);
            UpdateInterrupts();
        }

        private void HandleIRQConnect(int sourceNumber, IGPIOReceiver destination, int destinationNumber)
        {
            lock (routingTable)
            {
                destinations[sourceNumber] = new Destination(destination, destinationNumber);
            }
        }

        private void HandleIRQDisconnect(int sourceNumber)
        {
            lock (routingTable)
            {
                destinations[sourceNumber] = new Destination();
            }
        }

        public void OnGPIO(int number, bool value)
        {
            lock (routingTable)
            {
                if (routingTable[number])
                {
                    destinations[number].OnGPIO(value);
                }
            }
        }


        public uint ReadDoubleWord(long offset)
        {
            return RegistersCollection.Read(offset);
        }

        public void WriteDoubleWord(long offset, uint value)
        {
            RegistersCollection.Write(offset, value);
        }
        private void UpdateInterrupts()
        {
            var status = false;
            this.Log(LogLevel.Noisy, "Setting IRQ to {0}", status);
            IRQ.Set(status);
        }

        private enum Registers
        {
            PRO_MAC_INTR_MAP = 0x0,
            MAC_NMI_MAP = 0x4,
            PWR_INTR_MAP = 0x8,
            BB_INT_MAP = 0xc,
            BT_MAC_INT_MAP = 0x10,
            BT_BB_INT_MAP = 0x14,
            BT_BB_NMI_MAP = 0x18,
            RWBT_IRQ_MAP = 0x1c,
            RWBLE_IRQ_MAP = 0x20,
            RWBT_NMI_MAP = 0x24,
            RWBLE_NMI_MAP = 0x28,
            I2C_MST_INT_MAP = 0x2c,
            SLC0_INTR_MAP = 0x30,
            SLC1_INTR_MAP = 0x34,
            UHCI0_INTR_MAP = 0x38,
            UHCI1_INTR_MAP = 0x3c,
            GPIO_INTERRUPT_PRO_MAP = 0x40,
            GPIO_INTERRUPT_PRO_NMI_MAP = 0x44,
            GPIO_INTERRUPT_APP_MAP = 0x48,
            GPIO_INTERRUPT_APP_NMI_MAP = 0x4c,
            SPI_INTR_1_MAP = 0x50,
            SPI_INTR_2_MAP = 0x54,
            SPI_INTR_3_MAP = 0x58,
            SPI_INTR_4_MAP = 0x5c,
            LCD_CAM_INT_MAP = 0x60,
            I2S0_INT_MAP = 0x64,
            I2S1_INT_MAP = 0x68,
            UART_INTR_MAP = 0x6c,
            UART1_INTR_MAP = 0x70,
            UART2_INTR_MAP = 0x74,
            SDIO_HOST_INTERRUPT_MAP = 0x78,
            PWM0_INTR_MAP = 0x7c,
            PWM1_INTR_MAP = 0x80,
            PWM2_INTR_MAP = 0x84,
            PWM3_INTR_MAP = 0x88,
            LEDC_INT_MAP = 0x8c,
            EFUSE_INT_MAP = 0x90,
            CAN_INT_MAP = 0x94,
            USB_INTR_MAP = 0x98,
            RTC_CORE_INTR_MAP = 0x9c,
            RMT_INTR_MAP = 0xa0,
            PCNT_INTR_MAP = 0xa4,
            I2C_EXT0_INTR_MAP = 0xa8,
            I2C_EXT1_INTR_MAP = 0xac,
            SPI2_DMA_INT_MAP = 0xb0,
            SPI3_DMA_INT_MAP = 0xb4,
            SPI4_DMA_INT_MAP = 0xb8,
            WDG_INT_MAP = 0xbc,
            TIMER_INT1_MAP = 0xc0,
            TIMER_INT2_MAP = 0xc4,
            TG_T0_INT_MAP = 0xc8,
            TG_T1_INT_MAP = 0xcc,
            TG_WDT_INT_MAP = 0xd0,
            TG1_T0_INT_MAP = 0xd4,
            TG1_T1_INT_MAP = 0xd8,
            TG1_WDT_INT_MAP = 0xdc,
            CACHE_IA_INT_MAP = 0xe0,
            SYSTIMER_TARGET0_INT_MAP = 0xe4,
            SYSTIMER_TARGET1_INT_MAP = 0xe8,
            SYSTIMER_TARGET2_INT_MAP = 0xec,
            SPI_MEM_REJECT_INTR_MAP = 0xf0,
            DCACHE_PRELOAD_INT_MAP = 0xf4,
            ICACHE_PRELOAD_INT_MAP = 0xf8,
            DCACHE_SYNC_INT_MAP = 0xfc,
            ICACHE_SYNC_INT_MAP = 0x100,
            APB_ADC_INT_MAP = 0x104,
            DMA_IN_CH0_INT_MAP = 0x108,
            DMA_IN_CH1_INT_MAP = 0x10c,
            DMA_IN_CH2_INT_MAP = 0x110,
            DMA_IN_CH3_INT_MAP = 0x114,
            DMA_IN_CH4_INT_MAP = 0x118,
            DMA_OUT_CH0_INT_MAP = 0x11c,
            DMA_OUT_CH1_INT_MAP = 0x120,
            DMA_OUT_CH2_INT_MAP = 0x124,
            DMA_OUT_CH3_INT_MAP = 0x128,
            DMA_OUT_CH4_INT_MAP = 0x12c,
            RSA_INT_MAP = 0x130,
            AES_INT_MAP = 0x134,
            SHA_INT_MAP = 0x138,
            CPU_INTR_FROM_CPU_0_MAP = 0x13c,
            CPU_INTR_FROM_CPU_1_MAP = 0x140,
            CPU_INTR_FROM_CPU_2_MAP = 0x144,
            CPU_INTR_FROM_CPU_3_MAP = 0x148,
            ASSIST_DEBUG_INTR_MAP = 0x14c,
            DMA_APBPERI_PMS_MONITOR_VIOLATE_INTR_MAP = 0x150,
            CORE_0_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP = 0x154,
            CORE_0_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP = 0x158,
            CORE_0_PIF_PMS_MONITOR_VIOLATE_INTR_MAP = 0x15c,
            CORE_0_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP = 0x160,
            CORE_1_IRAM0_PMS_MONITOR_VIOLATE_INTR_MAP = 0x164,
            CORE_1_DRAM0_PMS_MONITOR_VIOLATE_INTR_MAP = 0x168,
            CORE_1_PIF_PMS_MONITOR_VIOLATE_INTR_MAP = 0x16c,
            CORE_1_PIF_PMS_MONITOR_VIOLATE_SIZE_INTR_MAP = 0x170,
            BACKUP_PMS_VIOLATE_INTR_MAP = 0x174,
            CACHE_CORE0_ACS_INT_MAP = 0x178,
            CACHE_CORE1_ACS_INT_MAP = 0x17c,
            USB_DEVICE_INT_MAP = 0x180,
            PERI_BACKUP_INT_MAP = 0x184,
            DMA_EXTMEM_REJECT_INT_MAP = 0x188,
            PRO_INTR_STATUS_0 = 0x18c,
            PRO_INTR_STATUS_1 = 0x190,
            PRO_INTR_STATUS_2 = 0x194,
            PRO_INTR_STATUS_3 = 0x198,
            CLOCK_GATE = 0x19c,
            DATE = 0x7fc,
        }
        private struct Destination
        {
            public Destination(IGPIOReceiver receiver, int destinationNo)
            {
                this.receiver = receiver;
                this.destinationNo = destinationNo;
            }

            public void OnGPIO(bool state)
            {
                receiver.OnGPIO(destinationNo, state);
            }

            public readonly IGPIOReceiver receiver;
            public readonly int destinationNo;
        }
    }
}
