use crate::config::{Context, Mode};
use crate::zabbix::api::get_zabbix_problems;
use launchy::Color;
use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad};
use std::thread::sleep;
use std::time::Duration;

const MATRIX_WIDTH: i32 = 8;
const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
const REQUEST_TIMEOUT: i32 = 5;
const SLOWEFFECT_FACTOR: f32 = 800.0;

pub fn run(ctx: &mut Context) {
    match ctx.mode {
        Some(Mode::Draw) => {
            let (mut canvas, mut _poller) = initpad();
            draw(&mut canvas, ctx);
        }
        Some(Mode::Input) => {
            let (mut canvas, mut poller) = initpad();
            input(&mut canvas, &mut poller);
        }
        Some(Mode::Effect) => {
            let (mut canvas, mut poller) = initpad();
            effect(&mut canvas, &mut poller)
        }
        Some(Mode::Test) => test(ctx),
        None => {
            println!("No valid option choosen for mode");
        }
    }
}

fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) {
    let (mut canvas, poller) = launchy::CanvasLayout::new_polling();
    match canvas.add_by_guess::<launchy::mini_mk3::Canvas>(0, 0) {
        Ok(_) => {
            println!("Connected to device using USB");
            clear(&mut canvas);
        }
        Err(err) => {
            eprintln!("Failed to connect to device, check USB connection {err}",);
        }
    };
    canvas[Pad { x: 0, y: 0 }] = Color::BLUE;
    canvas.flush().unwrap();
    sleep(Duration::from_millis(100));
    (canvas, poller)
}

fn input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
    for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
        for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
            canvas[msg.pad()] = color;
            println!("{msg:?}", msg = msg.pad())
        }
        canvas.flush().unwrap();
    }
}

fn test(ctx: &mut Context) {
    println!(
        "Refresh rate is {sec} seconds",
        sec = ctx.cfg.refresh.unwrap()
    );
    loop {
        let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
            Ok(z) => z,
            Err(_) => {
                println!(
                    "Error requesting zabbix service, retrying in {} seconds",
                    REQUEST_TIMEOUT
                );
                sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
                continue;
            }
        };
        println!("{}", zabbix_data);
        ctx.hotreload();
    }
}

fn draw(canvas: &mut CanvasLayout, ctx: &mut Context) {
    println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
    loop {
        let mut max = 0;
        let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
            Ok(zabbix_data) => zabbix_data,
            Err(_) => {
                println!(
                    "Error requesting zabbix service, retrying in {}",
                    REQUEST_TIMEOUT
                );
                sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
                continue;
            }
        };
        ctx.datamatrix.compute(&zabbix_data);
        clear(canvas);

        for (i, j) in ctx.datamatrix.layout.iter().enumerate() {
            let p = Pad {
                x: ((i as i32) % MATRIX_WIDTH),
                y: ((i as i32) / MATRIX_WIDTH) + 1,
            };
            canvas[p] = match j.status {
                5 => Color::RED,
                4 => Color::new(1., 0.2, 0.),
                3 => Color::new(1., 0.4, 0.),
                2 => Color::new(1., 0.6, 0.),
                1 => Color::BLUE,
                _ => Color::GREEN,
            };
            if ctx.cfg.sloweffect.unwrap() {
                sleep(Duration::from_millis(
                    (SLOWEFFECT_FACTOR * (1.0 / ctx.datamatrix.layout.len() as f32)) as u64,
                ));
            }
            canvas.flush().unwrap();
            max += 1;
            if max >= MATRIX_SIZE {
                break;
            }
        }
        ctx.hotreload();
    }
}

fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
    loop {
        for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
            for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
                canvas[msg.pad()] = color;
                println!("{msg:?}", msg = msg.pad())
            }
            canvas.flush().unwrap();

            canvas.iter().for_each(|pad| {
                let surrounding_color = pad
                    .neighbors_5()
                    .iter()
                    .map(|&p| canvas.get(p).unwrap_or(Color::GREEN))
                    .sum::<Color>()
                    / 5.0
                    / 1.05;

                canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
            });
        }
        clear(canvas);
    }
}

fn clear(canvas: &mut CanvasLayout) {
    for a in 0..MATRIX_WIDTH {
        for b in 0..MATRIX_WIDTH + 1 {
            canvas[Pad { x: a, y: b }] = Color::BLACK;
        }
    }
    canvas.flush().unwrap();
}