use std::collections::HashMap;
use crate::
{
	// Internal code
	character,
	api,
	tokenise,

	// Libraries
	mpsc::Receiver,
	Arc,
	Mutex,
	info,
	debug,
	warn,
	type_name,
};

mod strings;
mod character_parse;

// Parse the tokens in a file
// Returns success or an error string
pub fn token_parse(
	tokens: &Vec<tokenise::Token>,
	characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
	data_to_send: &Arc<Mutex<api::DataToSend>>,
	rx: &Receiver<(bool,usize,String)>,
) -> Result<(),String>
{
	info!("DSL parsing begun");
	let mut index: usize = 0;
	if rx.recv().is_err()
	{
		warn!("Some issue with api");
		// TODO eh?
	}
	info!("Client has connected");
	// Run an infinite loop
	'parse_loop: loop
	{
		// Get the next token
		let token = match tokens.get(index) {
			Some(tokenise::Token::String(s)) => s.clone(),
			Some(_) => return Err("Unexpected token".to_string()),
			None => return Err("File unexpectedly reached termination point".to_string()),
		};
		debug!("{index}: {token}");
		// The instructions are related to characters
		if token.starts_with('@')
		{
			let character_name: String = token.chars().skip(1).collect();
			debug!("Doing something with a character: {character_name}");
			// The index is incremented to after the character's instructions
			index = match character_parse::character_parse(index+1, tokens, character_name, characters, data_to_send)
			{
				Ok(increment) => increment,
				Err((err,increment)) =>
				{
					warn!("{err}");
					increment
				},
			};
		}
		// Miscelleneous instructions
		else
		{
			match token.to_lowercase().as_str()
			{
				"end" =>
				{
					info!("END command, exiting");
					return Ok(()) // quit successfully
				},
				"choice" =>
				{
					let mut choices: Vec<String> = Vec::new();
					let mut choice_indeces: Vec<usize> = Vec::new();
					index += 1;
					choices.push
					(match tokens.get(index) {
						Some(tokenise::Token::String(s)) => s.clone(),
						Some(_) => return Err("Unexpected token".to_string()),
						None => return Err("File unexpectedly reached termination point".to_string()),
					});
					choice_indeces.push(index+1);
					index += 2;
					let next_token = match tokens.get(index) {
						Some(tokenise::Token::String(s)) => s.clone(),
						Some(_) => return Err("Unexpected token".to_string()),
						None => return Err("File unexpectedly reached termination point".to_string()),
					};
					while next_token == "or"
					{
						index += 1
						choices.push
						(match tokens.get(index) {
							Some(tokenise::Token::String(s)) => s.clone(),
							Some(_) => return Err("Unexpected token".to_string()),
							None => return Err("File unexpectedly reached termination point".to_string()),
						});
					}
				},
				/*
				"choice" =>
				{
					let (_,jump_points) = match choice_parse(index+1, tokens, data_to_send)
					{
						Ok((increment,jump_point)) => (increment,jump_point),
						Err(error) => return Err(error),
					};
					if rx.recv().is_err() { warn!("Error sending choices to client"); }
					let (_, choice, _) = match rx.recv()
					{
						Ok((_,choice,_)) => (None::<bool>, choice, None::<String>),
						Err(err) =>
						{
							warn!("Error receiving choice from client, defaulting to choice 0 {err}");
							(None::<bool>, 0, None::<String>)
						}
					};
					index = jump_points[choice];
					info!("CHOICE command with {} choices",jump_points.len());
					debug!("{jump_points:?} {choice} {index}");
					continue 'parse_loop
				},
				*/
				"or" =>
				{
					info!("OR command, jumping over");
					index += 2;
					continue
				},
				_ => 
				{
					warn!("Invalid command: {token}");
				}
			}
		}
		if rx.recv().is_err()
		{
			warn!("Some issue with api");
		}
	}
}

// Parse the options in a choice clause and returns the idexes of the code blocks
fn choice_parse
(
	index: usize,
	tokens: &[&str],
	data_to_send: &Arc<Mutex<api::DataToSend>>,
) -> Result<(usize, Vec<usize>), String>
{
	let mut sum_index: usize = index;
	let mut choices: Vec<String> = Vec::new();
	let mut choice_indeces: Vec<usize> = Vec::new();
	// Ensure the index is valid (the index is not beyond the vector)
	// Get the initial choice
	let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index..])
		.ok_or_else(|| "No choice string".to_string())?;
	sum_index += counter;
	choices.push(choice_string);
	choice_indeces.push(sum_index+1);
	sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
		.ok_or_else(|| "No closing brace".to_string())? + 1;
	// Find all the alternate choices labelled with OR
	// Fill out the choices vector with all the choice strings
	while tokens[sum_index].to_lowercase() == "or"
	{
		let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index+1..])
			.ok_or_else(|| "No choice string".to_string())?;
		sum_index += counter;
		choices.push(choice_string);
		choice_indeces.push(sum_index+2);
		sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
			.ok_or_else(|| "No closing brace".to_string())? + 1;
	}
	debug!("{choices:?}");
	// Send the choices to the Client via the API
	api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices);
	// Return the choice indeces
	Ok((sum_index + 1, choice_indeces))
}
