macro_rules! lut {
    ($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => {
        const $name: [$ty; $len] = {
            let mut table = [0; $len];
            let mut $n = 0;
            while $n < $len {
                table[$n] = $expr;
                $n += 1;
            }
            table
        };
    };
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct LengthTimerHigh(u8);
    impl Debug;
    length, set_length: 7, 3;
    u16;
    timer_high, set_timer_high: 2, 0;
}

#[derive(Debug, Clone)]
struct LengthCounter {
    current: u16,
    silenced: bool,
}

impl LengthCounter {
    pub fn new() -> Self {
        Self {
            current: 0,
            silenced: true,
        }
    }
    pub fn clock(&mut self) {
        if self.current == 0 {
            self.silenced = true;
        } else {
            self.current -= 1;
        }
    }

    pub fn write(&mut self, length: u8) {
        // self.reg.0 = val;
        self.silenced = false;
        const LENGTH_LUT: [u16; 0x20] = [
            10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20,
            96, 22, 192, 24, 72, 26, 16, 28, 32, 3,
        ];
        self.current = LENGTH_LUT[length as usize] + 1; // I think?
    }

    pub fn silenced(&self) -> bool {
        self.silenced
    }
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct DutyVol(u8);
    impl Debug;
    duty, set_duty: 7, 6;
    length_counter_halt, set_length_counter_halt: 5;
    const_vol, set_const_vol: 4;
    volume, set_volume: 3, 0;
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct Sweep(u8);
    impl Debug;
    enable, set_enable: 7;
    period, set_period: 6, 4;
    negate, set_negate: 3;
    shift, set_shift: 2, 0;
}

#[derive(Debug, Clone)]
struct PulseChannel {
    enabled: bool,
    duty_vol: DutyVol,

    sweep: Sweep,
    sweep_reload: bool,
    sweep_counter: u8,

    counter: LengthCounter,

    period: u16,
    period_timer: u16,
    cur: u8,
    sample: u8,

    envelope_start: bool,
    envelope_counter: u8,
    envelope_divider: u8,
    extra: u16,
}

impl PulseChannel {
    pub fn new(extra: u16) -> Self {
        Self {
            enabled: false,
            duty_vol: DutyVol(0),
            sweep: Sweep(0),
            sweep_reload: false,
            sweep_counter: 0,
            counter: LengthCounter::new(),
            period: 0,
            period_timer: 0,
            cur: 0,
            sample: 0,
            envelope_start: false,
            envelope_counter: 0,
            envelope_divider: 0,
            extra,
        }
    }

    pub fn write(&mut self, offset: u16, val: u8) {
        match offset {
            0x00 => {
                self.duty_vol.0 = val;
                self.envelope_start = true;
            }
            0x01 => {
                self.sweep.0 = val;
                self.sweep_reload = true;
            }
            0x02 => self.period = self.period & 0x700 | (val as u16),
            0x03 => {
                let reg = LengthTimerHigh(val);
                self.counter.write(reg.length());
                self.period = (self.period & 0xFF) | (reg.timer_high() << 8);
                self.period_timer = self.period;
                self.cur = 0;
            }
            _ => unreachable!(),
        }
    }

    fn volume(&self) -> u8 {
        if self.duty_vol.const_vol() {
            self.duty_vol.volume()
        } else {
            self.envelope_counter
        }
    }

    pub fn clock(&mut self) {
        if !self.enabled {
            self.sample = 0;
            return;
        }
        if self.period_timer == 0 {
            self.period_timer = self.period;
            self.cur = (self.cur + 1) % 8;
            const DUTY: [[bool; 8]; 4] = [
                [false, true, false, false, false, false, false, false],
                [false, true, true, false, false, false, false, false],
                [false, true, true, true, true, false, false, false],
                [true, false, false, true, true, true, true, true],
            ];
            self.sample = if DUTY[self.duty_vol.duty() as usize][self.cur as usize] {
                self.volume()
            } else {
                0x00
            };
        } else {
            self.period_timer -= 1;
        }
        // TODO
    }
    pub fn cur_sample(&self) -> u8 {
        if self.enabled && !self.counter.silenced() && !self.sweep_silenced() {
            self.sample
        } else {
            0
        }
    }
    pub fn q_frame_clock(&mut self) {
        if !self.enabled {
            return;
        }
        if !self.duty_vol.length_counter_halt() {
            self.counter.clock();
        } else {
            self.counter.silenced = false;
        }
        if !self.duty_vol.const_vol() {
            if self.envelope_start || self.envelope_counter == 0 {
                self.envelope_counter = 0xF;
                self.envelope_divider = self.duty_vol.volume();
                self.envelope_start = false;
            } else if self.envelope_divider == 0 {
                self.envelope_divider = self.duty_vol.volume();
                self.envelope_counter -= 1;
            } else {
                self.envelope_divider -= 1;
            }
        }
    }
    fn target_period(&self) -> u16 {
        let amt = self.period >> self.sweep.shift();
        if self.sweep.negate() {
            self.period.saturating_sub(amt + self.extra)
        } else {
            self.period + amt
        }
    }
    fn sweep_silenced(&self) -> bool {
        self.period < 8 || self.target_period() > 0x7FF
    }
    pub fn h_frame_clock(&mut self) {
        self.q_frame_clock();
        if self.sweep.enable() {
            if self.sweep_reload {
                self.sweep_counter = self.sweep.period();
                self.sweep_reload = false;
            } else if self.sweep_counter == 0 {
                self.sweep_counter = self.sweep.period();
                if self.period < 8 || self.target_period() < 0x7FF {
                    self.period = self.target_period();
                }
            } else {
                self.sweep_counter -= 1;
            }
        }
    }

    pub fn view(&self) -> String {
        format!(
            "Square Channel
Evelope Volume:                 {0:>4}     ${0:02X}
Constant Volume:                {1}
Length Counter - Halted:        {2}
Duty:                           {3:>4}     ${3:02X}
Sweep - Shift:                  {4:>4}     ${4:02X}
Sweep - Negate:                 {5}
Sweep - Period:                 {6:>4}     ${6:02X}
Sweep - Enabled:                {7}
Period:                         {8:>4}     ${8:04X}
Length Counter - Reload Value:  {9:>4}     ${9:04X}
Enabled:                        {10}
Timer:                          {11:>4}     ${11:04X}
Duty Position:                  {12:>4}     ${12:02X}
Length Counter - Counter:       {13:>4}     ${13:02X}
Envelope - Counter:             {14:>4}     ${14:02X}
Envelope - Divider:             {15:>4}     ${15:02X}
Output:                         {16:>4}     ${16:02X}
            ",
            self.duty_vol.volume(),
            self.duty_vol.const_vol(),
            self.duty_vol.length_counter_halt(),
            self.duty_vol.duty(),
            self.sweep.shift(),
            self.sweep.negate(),
            self.sweep.period(),
            self.sweep.enable(),
            self.period,
            self.counter.current,
            self.enabled,
            self.period_timer,
            self.cur, // ?
            self.counter.current,
            self.envelope_counter,
            self.envelope_divider,
            self.cur_sample(),
        )
    }
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
    pub struct LengthCounterReg(u8);
    impl Debug;
    halt, set_halt: 7;
    value, set_value: 6, 0;
}

#[derive(Debug, Clone)]
struct TriangleChannel {
    enabled: bool,
    length: LengthCounterReg,
    reload: bool,
    counter: LengthCounter,

    length_counter: u16,
    cur: u8,
    period: u16,
    period_timer: u16,
    sample: u8,
}

impl TriangleChannel {
    pub fn new() -> Self {
        Self {
            length: LengthCounterReg(0),
            counter: LengthCounter::new(),
            reload: false,
            enabled: false,
            sample: 0,
            period: 0,
            period_timer: 0,
            cur: 0,
            length_counter: 0,
        }
    }

    pub fn write(&mut self, offset: u16, val: u8) {
        match offset {
            0x00 => {
                self.length.0 = val;
            }
            0x01 => (),
            0x02 => self.period = self.period & 0x700 | (val as u16),
            0x03 => {
                // self.length_load.0 = val;
                // self.reload = true;
                let reg = LengthTimerHigh(val);
                self.counter.write(reg.length());
                self.period = (self.period & 0xFF) | (reg.timer_high() << 8);
                self.period_timer = self.period;
                self.cur = 0;
            }
            _ => unreachable!(),
        }
    }

    pub fn cur_sample(&self) -> u8 {
        self.sample
    }

    pub fn clock(&mut self) {
        if self.length_counter > 0 && self.period > 0 {
            if self.period_timer == 0 {
                self.period_timer = self.period;
                const SAMPLES: [u8; 32] = [
                    15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7,
                    8, 9, 10, 11, 12, 13, 14, 15,
                ];
                self.cur = (self.cur + 1) % SAMPLES.len() as u8;
                self.sample = SAMPLES[self.cur as usize];
            } else {
                self.period_timer -= 1;
            }
        }
    }

    pub fn q_frame_clock(&mut self) {
        if self.reload {
            self.length_counter = self.length.value() as u16;
            self.reload = self.length.halt();
        } else if self.length_counter == 0 {
        } else {
            self.length_counter -= 1;
        }
    }

    pub fn h_frame_clock(&mut self) {
        self.q_frame_clock();
    }

    pub fn view(&self) -> String {
        format!(
            "Triangle Channel
Linear Counter - Reload:        {0:>3}     ${0:02X}
Linear Counter - Halted:        {1}
Period:                         {2:>3}     ${2:04X}
Length Counter - Reload Value:  {3:>3}     ${3:04X}
Enabled:                        {4}
Timer:                          {5:>3}     ${5:02X}
Frequency:                      ???
Sequence Position:              {6:>3}     ${6:02X}
Length Counter - Counter:       {7:>3}     ${7:02X}
Linear Counter - Counter:       {8:>3}     ${8:02X}
Linear Counter - Reload Flag:   {9}
Output:                         {10:>3}     ${10:02X}
            ",
            self.length.value(),
            self.length.halt(),
            self.period,
            0,
            self.enabled,
            self.period_timer,
            self.cur,
            0,
            self.length_counter,
            self.reload,
            self.cur_sample(),
        )
    }
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
    pub struct NoiseEnvelope(u8);
    impl Debug;
    length_counter_halt, set_length_counter_halt: 5;
    constant_volume, set_constant_volume: 4;
    volume, set_volume: 3, 0;
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
    pub struct ModePeriod(u8);
    impl Debug;
    mode, set_mode: 7;
    period, set_period: 3, 0;
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
    pub struct LengthCounterLoad(u8);
    impl Debug;
    length, set_length: 7, 3;
}

#[derive(Debug, Clone)]
#[allow(dead_code)]
struct NoiseChannel {
    enabled: bool,
    evelope: NoiseEnvelope,
    mode_period: ModePeriod,
    length_counter_load: LengthCounterLoad,

    shr: u16,
    count: u16,
    length_counter: u8,
}

impl NoiseChannel {
    pub fn new() -> Self {
        Self {
            enabled: false,
            evelope: NoiseEnvelope(0),
            mode_period: ModePeriod(0),
            length_counter_load: LengthCounterLoad(0),
            shr: 1,
            count: 0,
            length_counter: 0,
        }
    }

    pub fn write(&mut self, offset: u16, _val: u8) {
        match offset {
            0x00 => (),
            0x01 => (),
            0x02 => (),
            0x03 => (),
            _ => unreachable!(),
        }
    }

    pub fn cur_sample(&self) -> u8 {
        if self.length_counter == 0 || self.shr & 1 == 1 {
            0
        } else {
            self.evelope.volume()
        }
    }
    pub fn clock(&mut self) {
        const PERIODS: &[u16] = &[
            4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4096,
        ];
        if self.count == PERIODS[self.mode_period.period() as usize] / 2 {
            self.count = 0;
            let bit_0 = (self.shr & 0b1) << 14;
            let other = if self.mode_period.mode() {
                (self.shr & 0b100_0000) << 8
            } else {
                (self.shr & 0b10) << 13
            };
            self.shr = (self.shr >> 1) | (bit_0 ^ other);
        } else {
            self.count += 1;
        }
    }
    pub fn q_frame_clock(&mut self) {}
    pub fn h_frame_clock(&mut self) {
        self.q_frame_clock();
    }

    pub fn view(&self) -> String {
        format!("")
    }
}

#[derive(Debug, Clone)]
struct DeltaChannel {
    enabled: bool,
    sample: u8,
}

impl DeltaChannel {
    pub fn new() -> Self {
        Self {
            enabled: false,
            sample: 0,
        }
    }

    pub fn write(&mut self, offset: u16, _val: u8) {
        match offset {
            0x00 => (),
            0x01 => (),
            0x02 => (),
            0x03 => (),
            _ => unreachable!(),
        }
    }

    pub fn cur_sample(&self) -> u8 {
        self.sample
    }
    pub fn clock(&mut self) {}
    pub fn q_frame_clock(&mut self) {}
    pub fn h_frame_clock(&mut self) {}

    pub fn int(&self) -> bool {
        false
    }

    pub fn view(&self) -> String {
        format!("")
    }
}

#[derive(Debug, Clone)]
struct FrameCounter {
    count: usize,
    mode_5_step: bool,
    interrupt_enabled: bool,
    irq: bool,
}

#[derive(Clone)]
pub struct APU {
    pulse_1: PulseChannel,
    pulse_2: PulseChannel,
    triangle: TriangleChannel,
    noise: NoiseChannel,
    dmc: DeltaChannel,
    frame_counter: FrameCounter,

    samples: Vec<u8>,
}

impl std::fmt::Debug for APU {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "APU
{:#?}
{:#?}
{:#?}
{:#?}
{:#?}
            ",
            self.pulse_1,
            self.pulse_2,
            self.triangle,
            self.noise,
            self.dmc,
        )
    }
}

impl APU {
    pub fn init() -> Self {
        Self {
            pulse_1: PulseChannel::new(1),
            pulse_2: PulseChannel::new(0),
            triangle: TriangleChannel::new(),
            noise: NoiseChannel::new(),
            dmc: DeltaChannel::new(),
            frame_counter: FrameCounter {
                mode_5_step: false,
                interrupt_enabled: true,
                count: 0,
                irq: false,
            },

            samples: Vec::with_capacity(10000),
        }
    }

    pub fn reset(&mut self) {
        *self = Self::init();
    }

    pub fn read_reg(&mut self, offset: u16) -> u8 {
        // println!("APU read: {offset:02X}");
        match offset {
            0x15 => {
                let val = (u8::from(self.dmc.int()) << 7)
                    | (u8::from(self.frame_counter.irq) << 6)
                    | (u8::from(self.dmc.enabled) << 4)
                    | (u8::from(self.noise.enabled) << 3)
                    | (u8::from(self.triangle.enabled) << 2)
                    | (u8::from(self.pulse_2.enabled) << 1)
                    | (u8::from(self.pulse_1.enabled) << 0);
                self.frame_counter.irq = false;
                val
            }
            _ => panic!("No register at {:X}", offset),
        }
    }

    pub fn write_reg(&mut self, offset: u16, val: u8) {
        // println!("APU write: {offset:02X} <= {val:02X}");
        match offset {
            0x00..0x04 => self.pulse_1.write(offset - 0x00, val),
            0x04..0x08 => self.pulse_2.write(offset - 0x04, val),
            0x08..0x0C => self.triangle.write(offset - 0x08, val),
            0x0C..0x10 => self.noise.write(offset - 0x0C, val),
            0x10..0x14 => self.dmc.write(offset - 0x10, val),
            0x14 => (),
            0x15 => {
                self.dmc.enabled = val & 0b0001_0000 != 0;
                self.noise.enabled = val & 0b0000_1000 != 0;
                self.triangle.enabled = val & 0b0000_0100 != 0;
                self.pulse_2.enabled = val & 0b0000_0010 != 0;
                self.pulse_1.enabled = val & 0b0000_0001 != 0;
            }
            0x17 => {
                self.frame_counter.mode_5_step = val & 0b1000_0000 != 0;
                self.frame_counter.interrupt_enabled = val & 0b0100_0000 == 0;
                if !self.frame_counter.interrupt_enabled {
                    self.frame_counter.irq = false;
                }
            }
            // _ => (),
            _ => panic!("No register at {:X}", offset),
        }
    }

    fn q_frame_clock(&mut self) {
        self.pulse_1.q_frame_clock();
        self.pulse_2.q_frame_clock();
        self.triangle.q_frame_clock();
        self.noise.q_frame_clock();
        self.dmc.q_frame_clock();
    }

    fn h_frame_clock(&mut self) {
        self.pulse_1.h_frame_clock();
        self.pulse_2.h_frame_clock();
        self.triangle.h_frame_clock();
        self.noise.h_frame_clock();
        self.dmc.h_frame_clock();
    }

    fn gen_sample(&mut self) {
        lut!(P_LUT: [u8; 32] = |n| (95.52 / (8128.0 / n as f64 + 100.0) * 255.0) as u8);
        let pulse_out = P_LUT[(self.pulse_1.cur_sample() + self.pulse_2.cur_sample()) as usize];
        lut!(TND_LUT: [u8; 204] = |n| (163.67 / (24329.0 / n as f64 + 100.0) * 255.0) as u8);
        let tnd_out = TND_LUT[3 * self.triangle.cur_sample() as usize
            + 2 * self.noise.cur_sample() as usize
            + self.dmc.cur_sample() as usize];
        self.samples.push(pulse_out + tnd_out);
    }

    pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool {
        if ppu_cycle % 6 == 1 {
            // APU Frame Counter clock cycle
            self.frame_counter.count += 1;
            if self.frame_counter.count == 3728 {
                self.q_frame_clock();
            } else if self.frame_counter.count == 7456 {
                self.h_frame_clock();
            } else if self.frame_counter.count == 11185 {
                self.q_frame_clock();
            } else if self.frame_counter.count == 14914 && !self.frame_counter.mode_5_step {
                self.frame_counter.irq = self.frame_counter.interrupt_enabled;
                self.h_frame_clock();
            } else if self.frame_counter.count == 14915 && !self.frame_counter.mode_5_step {
                self.frame_counter.count = 0;
                self.frame_counter.irq = self.frame_counter.interrupt_enabled;
            } else if self.frame_counter.count == 18640 {
                self.h_frame_clock();
                self.frame_counter.count = 0;
            }

            self.pulse_1.clock();
            self.pulse_2.clock();
            self.noise.clock();
            self.dmc.clock();
        }
        if ppu_cycle % 3 == 1 {
            self.triangle.clock();
        }
        if ppu_cycle % (6 * 4) == 1 {
            self.gen_sample();
        }
        false
    }

    pub fn get_frame_samples(&self) -> &[u8] {
        // println!("'Frame' of samples: {}", self.samples.len());
        &self.samples
    }

    pub fn reset_frame_samples(&mut self) {
        self.samples.clear();
    }

    pub fn peek_nmi(&self) -> bool {
        false
    }

    pub fn nmi_waiting(&mut self) -> bool {
        false
    }

    pub fn peek_irq(&self) -> bool {
        self.frame_counter.irq
    }

    pub fn irq_waiting(&mut self) -> bool {
        self.frame_counter.irq
    }
}

#[cfg(feature = "iced")]
mod apu_iced {
    use super::*;
    use iced::{
        Element, Font,
        widget::{column, text},
    };

    impl APU {
        pub fn view<'s, T: 's>(&'s self) -> Element<'s, T> {
            column![
                text(self.pulse_1.view()).font(Font::MONOSPACE),
                text(self.pulse_2.view()).font(Font::MONOSPACE),
                text(self.triangle.view()).font(Font::MONOSPACE),
                text(self.noise.view()).font(Font::MONOSPACE),
                text(self.dmc.view()).font(Font::MONOSPACE),
            ]
            .into()
        }
    }
}

#[cfg(test)]
mod tests;
