// 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::RichText;
use egui::style::Margin;

use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalContainer, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, TitleType, View};
use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup};
use crate::gui::views::types::{NetworkTab, NetworkTabType};
use crate::node::Node;

/// Network content.
pub struct Network {
    /// Current tab view to show at ui.
    current_tab: Box<dyn NetworkTab>,
    /// [`Modal`] ids allowed at this ui container.
    modal_ids: Vec<&'static str>,
}

impl Default for Network {
    fn default() -> Self {
        Self {
            current_tab: Box::new(NetworkNode::default()),
            modal_ids: vec![
                // Network settings modals.
                NetworkSettings::NODE_RESTART_REQUIRED_MODAL,
                NetworkSettings::RESET_SETTINGS_MODAL,
                // Node setup modals.
                NodeSetup::API_PORT_MODAL,
                NodeSetup::API_SECRET_MODAL,
                NodeSetup::FOREIGN_API_SECRET_MODAL,
                NodeSetup::FTL_MODAL,
                // P2P setup modals.
                P2PSetup::PORT_MODAL,
                P2PSetup::CUSTOM_SEED_MODAL,
                P2PSetup::ALLOW_PEER_MODAL,
                P2PSetup::DENY_PEER_MODAL,
                P2PSetup::PREFER_PEER_MODAL,
                P2PSetup::BAN_WINDOW_MODAL,
                P2PSetup::MAX_INBOUND_MODAL,
                P2PSetup::MAX_OUTBOUND_MODAL,
                P2PSetup::MIN_OUTBOUND_MODAL,
                // Stratum setup modals.
                StratumSetup::STRATUM_PORT_MODAL,
                StratumSetup::ATTEMPT_TIME_MODAL,
                StratumSetup::MIN_SHARE_DIFF_MODAL,
                // Pool setup modals.
                PoolSetup::FEE_BASE_MODAL,
                PoolSetup::REORG_PERIOD_MODAL,
                PoolSetup::POOL_SIZE_MODAL,
                PoolSetup::STEMPOOL_SIZE_MODAL,
                PoolSetup::MAX_WEIGHT_MODAL,
                // Dandelion setup modals.
                DandelionSetup::EPOCH_MODAL,
                DandelionSetup::EMBARGO_MODAL,
                DandelionSetup::AGGREGATION_MODAL,
                DandelionSetup::STEM_PROBABILITY_MODAL,
            ]
        }
    }
}

impl ModalContainer for Network {
    fn modal_ids(&self) -> &Vec<&'static str> {
        self.modal_ids.as_ref()
    }
}

impl Network {
    pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
        // Show modal content for current ui container.
        if self.can_draw_modal() {
            Modal::ui(ui, |ui, modal| {
                self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
            });
        }

        // Show title panel.
        self.title_ui(ui, frame);

        // Show bottom tabs.
        egui::TopBottomPanel::bottom("network_tabs")
            .frame(egui::Frame {
                fill: Colors::FILL,
                inner_margin: Self::tabs_inner_margin(ui, frame),
                ..Default::default()
            })
            .show_inside(ui, |ui| {
                self.tabs_ui(ui);
            });

        // Show tab content.
        egui::CentralPanel::default()
            .frame(egui::Frame {
                stroke: View::DEFAULT_STROKE,
                inner_margin: Self::content_inner_margin(ui, frame),
                fill: Colors::WHITE,
                ..Default::default()
            })
            .show_inside(ui, |ui| {
                self.current_tab.ui(ui, cb);
            });

        // Redraw content after delay if node is not syncing to update stats.
        if Node::not_syncing() {
            ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY);
        }
    }

    /// Calculate tabs inner margin based on display insets (cutouts).
    fn tabs_inner_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Margin {
        Margin {
            left: View::get_left_inset() + 4.0,
            right: View::far_right_inset_margin(ui, frame) + 4.0,
            top: 4.0,
            bottom: View::get_bottom_inset() + 4.0,
        }
    }

    /// Calculate content inner margin based on display insets (cutouts).
    fn content_inner_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Margin {
        Margin {
            left: View::get_left_inset() + 4.0,
            right: View::far_right_inset_margin(ui, frame) + 4.0,
            top: 3.0,
            bottom: 4.0,
        }
    }

    /// Draw tab buttons in the bottom of the screen.
    fn tabs_ui(&mut self, ui: &mut egui::Ui) {
        ui.scope(|ui| {
            // Setup spacing between tabs.
            ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0);
            // Setup vertical padding inside tab button.
            ui.style_mut().spacing.button_padding = egui::vec2(0.0, 8.0);

            // Draw tab buttons.
            let current_type = self.current_tab.get_type();
            ui.columns(4, |columns| {
                columns[0].vertical_centered_justified(|ui| {
                    View::tab_button(ui, DATABASE, current_type == NetworkTabType::Node, || {
                            self.current_tab = Box::new(NetworkNode::default());
                        });
                });
                columns[1].vertical_centered_justified(|ui| {
                    View::tab_button(ui, GAUGE, current_type == NetworkTabType::Metrics, || {
                            self.current_tab = Box::new(NetworkMetrics::default());
                        });
                });
                columns[2].vertical_centered_justified(|ui| {
                    View::tab_button(ui, FACTORY, current_type == NetworkTabType::Mining, || {
                            self.current_tab = Box::new(NetworkMining::default());
                        });
                });
                columns[3].vertical_centered_justified(|ui| {
                    View::tab_button(ui, FADERS, current_type == NetworkTabType::Settings, || {
                            self.current_tab = Box::new(NetworkSettings::default());
                        });
                });
            });
        });
    }

    /// Draw title content.
    fn title_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
        // Setup values for title panel.
        let title_text = self.current_tab.get_type().title().to_uppercase();
        let subtitle_text = Node::get_sync_status_text();
        let not_syncing = Node::not_syncing();
        let title_content = TitleType::WithSubTitle(title_text, subtitle_text, !not_syncing);

        // Draw title panel.
        TitlePanel::ui(title_content, |ui, frame| {
            View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
                //TODO: Show connections
            });
        }, |ui, frame| {
            if !Root::is_dual_panel_mode(frame) {
                View::title_button(ui, CARDHOLDER, || {
                    Root::toggle_network_panel();
                });
            }
        }, ui, frame);
    }

    /// Content to draw when node is disabled.
    pub fn disabled_node_ui(ui: &mut egui::Ui) {
        View::center_content(ui, 156.0, |ui| {
            let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
            ui.label(RichText::new(text)
                .size(16.0)
                .color(Colors::INACTIVE_TEXT)
            );
            ui.add_space(8.0);
            View::button(ui, format!("{} {}", POWER, t!("network.enable_node")), Colors::GOLD, || {
                Node::start();
            });
            ui.add_space(2.0);
            Self::autorun_node_ui(ui);
        });
    }

    /// Draw checkbox to run integrated node on application launch.
    pub fn autorun_node_ui(ui: &mut egui::Ui) {
        let autostart = AppConfig::autostart_node();
        View::checkbox(ui, autostart, t!("network.autorun"), || {
            AppConfig::toggle_node_autostart();
        });
    }
}