// 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 chrono::{DateTime, NaiveDateTime, Utc};
use egui::{RichText, Rounding, ScrollArea};
use grin_chain::SyncStatus;
use grin_servers::WorkerStats;

use crate::gui::Colors;
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_SIMPLE_MINUS, FOLDER_SIMPLE_PLUS, HARD_DRIVES, PLUGS, PLUGS_CONNECTED, POLYGON};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Network, View};
use crate::gui::views::network::setup::StratumSetup;
use crate::gui::views::types::{NetworkTab, NetworkTabType};
use crate::node::{Node, NodeConfig};

/// Mining tab content.
#[derive(Default)]
pub struct NetworkMining {
    stratum_server_setup: StratumSetup
}

impl NetworkTab for NetworkMining {
    fn get_type(&self) -> NetworkTabType {
        NetworkTabType::Mining
    }

    fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
        let server_stats = Node::get_stats();

        // Show message to enable node when it's not running.
        if !Node::is_running() {
            Network::disabled_node_ui(ui);
            return;
        }

        // Show loading spinner when node is stopping or stratum server is starting.
        if Node::is_stopping() || Node::is_stratum_starting() {
            ui.centered_and_justified(|ui| {
                View::big_loading_spinner(ui);
            });
            return;
        }

        // Show message when mining is not available.
        if server_stats.is_none() || Node::is_restarting()
            || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
            View::center_content(ui, 162.0, |ui| {
                View::big_loading_spinner(ui);
                ui.add_space(18.0);
                ui.label(RichText::new(t!("network_mining.loading"))
                    .size(16.0)
                    .color(Colors::INACTIVE_TEXT)
                );
            });
            return;
        }

        let stratum_stats = Node::get_stratum_stats();

        // Show stratum server setup when mining server is not running.
        if !stratum_stats.is_running {
            ScrollArea::vertical()
                .id_source("stratum_setup_scroll")
                .auto_shrink([false; 2])
                .show(ui, |ui| {
                    self.stratum_server_setup.ui(ui, cb);
                });
            return;
        }

        // Show stratum mining server info.
        View::sub_title(ui, format!("{} {}", HARD_DRIVES, t!("network_mining.server")));
        ui.columns(2, |columns| {
            columns[0].vertical_centered(|ui| {
                let (stratum_addr, stratum_port) = NodeConfig::get_stratum_address();
                View::rounded_box(ui,
                                  format!("{}:{}", stratum_addr, stratum_port),
                                  t!("network_mining.address"),
                                  [true, false, true, false]);
            });
            columns[1].vertical_centered(|ui| {
                //TODO: Stratum mining wallet listening address. Replace with local wallet name.
                let wallet_addr = NodeConfig::get_stratum_wallet_addr().replace("http://", "");
                View::rounded_box(ui,
                                  wallet_addr,
                                  t!("network_mining.rewards_wallet"),
                                  [false, true, false, true]);
            });
        });
        ui.add_space(4.0);

        // Show network info.
        View::sub_title(ui, format!("{} {}", POLYGON, t!("network.self")));
        ui.columns(3, |columns| {
            columns[0].vertical_centered(|ui| {
                let difficulty = if stratum_stats.network_difficulty > 0 {
                    stratum_stats.network_difficulty.to_string()
                } else {
                    "-".into()
                };
                View::rounded_box(ui,
                                  difficulty,
                                  t!("network_node.difficulty"),
                                  [true, false, true, false]);
            });
            columns[1].vertical_centered(|ui| {
                let block_height = if stratum_stats.block_height > 0 {
                    stratum_stats.block_height.to_string()
                } else {
                    "-".into()
                };
                View::rounded_box(ui,
                                  block_height,
                                  t!("network_node.header"),
                                  [false, false, false, false]);
            });
            columns[2].vertical_centered(|ui| {
                let hashrate = if stratum_stats.network_hashrate > 0.0 {
                    format!("{:.*}", 2, stratum_stats.network_hashrate)
                } else {
                    "-".into()
                };
                View::rounded_box(ui,
                                  hashrate,
                                  t!("network_mining.hashrate", "bits" => stratum_stats.edge_bits),
                                  [false, true, false, true]);
            });
        });
        ui.add_space(4.0);

        // Show mining info.
        View::sub_title(ui, format!("{} {}", CPU, t!("network_mining.miners")));
        ui.columns(2, |columns| {
            columns[0].vertical_centered(|ui| {
                View::rounded_box(ui,
                                  stratum_stats.num_workers.to_string(),
                                  t!("network_mining.devices"),
                                  [true, false, true, false]);
            });

            columns[1].vertical_centered(|ui| {
                View::rounded_box(ui,
                                  stratum_stats.blocks_found.to_string(),
                                  t!("network_mining.blocks_found"),
                                  [false, true, false, true]);
            });
        });
        ui.add_space(4.0);

        // Show workers stats or info text when possible.
        let workers_size = stratum_stats.worker_stats.len();
        if workers_size != 0 && stratum_stats.num_workers > 0 {
            ui.add_space(4.0);
            View::horizontal_line(ui, Colors::ITEM_STROKE);
            ui.add_space(4.0);
            ScrollArea::vertical()
                .auto_shrink([false; 2])
                .id_source("stratum_workers_scroll")
                .show_rows(
                    ui,
                    WORKER_ITEM_HEIGHT,
                    workers_size,
                    |ui, row_range| {
                        for index in row_range {
                            // Add space before the first item.
                            if index == 0 {
                                ui.add_space(4.0);
                            }
                            let worker = stratum_stats.worker_stats.get(index).unwrap();
                            worker_item_ui(ui, worker, View::item_rounding(index, workers_size));
                        }
                    },
                );
        } else if ui.available_height() > 142.0 {
            View::center_content(ui, 142.0, |ui| {
                ui.label(RichText::new(t!("network_mining.info", "settings" => FADERS))
                    .size(16.0)
                    .color(Colors::INACTIVE_TEXT)
                );
            });
        }
    }

    fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
        match modal.id {
            StratumSetup::STRATUM_PORT_MODAL => {
                self.stratum_server_setup.port_modal(ui, modal, cb);
            },
            StratumSetup::ATTEMPT_TIME_MODAL => {
                self.stratum_server_setup.attempt_modal(ui, modal, cb);
            },
            StratumSetup::MIN_SHARE_DIFF_MODAL => {
                self.stratum_server_setup.min_diff_modal(ui, modal, cb);
            },
            _ => {}
        }
    }
}

