// Copyright 2023 The Grim Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use egui::{Align, Id, Layout, RichText, Rounding, TextStyle, Ui, Widget};
use egui_extras::{Size, StripBuilder};
use grin_core::global::ChainTypes;

use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{ARROW_FAT_LINE_UP, ARROW_FAT_LINES_DOWN, ARROW_FAT_LINES_UP, CLIPBOARD_TEXT, GLOBE_SIMPLE, HANDSHAKE, PLUG, PLUS_CIRCLE, PROHIBIT_INSET, TRASH};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalPosition, View};
use crate::gui::views::network::settings::NetworkSettings;
use crate::node::{NodeConfig, PeersConfig};

/// Type of peer.
#[derive(Eq, PartialEq)]
enum PeerType {
    DefaultSeed,
    CustomSeed,
    Allowed,
    Denied,
    Preferred
}

/// P2P server setup ui section.
pub struct P2PSetup {
    /// P2P port value.
    port_edit: String,
    /// Flag to check if p2p port is available.
    port_available_edit: bool,

    /// Flag to check if p2p port from saved config value is available.
    is_port_available: bool,

    /// Flag to check if entered peer address is correct and/or available.
    is_correct_address_edit: bool,
    /// Peer edit value for modal.
    peer_edit: String,

    /// Default main network seeds.
    default_main_seeds: Vec<String>,
    /// Default test network seeds.
    default_test_seeds: Vec<String>,

    /// How long banned peer should stay banned.
    ban_window_edit: String,

    /// Maximum number of inbound peer connections.
    max_inbound_count: String,

    /// Maximum number of outbound peer connections.
    max_outbound_count: String,

    /// Preferred minimum number of outbound peers.
    min_outbound_count: String
}

impl Default for P2PSetup {
    fn default() -> Self {
        let port = NodeConfig::get_p2p_port();
        let is_port_available = NodeConfig::is_p2p_port_available(&port);
        let default_main_seeds = grin_servers::MAINNET_DNS_SEEDS
            .iter()
            .map(|s| s.to_string())
            .collect();
        let default_test_seeds = grin_servers::TESTNET_DNS_SEEDS
            .into_iter()
            .map(|s| s.to_string())
            .collect();
        Self {
            port_edit: port,
            port_available_edit: is_port_available,
            is_correct_address_edit: true,
            is_port_available,
            peer_edit: "".to_string(),
            default_main_seeds,
            default_test_seeds,
            ban_window_edit: NodeConfig::get_p2p_ban_window(),
            max_inbound_count: NodeConfig::get_max_inbound_peers(),
            max_outbound_count: NodeConfig::get_max_outbound_peers(),
            min_outbound_count: NodeConfig::get_min_outbound_peers(),
        }
    }
}

impl P2PSetup {
    /// Identifier for port value [`Modal`].
    pub const PORT_MODAL: &'static str = "p2p_port";
    /// Identifier for custom seed [`Modal`].
    pub const CUSTOM_SEED_MODAL: &'static str = "p2p_custom_seed";
    /// Identifier for allowed peer [`Modal`].
    pub const ALLOW_PEER_MODAL: &'static str = "p2p_allow_peer";
    /// Identifier for denied peer [`Modal`].
    pub const DENY_PEER_MODAL: &'static str = "p2p_deny_peer";
    /// Identifier for preferred peer [`Modal`].
    pub const PREFER_PEER_MODAL: &'static str = "p2p_prefer_peer";
    /// Identifier for ban window [`Modal`].
    pub const BAN_WINDOW_MODAL: &'static str = "p2p_ban_window";
    /// Identifier for maximum number of inbound peers [`Modal`].
    pub const MAX_INBOUND_MODAL: &'static str = "p2p_max_inbound";
    /// Identifier for maximum number of outbound peers [`Modal`].
    pub const MAX_OUTBOUND_MODAL: &'static str = "p2p_max_outbound";
    /// Identifier for minimum number of outbound peers [`Modal`].
    pub const MIN_OUTBOUND_MODAL: &'static str = "p2p_min_outbound";

