//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity

/**
 * MIT License
 *
 * termscp - Copyright (c) 2021 Christian Visintin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
// locals
use super::{
    AuthActivity, FileTransferProtocol, InputMask, COMPONENT_BOOKMARKS_LIST, COMPONENT_INPUT_ADDR,
    COMPONENT_INPUT_BOOKMARK_NAME, COMPONENT_INPUT_PASSWORD, COMPONENT_INPUT_PORT,
    COMPONENT_INPUT_S3_BUCKET, COMPONENT_INPUT_S3_PROFILE, COMPONENT_INPUT_S3_REGION,
    COMPONENT_INPUT_USERNAME, COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
    COMPONENT_RADIO_BOOKMARK_DEL_RECENT, COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
    COMPONENT_RADIO_INSTALL_UPDATE, COMPONENT_RADIO_PROTOCOL, COMPONENT_RADIO_QUIT,
    COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP, COMPONENT_TEXT_INFO,
    COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR, COMPONENT_TEXT_WAIT,
};
use crate::ui::keymap::*;
use tui_realm_stdlib::InputPropsBuilder;
use tuirealm::{Msg, Payload, PropsBuilder, Update, Value};

// -- update

impl Update for AuthActivity {
    /// ### update
    ///
    /// Update auth activity model based on msg
    /// The function exits when returns None
    fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
        let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
        // Match msg
        match ref_msg {
            None => None, // Exit after None
            Some(msg) => match msg {
                // Focus ( DOWN )
                (COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_DOWN => {
                    // Give focus based on current mask
                    match self.input_mask() {
                        InputMask::Generic => self.view.active(COMPONENT_INPUT_ADDR),
                        InputMask::AwsS3 => self.view.active(COMPONENT_INPUT_S3_BUCKET),
                    };
                    None
                }
                // -- generic mask (DOWN)
                (COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_INPUT_PORT);
                    None
                }
                (COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_INPUT_USERNAME);
                    None
                }
                (COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_INPUT_PASSWORD);
                    None
                }
                (COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_RADIO_PROTOCOL);
                    None
                }
                // -- s3 mask (DOWN)
                (COMPONENT_INPUT_S3_BUCKET, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_INPUT_S3_REGION);
                    None
                }
                (COMPONENT_INPUT_S3_REGION, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_INPUT_S3_PROFILE);
                    None
                }
                (COMPONENT_INPUT_S3_PROFILE, key) if key == &MSG_KEY_DOWN => {
                    self.view.active(COMPONENT_RADIO_PROTOCOL);
                    None
                }
                // Focus ( UP )
                // -- generic (UP)
                (COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_INPUT_USERNAME);
                    None
                }
                (COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_INPUT_PORT);
                    None
                }
                (COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_INPUT_ADDR);
                    None
                }
                (COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_RADIO_PROTOCOL);
                    None
                }
                // -- s3 (UP)
                (COMPONENT_INPUT_S3_BUCKET, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_RADIO_PROTOCOL);
                    None
                }
                (COMPONENT_INPUT_S3_REGION, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_INPUT_S3_BUCKET);
                    None
                }
                (COMPONENT_INPUT_S3_PROFILE, key) if key == &MSG_KEY_UP => {
                    self.view.active(COMPONENT_INPUT_S3_REGION);
                    None
                }
                (COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_UP => {
                    // Give focus based on current mask
                    match self.input_mask() {
                        InputMask::Generic => self.view.active(COMPONENT_INPUT_PASSWORD),
                        InputMask::AwsS3 => self.view.active(COMPONENT_INPUT_S3_PROFILE),
                    };
                    None
                }
                // Protocol - On Change
                (COMPONENT_RADIO_PROTOCOL, Msg::OnChange(Payload::One(Value::Usize(protocol)))) => {
                    // If port is standard, update the current port with default for selected protocol
                    let protocol: FileTransferProtocol = Self::protocol_opt_to_enum(*protocol);
                    // Get port
                    let port: u16 = self.get_input_port();
                    match Self::is_port_standard(port) {
                        false => None, // Return None
                        true => {
                            self.update_input_port(Self::get_default_port_for_protocol(protocol))
                        }
                    }
                }
                // Bookmarks commands
                // <RIGHT> / <LEFT>
                (COMPONENT_BOOKMARKS_LIST, key) if key == &MSG_KEY_RIGHT => {
                    // Give focus to recents
                    self.view.active(COMPONENT_RECENTS_LIST);
                    None
                }
                (COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_LEFT => {
                    // Give focus to bookmarks
                    self.view.active(COMPONENT_BOOKMARKS_LIST);
                    None
                }
                // <DEL | 'E'>
                (COMPONENT_BOOKMARKS_LIST, key)
                    if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E =>
                {
                    // Show delete popup
                    self.mount_bookmark_del_dialog();
                    None
                }
                (COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E => {
                    // Show delete popup
                    self.mount_recent_del_dialog();
                    None
                }
                // Enter
                (COMPONENT_BOOKMARKS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
                    self.load_bookmark(*idx);
                    // Give focus to input password (or to protocol if not generic)
                    self.view.active(match self.input_mask() {
                        InputMask::Generic => COMPONENT_INPUT_PASSWORD,
                        InputMask::AwsS3 => COMPONENT_INPUT_S3_BUCKET,
                    });
                    None
                }
                (COMPONENT_RECENTS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
                    self.load_recent(*idx);
                    // Give focus to input password
                    self.view.active(match self.input_mask() {
                        InputMask::Generic => COMPONENT_INPUT_PASSWORD,
                        InputMask::AwsS3 => COMPONENT_INPUT_S3_BUCKET,
                    });
                    None
                }
                // Bookmark radio
                // Del bookmarks
                (
                    COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
                    Msg::OnSubmit(Payload::One(Value::Usize(index))),
                ) => {
                    // hide bookmark delete
                    self.umount_bookmark_del_dialog();
                    // Index must be 0 => YES
                    match *index {
                        0 => {
                            // Get selected bookmark
                            match self.view.get_state(COMPONENT_BOOKMARKS_LIST) {
                                Some(Payload::One(Value::Usize(index))) => {
                                    // Delete bookmark
                                    self.del_bookmark(index);
                                    // Update bookmarks
                                    self.view_bookmarks()
                                }
                                _ => None,
                            }
                        }
                        _ => None,
                    }
                }
                (
                    COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
                    Msg::OnSubmit(Payload::One(Value::Usize(index))),
                ) => {
                    // hide bookmark delete
                    self.umount_recent_del_dialog();
                    // Index must be 0 => YES
                    match *index {
                        0 => {
                            // Get selected bookmark
                            match self.view.get_state(COMPONENT_RECENTS_LIST) {
                                Some(Payload::One(Value::Usize(index))) => {
                                    // Delete recent
                                    self.del_recent(index);
                                    // Update bookmarks
                                    self.view_recent_connections()
                                }
                                _ => None,
                            }
                        }
                        _ => None,
                    }
                }
                // <ESC> hide tab
                (COMPONENT_RADIO_BOOKMARK_DEL_RECENT, key) if key == &MSG_KEY_ESC => {
                    self.umount_recent_del_dialog();
                    None
                }
                (COMPONENT_RADIO_BOOKMARK_DEL_RECENT, _) => None,
                (COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, key) if key == &MSG_KEY_ESC => {
                    self.umount_bookmark_del_dialog();
                    None
                }
                (COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, _) => None,
                // Error message
                (COMPONENT_TEXT_ERROR, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
                    // Umount text error
                    self.umount_error();
                    None
                }
                // -- Text info
                (COMPONENT_TEXT_INFO, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
                    // Umount text info
                    self.umount_info();
                    None
                }
                (COMPONENT_TEXT_ERROR, _) | (COMPONENT_TEXT_INFO, _) => None,
                // -- Text wait
                (COMPONENT_TEXT_WAIT, _) => None,
                // -- Release notes
                (COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_ESC => {
                    // Umount release notes
                    self.umount_release_notes();
                    None
                }
                (COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_TAB => {
                    // Focus to radio update
                    self.view.active(COMPONENT_RADIO_INSTALL_UPDATE);
                    None
                }
                (COMPONENT_TEXT_NEW_VERSION_NOTES, _) => None,
                // -- Install update radio
                (COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
                    // Install update
                    self.install_update();
                    None
                }
                (COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => {
                    // Umount
                    self.umount_release_notes();
                    None
                }
                (COMPONENT_RADIO_INSTALL_UPDATE, key) if key == &MSG_KEY_TAB => {
                    // Focus to changelog
                    self.view.active(COMPONENT_TEXT_NEW_VERSION_NOTES);
                    None
                }
                (COMPONENT_RADIO_INSTALL_UPDATE, _) => None,
                // Help
                (_, key) if key == &MSG_KEY_CTRL_H => {
                    // Show help
                    self.mount_help();
                    None
                }
                // Release notes
                (_, key) if key == &MSG_KEY_CTRL_R => {
                    // Show release notes
                    self.mount_release_notes();
                    None
                }
                (COMPONENT_TEXT_HELP, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
                    // Hide text help
                    self.umount_help();
                    None
                }
                (COMPONENT_TEXT_HELP, _) => None,
                // Enter setup
                (_, key) if key == &MSG_KEY_CTRL_C => {
                    self.exit_reason = Some(super::ExitReason::EnterSetup);
                    None
                }
                // Save bookmark; show popup
                (_, key) if key == &MSG_KEY_CTRL_S => {
                    // Show popup
                    self.mount_bookmark_save_dialog();
                    // Give focus to bookmark name
                    self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
                    None
                }
                (COMPONENT_INPUT_BOOKMARK_NAME, key) if key == &MSG_KEY_DOWN => {
                    // Give focus to pwd
                    self.view.active(COMPONENT_RADIO_BOOKMARK_SAVE_PWD);
                    None
                }
                (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key) if key == &MSG_KEY_UP => {
                    // Give focus to pwd
                    self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
                    None
                }
                // Save bookmark
                (COMPONENT_INPUT_BOOKMARK_NAME, Msg::OnSubmit(_))
                | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, Msg::OnSubmit(_)) => {
                    // Get values
                    let bookmark_name: String =
                        match self.view.get_state(COMPONENT_INPUT_BOOKMARK_NAME) {
                            Some(Payload::One(Value::Str(s))) => s,
                            _ => String::new(),
                        };
                    let save_pwd: bool = matches!(
                        self.view.get_state(COMPONENT_RADIO_BOOKMARK_SAVE_PWD),
                        Some(Payload::One(Value::Usize(0)))
                    );
                    // Save bookmark
                    if !bookmark_name.is_empty() {
                        self.save_bookmark(bookmark_name, save_pwd);
                    }
                    // Umount popup
                    self.umount_bookmark_save_dialog();
                    // Reload bookmarks
                    self.view_bookmarks()
                }
                // Hide save bookmark
                (COMPONENT_INPUT_BOOKMARK_NAME, key) | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key)
                    if key == &MSG_KEY_ESC =>
                {
                    // Umount popup
                    self.umount_bookmark_save_dialog();
                    None
                }
                (COMPONENT_INPUT_BOOKMARK_NAME, _) | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, _) => None,
                // Quit dialog
                (COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(choice)))) => {
                    // If choice is 0, quit termscp
                    if *choice == 0 {
                        self.exit_reason = Some(super::ExitReason::Quit);
                    }
                    self.umount_quit();
                    None
                }
                (COMPONENT_RADIO_QUIT, key) if key == &MSG_KEY_ESC => {
                    self.umount_quit();
                    None
                }
                // -- text size error; block everything
                (COMPONENT_TEXT_SIZE_ERR, _) => None,
                // <TAB> bookmarks
                (COMPONENT_BOOKMARKS_LIST, key) | (COMPONENT_RECENTS_LIST, key)
                    if key == &MSG_KEY_TAB =>
                {
                    // Give focus to address
                    self.view.active(COMPONENT_RADIO_PROTOCOL);
                    None
                }
                // Any <TAB>, go to bookmarks
                (_, key) if key == &MSG_KEY_TAB => {
                    self.view.active(COMPONENT_BOOKMARKS_LIST);
                    None
                }
                // On submit on any unhandled (connect)
                (_, Msg::OnSubmit(_)) => self.on_unhandled_submit(),
                (_, key) if key == &MSG_KEY_ENTER => self.on_unhandled_submit(),
                // <ESC> => Quit
                (_, key) if key == &MSG_KEY_ESC => {
                    self.mount_quit();
                    None
                }
                (_, _) => None, // Ignore other events
            },
        }
    }
}

impl AuthActivity {
    fn update_input_port(&mut self, port: u16) -> Option<(String, Msg)> {
        match self.view.get_props(COMPONENT_INPUT_PORT) {
            None => None,
            Some(props) => {
                let props = InputPropsBuilder::from(props)
                    .with_value(port.to_string())
                    .build();
                self.view.update(COMPONENT_INPUT_PORT, props)
            }
        }
    }

    fn on_unhandled_submit(&mut self) -> Option<(String, Msg)> {
        // Validate fields
        match self.collect_host_params() {
            Err(err) => {
                // mount error
                self.mount_error(err);
            }
            Ok(params) => {
                self.save_recent();
                // Set file transfer params to context
                self.context_mut().set_ftparams(params);
                // Set exit reason
                self.exit_reason = Some(super::ExitReason::Connect);
            }
        }
        // Return None
        None
    }
}