const WORKER_ITEM_HEIGHT: f32 = 76.0;

/// Draw worker statistics item.
fn worker_item_ui(ui: &mut egui::Ui, ws: &WorkerStats, rounding: Rounding) {
    ui.horizontal_wrapped(|ui| {
        ui.vertical_centered_justified(|ui| {
            // Draw round background.
            let mut rect = ui.available_rect_before_wrap();
            rect.set_height(WORKER_ITEM_HEIGHT);
            ui.painter().rect(rect, rounding, Colors::WHITE, View::ITEM_STROKE);

            ui.add_space(2.0);
            ui.horizontal(|ui| {
                ui.add_space(5.0);

                // Draw worker connection status.
                let (status_text, status_icon, status_color) = match ws.is_connected {
                    true => (t!("network_mining.connected"), PLUGS_CONNECTED, Colors::BLACK),
                    false => (t!("network_mining.disconnected"), PLUGS, Colors::INACTIVE_TEXT)
                };
                let status_line_text = format!("{} {} {}", status_icon, ws.id, status_text);
                ui.heading(RichText::new(status_line_text)
                    .color(status_color)
                    .size(17.0));
                ui.add_space(2.0);
            });
            ui.horizontal(|ui| {
                ui.add_space(6.0);

                // Draw difficulty.
                let diff_text = format!("{} {}", BARBELL, ws.pow_difficulty);
                ui.heading(RichText::new(diff_text)
                    .color(Colors::TITLE)
                    .size(16.0));
                ui.add_space(6.0);

                // Draw accepted shares.
                let accepted_text = format!("{} {}", FOLDER_SIMPLE_PLUS, ws.num_accepted);
                ui.heading(RichText::new(accepted_text)
                    .color(Colors::GREEN)
                    .size(16.0));
                ui.add_space(6.0);

                // Draw rejected shares.
                let rejected_text = format!("{} {}", FOLDER_SIMPLE_MINUS, ws.num_rejected);
                ui.heading(RichText::new(rejected_text)
                    .color(Colors::RED)
                    .size(16.0));
                ui.add_space(6.0);

                // Draw stale shares.
                let stale_text = format!("{} {}", FOLDER_DASHED, ws.num_stale);
                ui.heading(RichText::new(stale_text)
                    .color(Colors::GRAY)
                    .size(16.0));
                ui.add_space(6.0);

                // Draw blocks found.
                let blocks_found_text = format!("{} {}", CUBE, ws.num_blocks_found);
                ui.heading(RichText::new(blocks_found_text)
                    .color(Colors::TITLE)
                    .size(16.0));
            });
            ui.horizontal(|ui| {
                ui.add_space(6.0);

                // Draw block time
                let seen = ws.last_seen.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
                let naive_datetime = NaiveDateTime::from_timestamp_opt(seen as i64, 0).unwrap();
                let datetime: DateTime<Utc> = DateTime::from_utc(naive_datetime, Utc);
                let date_text = format!("{} {}",
                                        CLOCK_AFTERNOON,
                                        datetime.format("%d/%m/%Y %H:%M:%S UTC"));
                ui.heading(RichText::new(date_text)
                    .color(Colors::GRAY)
                    .size(16.0));
            });
        });
    });
}