    /// Title for custom DNS Seeds setup section.
    const DNS_SEEDS_TITLE: &'static str = "DNS Seeds";

    pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_settings.p2p_server")));
        View::horizontal_line(ui, Colors::STROKE);
        ui.add_space(6.0);

        ui.vertical_centered(|ui| {
            // Show p2p port setup.
            self.port_ui(ui, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            // Show seeding type setup.
            self.seeding_type_ui(ui, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            ui.label(RichText::new(t!("network_settings.allow_list"))
                .size(16.0)
                .color(Colors::GRAY));
            ui.add_space(6.0);
            // Show allowed peers setup.
            self.peer_list_ui(ui, &PeerType::Allowed, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            ui.label(RichText::new(t!("network_settings.deny_list"))
                .size(16.0)
                .color(Colors::GRAY));
            ui.add_space(6.0);
            // Show denied peers setup.
            self.peer_list_ui(ui, &PeerType::Denied, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            ui.label(RichText::new(t!("network_settings.favourites"))
                .size(16.0)
                .color(Colors::GRAY));
            ui.add_space(6.0);
            // Show preferred peers setup.
            self.peer_list_ui(ui, &PeerType::Preferred, cb);


            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            // Show ban window setup.
            self.ban_window_ui(ui, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            // Show maximum inbound peers value setup.
            self.max_inbound_ui(ui, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            // Show maximum outbound peers value setup.
            self.max_outbound_ui(ui, cb);

            ui.add_space(6.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(6.0);

            // Show minimum outbound peers value setup.
            self.min_outbound_ui(ui, cb);
        });
    }

    /// Draw p2p port setup content.
    fn port_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        ui.label(RichText::new(t!("network_settings.p2p_port"))
            .size(16.0)
            .color(Colors::GRAY)
        );
        ui.add_space(6.0);

        let port = NodeConfig::get_p2p_port();
        View::button(ui, format!("{} {}", PLUG, port.clone()), Colors::BUTTON, || {
            // Setup values for modal.
            self.port_edit = port;
            self.port_available_edit = self.is_port_available;
            // Show p2p port modal.
            Modal::new(Self::PORT_MODAL)
                .position(ModalPosition::CenterTop)
                .title(t!("network_settings.change_value"))
                .show();
            cb.show_keyboard();
        });
        ui.add_space(6.0);

        // Show error when p2p port is unavailable.
        if !self.is_port_available {
            ui.add_space(6.0);
            ui.label(RichText::new(t!("network_settings.port_unavailable"))
                .size(16.0)
                .color(Colors::RED));
            ui.add_space(12.0);
        }
    }

    /// Draw p2p port [`Modal`] content.
    pub fn port_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            ui.label(RichText::new(t!("network_settings.p2p_port"))
                .size(17.0)
                .color(Colors::GRAY));
            ui.add_space(8.0);

            // Draw p2p port text edit.
            let text_edit_resp = egui::TextEdit::singleline(&mut self.port_edit)
                .id(Id::from(modal.id))
                .font(TextStyle::Heading)
                .desired_width(64.0)
                .cursor_at_end(true)
                .ui(ui);
            text_edit_resp.request_focus();
            if text_edit_resp.clicked() {
                cb.show_keyboard();
            }

            // Show error when specified port is unavailable.
            if !self.port_available_edit {
                ui.add_space(12.0);
                ui.label(RichText::new(t!("network_settings.port_unavailable"))
                    .size(17.0)
                    .color(Colors::RED));
            }

            ui.add_space(12.0);

            // Show modal buttons.
            ui.scope(|ui| {
                // Setup spacing between buttons.
                ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

                // Save button callback.
                let on_save = || {
                    // Check if port is available.
                    let available = NodeConfig::is_p2p_port_available(&self.port_edit);
                    self.port_available_edit = available;

                    // Save port at config if it's available.
                    if available {
                        NodeConfig::save_p2p_port(self.port_edit.parse::<u16>().unwrap());

                        self.is_port_available = true;
                        cb.hide_keyboard();
                        modal.close();
                    }
                };

                ui.columns(2, |columns| {
                    columns[0].vertical_centered_justified(|ui| {
                        View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                            // Close modal.
                            cb.hide_keyboard();
                            modal.close();
                        });
                    });
                    columns[1].vertical_centered_justified(|ui| {
                        View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                    });
                });
                ui.add_space(6.0);
            });
        });
    }

