use super::{Repl, InterpreterDirectiveOutput};
use crate::repl::directive_actions::DirectiveAction;
use colored::*;

/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
/// Terminal, it will use the `DirectiveAction` found there to find an appropriate function to execute,
/// and then execute it with any remaining arguments
#[derive(Clone)]
pub enum CommandTree {
  Terminal {
    name: String,
    children: Vec<CommandTree>,
    help_msg: Option<String>,
    action: DirectiveAction,
  },
  NonTerminal {
    name: String,
    children: Vec<CommandTree>,
    help_msg: Option<String>,
    action: DirectiveAction,
  },
  Top(Vec<CommandTree>),
}

impl CommandTree {
  pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> CommandTree {
    CommandTree::NonTerminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children: vec![], action: DirectiveAction::Null }
  }

  pub fn terminal(s: &str, help: Option<&str>, children: Vec<CommandTree>, action: DirectiveAction) -> CommandTree {
    CommandTree::Terminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children, action}
  }

  pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
    CommandTree::NonTerminal {
      name: s.to_string(),
      help_msg: help.map(|x| x.to_string()),
      children,
      action: DirectiveAction::Null
    }
  }

  pub fn get_cmd(&self) -> &str {
    match self {
      CommandTree::Terminal { name, .. } => name.as_str(),
      CommandTree::NonTerminal {name, ..} => name.as_str(),
      CommandTree::Top(_) => "",
    }
  }
  pub fn get_help(&self) -> &str {
    match self {
      CommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
      CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
      CommandTree::Top(_) => ""
    }
  }
  pub fn get_children(&self) -> &Vec<CommandTree> {
    use CommandTree::*;
    match self {
      Terminal { children, .. } |
      NonTerminal { children, .. } |
      Top(children) => children
    }
  }
  pub fn get_subcommands(&self) -> Vec<&str> {
    self.get_children().iter().map(|x| x.get_cmd()).collect()
  }

  pub fn perform(&self, repl: &mut Repl, arguments: &Vec<&str>) -> InterpreterDirectiveOutput {
    let mut dir_pointer: &CommandTree = self;
    let mut idx = 0;

    let res: Result<(DirectiveAction, usize), String> = loop {
      match dir_pointer {
        CommandTree::Top(subcommands) | CommandTree::NonTerminal { children: subcommands, .. } => {
          let next_command = match arguments.get(idx) {
            Some(cmd) => cmd,
            None => break Err(format!("Command requires arguments"))
          };
          idx += 1;
          match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) {
            Some(command_tree) => {
              dir_pointer = command_tree;
            },
            None => break Err(format!("Command {} not found", next_command))
          };
        },
        CommandTree::Terminal { action, .. } => {
          break Ok((action.clone(), idx));
        },
      }
    };

    match res {
      Ok((action, idx)) => action.perform(repl, &arguments[idx..]),
      Err(err) => Some(err.red().to_string())
    }
  }
}