use std::{cell::Cell, rc::Rc};

use crate::{
    model::{builder_traits::Product, models::SignalReduce, Frequency},
    model_utils::signal_reducer::{find_by_frequency_value, set_default_values},
    view::{
        components::{info_bar::InfoBar, input::Input},
        properties::*,
    },
    view_utils::signal_reduce_utils::*,
};

use glib::clone;

use gio::ListStore;

use gtk::{
    prelude::{BoxExt, ButtonExt, Cast, GridExt, SorterExt},
    Align, *,
};

use gtk4 as gtk;

pub fn signal_reducing_page(wrapper: &Box) {
    let values = Rc::new(Cell::new(SignalReduce::default()));

    let info_bar = InfoBar::get_instance();

    let (input_height, monospace, input_wrapping): (i32, bool, WrapMode) =
        (24, true, WrapMode::Word);

    let input_block: Grid = Grid::new();

    input_block.set_column_homogeneous(true);
    input_block.set_row_homogeneous(true);

    let input_label_alignment = Alignment {
        horizontal: Align::Start,
        vertical: Align::Fill,
    };

    let input_labels: [&str; 6] = ["l, м:", "Rм, Ом", "Cм, пФ:", "Rи, Ом:", "Vи, мВ", "f, мГц:"];

    let all_inputs: Vec<Input> = input_labels
        .iter()
        .map(move |label| {
            Input::builder()
                .label(label)
                .margins(MarginData::EqualsMargin(6))
                .align(input_label_alignment)
                .build(monospace, input_wrapping, input_height)
        })
        .collect();

    for (id, elem) in all_inputs.iter().enumerate() {
        let row = id as i32 / 3;

        input_block.attach(elem.get(), (id as i32) - (3 * row), row, 1, 1);
    }

    let calculate_button = Button::builder().label("Расчитать").build();

    let result_table_headers_labels: [&str; 4] = ["f, МГц", "Xc, Ом", "Vп, мВ", "ζ"];

    let model = ListStore::new::<Frequency>();

    let model_for_events = model.clone();

    set_default_values(&model);

    let numeric_sorter = CustomSorter::new(|a, b| {
        let a = a.downcast_ref::<Frequency>().unwrap().frequency();
        let b = b.downcast_ref::<Frequency>().unwrap().frequency();

        a.total_cmp(&b).into()
    });

    let sorted_model = SortListModel::new(Some(model), Some(numeric_sorter.clone()));

    let selection_model = NoSelection::new(Some(sorted_model));

    let result_table = ColumnView::builder()
        .reorderable(true)
        .show_row_separators(true)
        .model(&selection_model)
        .build();

    result_table.connect_activate(clone!(
        #[strong]
        numeric_sorter,
        move |_, _| numeric_sorter.changed(SorterChange::Different)
    ));

    for label in result_table_headers_labels {
        let factory = SignalListItemFactory::new();
        let values_for_factory = values.clone();

        factory.connect_setup(move |_, list_item| {
            list_item
                .downcast_ref::<ListItem>()
                .expect("Needs to be ListItem")
                .set_child(Some(&Label::new(None)));
        });

        factory.connect_bind(move |_, list_item| {
            let cell_value = list_item
                .downcast_ref::<ListItem>()
                .expect("Needs to be ListItem")
                .item()
                .and_downcast::<Frequency>()
                .expect("The item has to be an `IntegerObject`.");

            let cell_label = list_item
                .downcast_ref::<ListItem>()
                .expect("Needs to be ListItem")
                .child()
                .and_downcast::<Label>()
                .expect("The child has to be a `Label`.");

            let result_values = values_for_factory.get();

            let reactive_resist: f64 = reactive_resistance_of_capacitor(
                result_values.wire_capacity * 10f64.powi(-12),
                result_values.length,
                cell_value.frequency() * 10f64.powi(6),
            );

            let full_resistance: f64 = full_resistance_of_capacitor(
                reactive_resist,
                result_values.source_resistance,
                result_values.wire_resistance,
                result_values.length,
            );

            let signal_source_voltage: f64 = voltage_from_signal_source(
                result_values.source_voltage * 10f64.powi(-3),
                reactive_resist,
                full_resistance,
            ) * 1000.0;

            match label {
                "f, МГц" => {
                    cell_label.set_label(&cell_value.frequency().to_string());
                }
                "Xc, Ом" => {
                    cell_label.set_label(format!("{0:.1$}", reactive_resist, 6).as_str());
                }
                "Vп, мВ" => {
                    cell_label.set_label(format!("{0:.1$}", signal_source_voltage, 6).as_str());
                }
                "ζ" => {
                    let coef: f64 =
                        coef_of_signal_reduce(result_values.source_voltage, signal_source_voltage);

                    cell_label.set_label(format!("{0:.1$}", coef, 6).as_str());
                }
                _ => {}
            }
        });

        let column = ColumnViewColumn::builder()
            .title(label)
            .expand(true)
            .resizable(false)
            .factory(&factory)
            .build();

        result_table.append_column(&column);
    }

    let scrollable_table = ScrolledWindow::builder()
        .vexpand(true)
        .child(&result_table)
        .build();

    let table = Frame::builder()
        .child(&scrollable_table)
        .vexpand(true)
        .build();

    calculate_button.connect_clicked(clone!(
        #[strong]
        model_for_events,
        move |_| match parse_fields(all_inputs.clone()) {
            Ok(results) => {
                values.set(results);

                let new_elem = &Frequency::new(values.get().frequency);

                let exist_elem_pos = model_for_events.find_with_equal_func(|elem| {
                    elem.downcast_ref::<Frequency>().unwrap().frequency() == new_elem.frequency()
                });

                match exist_elem_pos {
                    Some(_) => {
                        info_bar.set_text_label(Some("Данная частота уже была задана."));
                        info_bar.show_infobar(5u64);
                    }
                    None => {
                        model_for_events.append(&Frequency::new(values.get().frequency));
                        let a = model_for_events.n_items();

                        model_for_events.items_changed(0, 0, a - 1);
                        model_for_events.items_changed(a - 1, a - 1, 0);
                    }
                }
            }
            Err(error) => {
                let error_kind: Option<&str> = get_error_message(error);

                info_bar.set_text_label(error_kind);
                info_bar.show_infobar(5u64);
            }
        }
    ));

    wrapper.append(&input_block);
    wrapper.append(&calculate_button);
    wrapper.append(&table);
}