    /// Draw peer list content based on provided [`PeerType`].
    fn peer_list_ui(&mut self, ui: &mut Ui, peer_type: &PeerType, cb: &dyn PlatformCallbacks) {
        let peers = match peer_type {
            PeerType::DefaultSeed => {
                if AppConfig::chain_type() == ChainTypes::Testnet {
                    self.default_test_seeds.clone()
                } else {
                    self.default_main_seeds.clone()
                }
            }
            PeerType::CustomSeed => NodeConfig::get_custom_seeds(),
            PeerType::Allowed => NodeConfig::get_allowed_peers(),
            PeerType::Denied => NodeConfig::get_denied_peers(),
            PeerType::Preferred => NodeConfig::get_preferred_peers()
        };
        for (index, peer) in peers.iter().enumerate() {
            ui.horizontal_wrapped(|ui| {
                // Draw peer list item.
                Self::peer_item_ui(ui, peer, peer_type, View::item_rounding(index, peers.len()));
            });
        }

        if peer_type != &PeerType::DefaultSeed {
            // Draw description.
            if peer_type != &PeerType::CustomSeed {
                if !peers.is_empty() {
                    ui.add_space(12.0);
                }
                let desc = match peer_type {
                    PeerType::Allowed => t!("network_settings.allow_list_desc"),
                    PeerType::Denied => t!("network_settings.deny_list_desc"),
                    &_ => t!("network_settings.favourites_desc"),
                };
                ui.label(RichText::new(desc)
                    .size(16.0)
                    .color(Colors::INACTIVE_TEXT));
                ui.add_space(12.0);
            }

            let add_text = if peer_type == &PeerType::CustomSeed {
                format!("{} {}", PLUS_CIRCLE, t!("network_settings.add_seed"))
            } else {
                format!("{} {}", PLUS_CIRCLE, t!("network_settings.add_peer"))

            };
            View::button(ui, add_text, Colors::BUTTON, || {
                // Setup values for modal.
                self.peer_edit = "".to_string();
                // Select modal id.
                let modal_id = match peer_type {
                    PeerType::Allowed => Self::ALLOW_PEER_MODAL,
                    PeerType::Denied => Self::DENY_PEER_MODAL,
                    PeerType::Preferred => Self::PREFER_PEER_MODAL,
                    _ => Self::CUSTOM_SEED_MODAL
                };
                // Select modal title.
                let modal_title = match peer_type {
                    PeerType::Allowed => t!("network_settings.allow_list"),
                    PeerType::Denied => t!("network_settings.deny_list"),
                    PeerType::Preferred => t!("network_settings.favourites"),
                    _ => Self::DNS_SEEDS_TITLE.to_string()
                };
                // Show modal to add peer.
                Modal::new(modal_id)
                    .position(ModalPosition::CenterTop)
                    .title(modal_title)
                    .show();
                cb.show_keyboard();
            });
        }
        ui.add_space(6.0);
    }

