use std::rc::Rc;
use uuid::Uuid;
use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlInputElement};
use yew::prelude::*;
use yewdux::prelude::*;

use crate::components::transfer_menu::RegionDisplay;
use plate_tool_lib::{transfer::Transfer, transfer_region::Region};

use crate::components::states::{CurrentTransfer, MainState};

pub fn on_name_change_callback(ct_dispatch: Dispatch<CurrentTransfer>) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if input.value() == "" {
                return;
            } // We do not want empty inputs!
            ct_dispatch.reduce_mut(|state| {
                state.transfer.name = input.value();
            });
        }
    })
}

pub fn on_src_region_change_callback(ct_dispatch: Dispatch<CurrentTransfer>) -> Callback<Event> {
    Callback::from(move |e: Event| {
        if matches!(
            ct_dispatch.get().transfer.transfer_region.source_region,
            Region::Custom(_)
        ) {
            return; // Do nothing here!
        }
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(rd) = RegionDisplay::try_from(input.value().to_uppercase()) {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.source_region = Region::from(&rd);
                });
                input.set_custom_validity("");
            } else {
                input.set_custom_validity("Invalid region.")
            }
        }
    })
}

pub fn on_dest_region_change_callback(ct_dispatch: Dispatch<CurrentTransfer>) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(rd) = RegionDisplay::try_from(input.value().to_uppercase()) {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.dest_region = Region::from(&rd);
                });
                input.set_custom_validity("");
            } else {
                input.set_custom_validity("Invalid region.")
            }
        }
    })
}

pub fn on_source_interleave_x_change_callback(
    ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(num) = input.value().parse::<i8>() {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.interleave_source =
                        (num, state.transfer.transfer_region.interleave_source.1);
                });
            }
        }
    })
}

pub fn on_source_interleave_y_change_callback(
    ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(num) = input.value().parse::<i8>() {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.interleave_source =
                        (state.transfer.transfer_region.interleave_source.0, num);
                });
            }
        }
    })
}

pub fn on_dest_interleave_x_change_callback(
    ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(num) = input.value().parse::<i8>() {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.interleave_dest =
                        (num, state.transfer.transfer_region.interleave_dest.1);
                });
            }
        }
    })
}

pub fn on_dest_interleave_y_change_callback(
    ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let target: Option<EventTarget> = e.target();
        let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
        if let Some(input) = input {
            if let Ok(num) = input.value().parse::<i8>() {
                ct_dispatch.reduce_mut(|state| {
                    state.transfer.transfer_region.interleave_dest =
                        (state.transfer.transfer_region.interleave_dest.0, num);
                });
            }
        }
    })
}

pub fn on_volume_change_callback(ct_dispatch: Dispatch<CurrentTransfer>) -> Callback<Event> {
    Callback::from(move |e: Event| {
        let input = e
            .target()
            .expect("Event must have target")
            .dyn_into::<HtmlInputElement>()
            .expect("Must have been emitted by input");
        if let Ok(num) = input.value().parse::<f32>() {
            ct_dispatch.reduce_mut(|state| {
                state.transfer.volume = num;
            });
        }
    })
}

pub fn new_transfer_button_callback_callback(
    main_dispatch: Dispatch<MainState>,
    main_state: Rc<MainState>,
    ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<MouseEvent> {
    Callback::from(move |_: MouseEvent| {
        main_dispatch.reduce_mut(|state| {
            state.selected_transfer = Uuid::nil();
        });
        ct_dispatch.reduce_mut(|state| {
            state.transfer = Transfer::default();
            state.transfer.source_id = main_state.selected_source_plate;
            state.transfer.dest_id = main_state.selected_dest_plate;
        });
    })
}

pub fn save_transfer_button_callback_callback(
    main_dispatch: Dispatch<MainState>,
    main_state: Rc<MainState>,
    ct_state: Rc<CurrentTransfer>,
    new_transfer_button_callback: Callback<MouseEvent>)
    -> Callback<MouseEvent> {
    Callback::from(move |e: MouseEvent| {
        log::debug!("Button pressed");
        if main_state.selected_transfer.is_nil() {
            if let Some(spi) = main_state
                .source_plates
                .iter()
                .find(|spi| spi.get_uuid() == main_state.selected_source_plate)
            {
                if let Some(dpi) = main_state
                    .destination_plates
                    .iter()
                    .find(|dpi| dpi.get_uuid() == main_state.selected_dest_plate)
                {
                    let new_transfer = Transfer::new(
                        spi.clone(),
                        dpi.clone(),
                        ct_state.transfer.transfer_region.clone(),
                        ct_state.transfer.name.clone(),
                    );
                    main_dispatch.reduce_mut(|state| {
                        state.transfers.push(new_transfer);
                        state.selected_transfer = state
                            .transfers
                            .last()
                            .expect("An element should have just been added")
                            .get_uuid();
                    });
                    new_transfer_button_callback.emit(e); // If we just made a new transfer,
                                                          // then we should make another on
                                                          // save.
                }
            }
        } else if let Some(index) = main_state
            .transfers
            .iter()
            .position(|t| t.get_uuid() == main_state.selected_transfer)
        {
            main_dispatch.reduce_mut(|state| {
                state.transfers[index] = ct_state.transfer.clone();
            });
        }
    })
}

pub fn delete_transfer_button_callback(
    main_state: Rc<MainState>,
    main_dispatch: Dispatch<MainState>,
    ct_state: Rc<CurrentTransfer>,
    new_transfer_button_callback: Callback<MouseEvent>
) -> Callback<MouseEvent> {
        Callback::from(move |e: MouseEvent| {
            if main_state.selected_transfer.is_nil() {
                // Maybe reset transfer?
            } else if let Some(index) = main_state
                .transfers
                .iter()
                .position(|t| t.get_uuid() == ct_state.transfer.get_uuid())
            {
                main_dispatch.reduce_mut(|state| {
                    state.transfers.remove(index);
                    state.selected_transfer = Uuid::nil();
                });
                new_transfer_button_callback.emit(e); // We need a new transfer now
            }
        })
}
