use std::collections::VecDeque;
use std::net::UdpSocket;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::thread::JoinHandle;

use cpal::Stream;
use cpal::traits::DeviceTrait;
use cpal::traits::HostTrait;
use eframe::NativeOptions;
use egui::Color32;
use egui_plot::Line;
use egui_plot::PlotPoints;
use num::Complex;
use num::Zero;
use oxydsp_dsp::blocks::filtering::fir::FirFilter;
use oxydsp_dsp::blocks::iq::zero_if::ZeroIf;
use oxydsp_dsp::blocks::ted::early_late::EarlyLateGate;
use oxydsp_dsp::blocks::utilities::adapters::Map;
use oxydsp_dsp::blocks::utilities::adapters::NullSink;
use oxydsp_dsp::blocks::utilities::adapters::Scan;
use oxydsp_dsp::blocks::utilities::adapters::ScanTagged;
use oxydsp_dsp::blocks::utilities::channels::RxSource;
use oxydsp_dsp::blocks::utilities::squelch::Squelch;
use oxydsp_dsp::filtering::fir::Fir;
use oxydsp_dsp::units::DigitalFrequency;
use oxydsp_flowgraph::flowgraph;
use oxydsp_flowgraph::graph::FlowGraph;
use oxydsp_flowgraph::tag::Tag;
use oxydsp_flowgraph::tag::Tagged;
use oxydsp_flowgraph::tag::Tags;

use crate::CARRIER;
use crate::DEVIATION;
use crate::SAMPLE_PER_SYMBOL;
use crate::SAMPLE_RATE;
use crate::gaussian;
use crate::transmitter::root_raised_cosine;

pub enum PacketBuilderBitState
{
    WaitingForPreamble,
    InPacket,
}

pub enum PacketBuilderByteState
{
    Length1,
    Length2,
    Data,
}

pub struct PacketBuilder
{
    current_byte: u8,
    bit_index: u8,

    bit_state: PacketBuilderBitState,
    packet_state: PacketBuilderByteState,

    // Packet building
    length: u16,
    data: Vec<u8>,
}

impl PacketBuilder
{
    pub fn new() -> Self
    {
        Self {
            current_byte: 0,
            bit_index: 0,
            bit_state: PacketBuilderBitState::WaitingForPreamble,
            packet_state: PacketBuilderByteState::Length1,
            length: 0,
            data: vec![],
        }
    }

    fn next_byte(&mut self) -> Option<Vec<u8>>
    {
        match self.packet_state
        {
            PacketBuilderByteState::Length1 =>
            {
                self.length = 0;
                self.length |= self.current_byte as u16;
                println!("starting packet, length 1 {}", self.current_byte);
                self.packet_state = PacketBuilderByteState::Length2;
            }
            PacketBuilderByteState::Length2 =>
            {
                println!("starting packet, length 2 {}", self.current_byte);
                self.length |= (self.current_byte as u16) << 8;
                self.data = vec![];
                self.packet_state = PacketBuilderByteState::Data;
                println!("length : {}", self.length);
            }
            PacketBuilderByteState::Data =>
            {
                self.data.push(self.current_byte);
                self.length -= 1;
                if self.length == 0
                {
                    println!("finished");
                    let current = std::mem::replace(self, Self::new());
                    return Some(current.data);
                }
            }
        }
        None
    }

    pub fn next_bit(&mut self, bit: bool) -> Option<Vec<u8>>
    {
        self.current_byte >>= 1;
        self.current_byte |= (bit as u8) << 7;
        match self.bit_state
        {
            PacketBuilderBitState::WaitingForPreamble =>
            {
                if self.current_byte == 0b01100111
                {
                    println!("preamble heard !");
                    self.bit_state = PacketBuilderBitState::InPacket;
                    self.bit_index = 0;
                }
                return None;
            }
            PacketBuilderBitState::InPacket =>
            {
                self.bit_index += 1;
                if self.bit_index == 8
                {
                    let out = self.next_byte();
                    self.bit_index = 0;
                    return out;
                }
                None
            }
        }
    }
}

pub struct RadioReceiver {
    //stream: Stream,
    //pub packet_receiver: Receiver<Vec<u8>>,
}