    /// Draw peer creation [`Modal`] content.
    pub fn peer_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            let label_text = match modal.id {
                Self::CUSTOM_SEED_MODAL => t!("network_settings.seed_address"),
                &_ => t!("network_settings.peer_address")
            };
            ui.label(RichText::new(label_text).size(17.0).color(Colors::GRAY));
            ui.add_space(8.0);
            StripBuilder::new(ui)
                .size(Size::exact(42.0))
                .vertical(|mut strip| {
                    strip.strip(|builder| {
                        builder
                            .size(Size::remainder())
                            .size(Size::exact(48.0))
                            .horizontal(|mut strip| {
                                strip.cell(|ui| {
                                    ui.add_space(2.0);
                                    // Draw peer address text edit.
                                    let text_edit = egui::TextEdit::singleline(&mut self.peer_edit)
                                        .id(Id::from(modal.id))
                                        .font(TextStyle::Button)
                                        .desired_width(ui.available_width())
                                        .cursor_at_end(true)
                                        .ui(ui);
                                    text_edit.request_focus();
                                    if text_edit.clicked() {
                                        cb.show_keyboard();
                                    }
                                });
                                strip.cell(|ui| {
                                    ui.vertical_centered(|ui| {
                                        // Draw paste button.
                                        let paste_icon = CLIPBOARD_TEXT.to_string();
                                        View::button(ui, paste_icon, Colors::WHITE, || {
                                            self.peer_edit = cb.get_string_from_buffer();
                                        });
                                    });
                                });
                            });
                    })
                });

            // Show error when specified address is incorrect.
            if !self.is_correct_address_edit {
                ui.label(RichText::new(t!("network_settings.peer_address_error"))
                    .size(16.0)
                    .color(Colors::RED));
                ui.add_space(6.0);
            }

            ui.add_space(4.0);

