use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use gtk::prelude::*;

use crate::audio::sources::synthesizer;

pub const NOTES_NUMBER: usize = 60;

pub struct Key {
    pub id: gdk::enums::key::Key,
    pub note: usize,
    pub widget: gtk::Box,
    pub pressed: AtomicBool,
}

impl Key {
    pub fn get_pressed(&self) -> bool {
        self.pressed.load(Ordering::SeqCst)
    }

    pub fn set_pressed(&self, pressed: bool) {
        self.pressed.store(pressed, Ordering::SeqCst);
    }
}

pub struct Keyboard {
    pub keys: HashMap<gdk::enums::key::Key, Key>,
    pub widget: gtk::Grid,
    pub enabled: AtomicBool,
}

impl Keyboard {
    pub fn new() -> Self {
        let init_list = [
            vec![
                ("F1",  48, gdk::enums::key::F1),
                ("F2",  49, gdk::enums::key::F2),
                ("F3",  50, gdk::enums::key::F3),
                ("F4",  51, gdk::enums::key::F4),
                ("F5",  52, gdk::enums::key::F5),
                ("F6",  53, gdk::enums::key::F6),
                ("F7",  54, gdk::enums::key::F7),
                ("F8",  55, gdk::enums::key::F8),
                ("F9",  56, gdk::enums::key::F9),
                ("F10", 57, gdk::enums::key::F10),
                ("F11", 58, gdk::enums::key::F11),
                ("F12", 59, gdk::enums::key::F12),
            ], vec![
                ("1",   36, gdk::enums::key::_1),
                ("2",   37, gdk::enums::key::_2),
                ("3",   38, gdk::enums::key::_3),
                ("4",   39, gdk::enums::key::_4),
                ("5",   40, gdk::enums::key::_5),
                ("6",   41, gdk::enums::key::_6),
                ("7",   42, gdk::enums::key::_7),
                ("8",   43, gdk::enums::key::_8),
                ("9",   44, gdk::enums::key::_9),
                ("0",   45, gdk::enums::key::_0),
                ("-",   46, gdk::enums::key::minus),
                ("=",   47, gdk::enums::key::equal),
            ], vec![
                ("Q",   24, gdk::enums::key::q),
                ("W",   25, gdk::enums::key::w),
                ("E",   26, gdk::enums::key::e),
                ("R",   27, gdk::enums::key::r),
                ("T",   28, gdk::enums::key::t),
                ("Y",   29, gdk::enums::key::y),
                ("U",   30, gdk::enums::key::u),
                ("I",   31, gdk::enums::key::i),
                ("O",   32, gdk::enums::key::o),
                ("P",   33, gdk::enums::key::p),
                ("[",   34, gdk::enums::key::bracketleft),
                ("]",   35, gdk::enums::key::bracketright),
            ], vec![
                ("A",   12, gdk::enums::key::a),
                ("S",   13, gdk::enums::key::s),
                ("D",   14, gdk::enums::key::d),
                ("F",   15, gdk::enums::key::f),
                ("G",   16, gdk::enums::key::g),
                ("H",   17, gdk::enums::key::h),
                ("J",   18, gdk::enums::key::j),
                ("K",   19, gdk::enums::key::k),
                ("L",   20, gdk::enums::key::l),
                (";",   21, gdk::enums::key::semicolon),
                ("'",   22, gdk::enums::key::apostrophe),
            ], vec![
                ("Z",    0, gdk::enums::key::z),
                ("X",    1, gdk::enums::key::x),
                ("C",    2, gdk::enums::key::c),
                ("V",    3, gdk::enums::key::v),
                ("B",    4, gdk::enums::key::b),
                ("N",    5, gdk::enums::key::n),
                ("M",    6, gdk::enums::key::m),
                (",",    7, gdk::enums::key::comma),
                (".",    8, gdk::enums::key::period),
                ("/",    9, gdk::enums::key::slash),
            ],
        ];

        let widget = gtk::Grid::new();
        widget.set_row_spacing(1);
        widget.set_column_spacing(1);
        widget.set_row_homogeneous(true);
        widget.set_column_homogeneous(true);

        let row_max_length = init_list.iter().map(|row| row.len()).max().unwrap();
        let key_width = 2;
        let key_height = 1;

        let mut keys = HashMap::new();
        for (i, row) in init_list.iter().enumerate() {
            let offset = (row_max_length - row.len()) as i32;
            for (j, (label, note, id)) in row.iter().enumerate() {
                let key_wgt = gtk::Box::new(gtk::Orientation::Vertical, 0);
                let key_label = gtk::Label::new(Some(*label));
                let note_label = gtk::Label::new(Some(synthesizer::utils::get_note_name(*note)));
                key_label.get_style_context().add_class("key-main-label");
                note_label.get_style_context().add_class("key-note-label");
                note_label.get_style_context().add_class("dim-label");
                key_label.set_property_margin(0);
                note_label.set_property_margin(0);
                key_wgt.pack_start(&key_label, true, true, 0);
                key_wgt.pack_start(&note_label, true, true, 0);
                key_wgt.get_style_context().add_class("key");

                let x = j as i32 * key_width + offset;
                let y = i as i32 * key_height;
                widget.attach(&key_wgt, x, y, key_width, key_height);
                keys.insert(*id, Key { 
                    id: *id,
                    note: *note,
                    widget: key_wgt,
                    pressed: AtomicBool::new(false)
                });
            }
        }

        Self { keys, widget, enabled: AtomicBool::new(true) }
    }

    pub fn get_enabled(&self) -> bool {
        self.enabled.load(Ordering::SeqCst)
    }

    pub fn _set_enabled(&self, enabled: bool) {
        self.enabled.store(enabled, Ordering::SeqCst);
    }

}





