//! utility functions to help handle some internals which require a path

use {
    super::*,
    crate::{
        app::*,
        browser::BrowserState,
        command::TriggerType,
        display::Screen,
        path::{
            self,
            PathAnchor,
        },
        tree::Tree,
    },
    std::path::{
        Path,
        PathBuf,
    },
};

/// general implementation for verbs on the form ":some_internal path"
pub fn determine_path(
    internal_exec: &InternalExecution,
    input_invocation: Option<&VerbInvocation>,
    trigger_type: TriggerType,
    tree: &Tree,
    app_state: &AppState,
    cc: &CmdContext,
) -> Option<PathBuf> {
    info!(
        "internal_path::determine_path internal_exec={:?} input_invocation={:?} trygger_type={:?}",
        internal_exec, input_invocation, trigger_type,
    );
    let input_arg = input_invocation
        .as_ref()
        .and_then(|invocation| invocation.args.as_ref());
    match trigger_type {
        TriggerType::Input(verb) => {
            let path = path_from_input(
                verb,
                internal_exec,
                &tree.selected_line().path,
                input_arg,
                app_state,
                cc.app.con,
            );
            Some(path)
        }
        _ => {
            // the :select internal was triggered by a key
            if let Some(arg) = &internal_exec.arg {
                // the internal_execution specifies the path to use
                // (it may come from a configured verb whose execution is
                //  `:select some/path`).
                // The given path may be relative hence the need for the
                // state's selection
                let path =
                    path::path_from(&tree.selected_line().path, PathAnchor::Unspecified, arg);
                Some(path)
            } else {
                // there's nothing really to do here
                None
            }
        }
    }
}

/// Compute the path to go to in case of the internal being triggered from
/// the input.
///
/// This path depends on the verb (which may hardcore the path or have a
/// pattern), from the selection,
fn path_from_input(
    verb: &Verb,
    internal_exec: &InternalExecution,
    base_path: &Path, // either the selected path or the root path
    input_arg: Option<&String>,
    app_state: &AppState,
    con: &AppContext,
) -> PathBuf {
    match (input_arg, internal_exec.arg.as_ref()) {
        (Some(input_arg), Some(verb_arg)) => {
            // The verb probably defines some pattern which uses the input.
            // For example:
            // {
            //     invocation: "gotar {path}"
            //     execution: ":select {path}/target"
            // }
            // (or that input is useless)
            let path_builder = ExecutionBuilder::with_invocation(
                verb.invocation_parser.as_ref(),
                SelInfo::from_path(base_path),
                app_state,
                Some(input_arg),
            );
            path_builder.path(verb_arg, con)
        }
        (Some(input_arg), None) => {
            // the verb defines nothing
            // The :select internal execution was triggered from the
            // input (which must be a kind of alias for :select)
            // so we do exactly what the input asks for
            path::path_from(base_path, PathAnchor::Unspecified, input_arg)
        }
        (None, Some(verb_arg)) => {
            // the verb defines the path where to go..
            // the internal_execution specifies the path to use
            // (it may come from a configured verb whose execution is
            //  `:select some/path`).
            // The given path may be relative hence the need for the
            // state's selection
            // (we assume a check before ensured it doesn't need an input)
            path::path_from(base_path, PathAnchor::Unspecified, verb_arg)
        }
        (None, None) => {
            // This doesn't really make sense: we're selecting the currently
            // selected path
            base_path.to_path_buf()
        }
    }
}

pub fn on_path(
    path: PathBuf,
    tree: &mut Tree,
    screen: Screen,
    in_new_panel: bool,
) -> CmdResult {
    debug!("executing :select on path {:?}", &path);
    if in_new_panel {
        warn!("bang in :select isn't supported yet");
    }
    if tree.try_select_path(&path) {
        tree.make_selection_visible(BrowserState::page_height(screen));
    }
    CmdResult::Keep
}