            // Show modal buttons.
            ui.scope(|ui| {
                // Setup spacing between buttons.
                ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

                // Save button callback.
                let on_save = || {
                    // Check if peer is correct and/or available.
                    let peer = self.peer_edit.clone();
                    let is_correct_address = PeersConfig::peer_to_addr(peer.clone()).is_some();
                    self.is_correct_address_edit = is_correct_address;

                    // Save peer at config.
                    if is_correct_address {
                        match modal.id {
                            Self::CUSTOM_SEED_MODAL => NodeConfig::save_custom_seed(peer),
                            Self::ALLOW_PEER_MODAL => NodeConfig::allow_peer(peer),
                            Self::DENY_PEER_MODAL => NodeConfig::deny_peer(peer),
                            Self::PREFER_PEER_MODAL => NodeConfig::prefer_peer(peer),
                            &_ => {}
                        }

                        self.is_port_available = true;
                        cb.hide_keyboard();
                        modal.close();
                    }
                };

                ui.columns(2, |columns| {
                    columns[0].vertical_centered_justified(|ui| {
                        View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                            // Close modal.
                            cb.hide_keyboard();
                            modal.close();
                        });
                    });
                    columns[1].vertical_centered_justified(|ui| {
                        View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                    });
                });
                ui.add_space(6.0);
            });
        });
    }

    /// Draw peer list item.
    fn peer_item_ui(ui: &mut Ui, peer_addr: &String, peer_type: &PeerType, rounding: Rounding) {
        // Setup layout size.
        let mut rect = ui.available_rect_before_wrap();
        rect.set_height(42.0);

        // Draw round background.
        let mut bg_rect = rect.clone();
        bg_rect.min += egui::emath::vec2(6.0, 0.0);
        ui.painter().rect(bg_rect, rounding, Colors::WHITE, View::ITEM_STROKE);

        ui.vertical(|ui| {
            ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
                // Draw delete button for non-default seed peers.
                if peer_type != &PeerType::DefaultSeed {
                    View::item_button(ui, [false, true], TRASH, || {
                        match peer_type {
                            PeerType::CustomSeed => {
                                NodeConfig::remove_custom_seed(peer_addr);
                            }
                            PeerType::Allowed => {
                                NodeConfig::remove_allowed_peer(peer_addr);
                            }
                            PeerType::Denied => {
                                NodeConfig::remove_denied_peer(peer_addr);
                            }
                            PeerType::Preferred => {
                                NodeConfig::remove_preferred_peer(peer_addr);
                            }
                            PeerType::DefaultSeed => {}
                        }
                    });
                }

                let layout_size = ui.available_size();
                ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
                    ui.add_space(12.0);
                    // Draw peer address.
                    let peer_text = format!("{} {}", GLOBE_SIMPLE, &peer_addr);
                    ui.label(RichText::new(peer_text)
                        .color(Colors::TEXT_BUTTON)
                        .size(16.0));
                });
            });
        });
    }

    /// Draw seeding type setup content.
    fn seeding_type_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        let title = Self::DNS_SEEDS_TITLE;
        ui.label(RichText::new(title).size(16.0).color(Colors::GRAY));
        ui.add_space(2.0);

        let default_seeding = NodeConfig::is_default_seeding_type();
        View::checkbox(ui, default_seeding, t!("network_settings.default"), || {
            NodeConfig::toggle_seeding_type();
        });
        ui.add_space(8.0);

        let peers_type = if default_seeding {
            PeerType::DefaultSeed
        } else {
            PeerType::CustomSeed
        };
        self.peer_list_ui(ui, &peers_type, cb);
    }

    /// Draw ban window setup content.
    fn ban_window_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        ui.label(RichText::new(t!("network_settings.ban_window"))
            .size(16.0)
            .color(Colors::GRAY)
        );
        ui.add_space(6.0);

        let ban_window = NodeConfig::get_p2p_ban_window();
        View::button(ui, format!("{} {}", PROHIBIT_INSET, ban_window.clone()), Colors::BUTTON, || {
            // Setup values for modal.
            self.ban_window_edit = ban_window;
            // Show ban window period setup modal.
            Modal::new(Self::BAN_WINDOW_MODAL)
                .position(ModalPosition::CenterTop)
                .title(t!("network_settings.change_value"))
                .show();
            cb.show_keyboard();
        });
        ui.add_space(6.0);
        ui.label(RichText::new(t!("network_settings.ban_window_desc"))
            .size(16.0)
            .color(Colors::INACTIVE_TEXT)
        );
        ui.add_space(2.0);
    }

    /// Draw ban window [`Modal`] content.
    pub fn ban_window_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            ui.label(RichText::new(t!("network_settings.ban_window"))
                .size(17.0)
                .color(Colors::GRAY));
            ui.add_space(8.0);

            // Draw ban window text edit.
            let text_edit_resp = egui::TextEdit::singleline(&mut self.ban_window_edit)
                .id(Id::from(modal.id))
                .font(TextStyle::Heading)
                .desired_width(84.0)
                .cursor_at_end(true)
                .ui(ui);
            text_edit_resp.request_focus();
            if text_edit_resp.clicked() {
                cb.show_keyboard();
            }

            // Show error when specified value is not valid or reminder to restart enabled node.
            if self.ban_window_edit.parse::<i64>().is_err() {
                ui.add_space(12.0);
                ui.label(RichText::new(t!("network_settings.not_valid_value"))
                    .size(17.0)
                    .color(Colors::RED));
            } else {
                NetworkSettings::node_restart_required_ui(ui);
            }
            ui.add_space(12.0);
        });

        // Show modal buttons.
        ui.scope(|ui| {
            // Setup spacing between buttons.
            ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

            // Save button callback.
            let on_save = || {
                if let Ok(ban_window) = self.ban_window_edit.parse::<i64>() {
                    NodeConfig::save_p2p_ban_window(ban_window);
                    cb.hide_keyboard();
                    modal.close();
                }
            };

            ui.columns(2, |columns| {
                columns[0].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                        // Close modal.
                        cb.hide_keyboard();
                        modal.close();
                    });
                });
                columns[1].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                });
            });
            ui.add_space(6.0);
        });
    }

    /// Draw maximum number of inbound peers setup content.
    fn max_inbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        ui.label(RichText::new(t!("network_settings.max_inbound_count"))
            .size(16.0)
            .color(Colors::GRAY)
        );
        ui.add_space(6.0);

        let max_inbound = NodeConfig::get_max_inbound_peers();
        let button_text = format!("{} {}", ARROW_FAT_LINES_DOWN, max_inbound.clone());
        View::button(ui, button_text, Colors::BUTTON, || {
            // Setup values for modal.
            self.max_inbound_count = max_inbound;
            // Show maximum number of inbound peers setup modal.
            Modal::new(Self::MAX_INBOUND_MODAL)
                .position(ModalPosition::CenterTop)
                .title(t!("network_settings.change_value"))
                .show();
            cb.show_keyboard();
        });
        ui.add_space(6.0);
    }

    /// Draw maximum number of inbound peers [`Modal`] content.
    pub fn max_inbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            ui.label(RichText::new(t!("network_settings.max_inbound_count"))
                .size(17.0)
                .color(Colors::GRAY));
            ui.add_space(8.0);

            // Draw maximum number of inbound peers text edit.
            let text_edit_resp = egui::TextEdit::singleline(&mut self.max_inbound_count)
                .id(Id::from(modal.id))
                .font(TextStyle::Heading)
                .desired_width(42.0)
                .cursor_at_end(true)
                .ui(ui);
            text_edit_resp.request_focus();
            if text_edit_resp.clicked() {
                cb.show_keyboard();
            }

            // Show error when specified value is not valid or reminder to restart enabled node.
            if self.max_inbound_count.parse::<u32>().is_err() {
                ui.add_space(12.0);
                ui.label(RichText::new(t!("network_settings.not_valid_value"))
                    .size(17.0)
                    .color(Colors::RED));
            } else {
                NetworkSettings::node_restart_required_ui(ui);
            }
            ui.add_space(12.0);
        });

        // Show modal buttons.
        ui.scope(|ui| {
            // Setup spacing between buttons.
            ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

            // Save button callback.
            let on_save = || {
                if let Ok(max_inbound) = self.max_inbound_count.parse::<u32>() {
                    NodeConfig::save_max_inbound_peers(max_inbound);
                    cb.hide_keyboard();
                    modal.close();
                }
            };

            ui.columns(2, |columns| {
                columns[0].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                        // Close modal.
                        cb.hide_keyboard();
                        modal.close();
                    });
                });
                columns[1].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                });
            });
            ui.add_space(6.0);
        });
    }

    /// Draw maximum number of outbound peers setup content.
    fn max_outbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        ui.label(RichText::new(t!("network_settings.max_outbound_count"))
            .size(16.0)
            .color(Colors::GRAY)
        );
        ui.add_space(6.0);

        let max_outbound = NodeConfig::get_max_outbound_peers();
        let button_text = format!("{} {}", ARROW_FAT_LINES_UP, max_outbound.clone());
        View::button(ui, button_text, Colors::BUTTON, || {
            // Setup values for modal.
            self.max_outbound_count = max_outbound;
            // Show maximum number of outbound peers setup modal.
            Modal::new(Self::MAX_OUTBOUND_MODAL)
                .position(ModalPosition::CenterTop)
                .title(t!("network_settings.change_value"))
                .show();
            cb.show_keyboard();
        });
        ui.add_space(6.0);
    }

    /// Draw maximum number of outbound peers [`Modal`] content.
    pub fn max_outbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            ui.label(RichText::new(t!("network_settings.max_outbound_count"))
                .size(17.0)
                .color(Colors::GRAY));
            ui.add_space(8.0);

            // Draw maximum number of outbound peers text edit.
            let text_edit_resp = egui::TextEdit::singleline(&mut self.max_outbound_count)
                .id(Id::from(modal.id))
                .font(TextStyle::Heading)
                .desired_width(42.0)
                .cursor_at_end(true)
                .ui(ui);
            text_edit_resp.request_focus();
            if text_edit_resp.clicked() {
                cb.show_keyboard();
            }

            // Show error when specified value is not valid or reminder to restart enabled node.
            if self.max_outbound_count.parse::<u32>().is_err() {
                ui.add_space(12.0);
                ui.label(RichText::new(t!("network_settings.not_valid_value"))
                    .size(17.0)
                    .color(Colors::RED));
            } else {
                NetworkSettings::node_restart_required_ui(ui);
            }
            ui.add_space(12.0);
        });

        // Show modal buttons.
        ui.scope(|ui| {
            // Setup spacing between buttons.
            ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

            // Save button callback.
            let on_save = || {
                if let Ok(max_outbound) = self.max_outbound_count.parse::<u32>() {
                    NodeConfig::save_max_outbound_peers(max_outbound);
                    cb.hide_keyboard();
                    modal.close();
                }
            };

            ui.columns(2, |columns| {
                columns[0].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                        // Close modal.
                        cb.hide_keyboard();
                        modal.close();
                    });
                });
                columns[1].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                });
            });
            ui.add_space(6.0);
        });
    }

    /// Draw minimum number of outbound peers setup content.
    fn min_outbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) {
        ui.label(RichText::new(t!("network_settings.min_outbound_count"))
            .size(16.0)
            .color(Colors::GRAY)
        );
        ui.add_space(6.0);

        let min_outbound = NodeConfig::get_min_outbound_peers();
        let button_text = format!("{} {}", ARROW_FAT_LINE_UP, min_outbound.clone());
        View::button(ui, button_text, Colors::BUTTON, || {
            // Setup values for modal.
            self.min_outbound_count = min_outbound;
            // Show maximum number of outbound peers setup modal.
            Modal::new(Self::MIN_OUTBOUND_MODAL)
                .position(ModalPosition::CenterTop)
                .title(t!("network_settings.change_value"))
                .show();
            cb.show_keyboard();
        });
        ui.add_space(6.0);
        ui.label(RichText::new(t!("network_settings.min_outbound_desc"))
            .size(16.0)
            .color(Colors::INACTIVE_TEXT)
        );
    }

    /// Draw minimum number of outbound peers [`Modal`] content.
    pub fn min_outbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        ui.add_space(6.0);
        ui.vertical_centered(|ui| {
            ui.label(RichText::new(t!("network_settings.min_outbound_count"))
                .size(17.0)
                .color(Colors::GRAY));
            ui.add_space(8.0);

            // Draw maximum number of outbound peers text edit.
            let text_edit_resp = egui::TextEdit::singleline(&mut self.min_outbound_count)
                .id(Id::from(modal.id))
                .font(TextStyle::Heading)
                .desired_width(42.0)
                .cursor_at_end(true)
                .ui(ui);
            text_edit_resp.request_focus();
            if text_edit_resp.clicked() {
                cb.show_keyboard();
            }

            // Show error when specified value is not valid or reminder to restart enabled node.
            if self.min_outbound_count.parse::<u32>().is_err() {
                ui.add_space(12.0);
                ui.label(RichText::new(t!("network_settings.not_valid_value"))
                    .size(17.0)
                    .color(Colors::RED));
            } else {
                NetworkSettings::node_restart_required_ui(ui);
            }
            ui.add_space(12.0);
        });

        // Show modal buttons.
        ui.scope(|ui| {
            // Setup spacing between buttons.
            ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);

            // Save button callback.
            let on_save = || {
                if let Ok(max_outbound) = self.min_outbound_count.parse::<u32>() {
                    NodeConfig::save_min_outbound_peers(max_outbound);
                    cb.hide_keyboard();
                    modal.close();
                }
            };

            ui.columns(2, |columns| {
                columns[0].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
                        // Close modal.
                        cb.hide_keyboard();
                        modal.close();
                    });
                });
                columns[1].vertical_centered_justified(|ui| {
                    View::button(ui, t!("modal.save"), Colors::WHITE, on_save);
                });
            });
            ui.add_space(6.0);
        });
    }
}