impl RadioReceiver
{
    pub fn start_new() -> Self
    {
        let carrier = DigitalFrequency::from_time_frequency(CARRIER, SAMPLE_RATE as f64);
        let mut tags = Tags::default();

        let (audio_tx, audio_rx) = mpsc::channel();
        let (packet_tx, packet_rx) = mpsc::channel::<Vec<u8>>();
        let (source, signal) = RxSource::new(audio_rx);
        let (inspect, signal) = Map::new(signal, |x| {
            //println!("{x}");
            x
        });
        let (mut zero_if, iq) = ZeroIf::new(signal, carrier.into());

        zero_if.set_fir(Fir::lowpass(
            DigitalFrequency::from_time_frequency(2. * DEVIATION + 100., SAMPLE_RATE as f64),
            SAMPLE_PER_SYMBOL * 4,
        ));

        let (squelch, iq) = Squelch::new(iq, 5., 100);

        let (arg_extract, arg) = Scan::new(iq, Complex::zero(), |state, sample| {
            let angle: Complex<f32> = sample / *state;
            *state = sample;
            angle.arg()
                / DigitalFrequency::from_time_frequency(DEVIATION, SAMPLE_RATE as f64).as_rad()
                    as f32
        });

        // gaussian fir
        let fir = Fir((0..SAMPLE_PER_SYMBOL)
            .map(|x| gaussian(0.3, x as f32 / SAMPLE_PER_SYMBOL as f32))
            .collect())
        .normalized();

        // RRC fir
        let rrc_symbol_count = 4;
        let rrc_fir_length = SAMPLE_PER_SYMBOL * rrc_symbol_count;
        let fir = Fir((0..rrc_fir_length)
            .map(|x| {
                let centered = oxydsp_dsp::map(
                    x as f32,
                    0.,
                    rrc_fir_length as f32,
                    -(rrc_symbol_count as f32) * 0.5,
                    rrc_symbol_count as f32 * 0.5,
                );
                root_raised_cosine(centered, 1., 1.)
            })
            .collect())
        .normalized();

        let (matched_filter, arg) = FirFilter::new(arg, fir);
        let mut elg_loop = Fir(vec![1. / 30.; 30]);
        *elg_loop.0.last_mut().unwrap() = 0.3;
        let symbol_tag = tags.allocate_tag("early late gate symbol");
        let (elg, arg) = EarlyLateGate::new(arg, elg_loop, SAMPLE_PER_SYMBOL, symbol_tag.clone());

        // // Eye diagram
        let (tx, rx) = mpsc::channel::<(Vec<f32>, f32)>();
        //let (eye_sender, arg) = ScanTagged::new(arg, VecDeque::<()>::new(), move |history, x| {
        let symbol_tag2 = symbol_tag.clone();
        let (eye_sender, arg) = ScanTagged::new(arg, VecDeque::new(), move |history, x| {
            let cloned_tag = x.1.clone();
            if history.len() == 2 * SAMPLE_PER_SYMBOL
            {
                history.pop_back();
            }

            let mut error: f32 = 0.;
            let is_symbol_center = x.1.as_ref().is_some_and(|t| {
                if let Some(err) = t.retrieve(&symbol_tag2.clone())
                {
                    error = *err;
                    true
                }
                else
                {
                    false
                }
            });
            history.push_front(((is_symbol_center, error), x.0));

            if history.len() > SAMPLE_PER_SYMBOL && history[SAMPLE_PER_SYMBOL].0.0
            {
                let _ = tx.send((
                    history.iter().map(|(_, x)| *x).collect::<Vec<_>>(),
                    history[SAMPLE_PER_SYMBOL].0.1,
                ));
            }

            Tagged::new(x.0, None)
        });

        let (packet_map, arg) =
            ScanTagged::new(arg, PacketBuilder::new(), move |builder, sample| {
                if sample
                    .1
                    .as_ref()
                    .is_some_and(|t| t.retrieve(&symbol_tag.clone()).is_some())
                    && let Some(packet) = builder.next_bit(sample.0 < 0.)
                {
                    let _ = packet_tx.send(packet);
                }

                Tagged::new(sample.0, None)
            });
        let null_sink = NullSink::new(arg);

        let graph = flowgraph![
            source,
            inspect,
            squelch,
            zero_if,
            packet_map,
            arg_extract,
            //sig_lowpass,
            matched_filter,
            elg,
            eye_sender,
            null_sink
        ];
        let t = graph.run();

        // Setup input
        // let host = cpal::default_host();
        // let device = host.default_input_device().expect("No input device");
        // let mut supported_configs_range = device
        //     .supported_input_configs()
        //     .expect("error while querying configs");
        // let supported_config = supported_configs_range
        //     .next()
        //     .expect("no supported config?!")
        //     .with_sample_rate(SAMPLE_RATE as u32);
        // let stream = device
        //     .build_input_stream(
        //         &supported_config.into(),
        //         move |data: &[f32], _: &cpal::InputCallbackInfo| {
        //             for x in data.iter()
        //             {
        //                 let _ = audio_tx.send(*x);
        //             }
        //         },
        //         move |err| {
        //             panic!() // react to errors here.
        //         },
        //         None, // None=blocking, Some(Duration)=timeout
        //     )
        //     .unwrap();
        std::thread::spawn(move || {
            let socket = UdpSocket::bind("0.0.0.0:25565").unwrap();

            let mut buffer = [0u8; 4096];
            while let Ok(read) = socket.recv(&mut buffer)
            {
                let read_buffer = &mut buffer[0..read];
                for x in read_buffer.chunks(4)
                {
                    let val = f32::from_le_bytes([x[0], x[1], x[2], x[3]]);
                    let _ = audio_tx.send(val);
                }
            }
        });

        let mut eyes = VecDeque::new();
        eframe::run_simple_native("Plot", NativeOptions::default(), move |ctx, _frame| {
            while let Ok(x) = packet_rx.try_recv()
            {
                println!("Got data: {} bytes.", x.len());
                let str: String = x.iter().map(|x| *x as char).collect();
                println!("-----\n\n{}\n\n-----", str);
            }

            while let Ok(eye) = rx.try_recv()
            {
                if eyes.len() >= 100
                {
                    let _ = eyes.pop_back();
                }
                eyes.push_front(eye);
            }

            egui::CentralPanel::default().show(ctx, |ui| {
                egui_plot::Plot::new("hello").show(ui, |plot_ui| {
                    for eye in eyes.iter()
                    {
                        plot_ui.line(
                            Line::new(
                                "eyes",
                                eye.0
                                    .iter()
                                    .enumerate()
                                    .map(|(i, s)| [i as f64 / 2., *s as f64])
                                    .collect::<PlotPoints>(),
                            )
                            .id("eyes")
                            .color(Color32::GREEN),
                        );
                    }
                });
                ctx.request_repaint();
            });
        })
        .unwrap();

        Self {
            //stream,
            //packet_receiver: packet_rx,
        }
    }
}

pub fn color_from_err(error: f32, max: f32) -> Color32
{
    Color32::RED
        .linear_multiply(error.abs() / max)
        .blend(Color32::GREEN.linear_multiply((1. - error.abs() / max).max(0.)))
}
