/*
 * This file is part of BlueMap, licensed under the MIT License (MIT).
 *
 * Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
 * Copyright (c) contributors
 *
 * 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.
 */
package de.bluecolored.bluemap.sponge;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.spongepowered.api.command.CommandCallable;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.tree.CommandNode;

import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.plugin.commands.Commands;

public class SpongeCommands {

	private CommandDispatcher<CommandSource> dispatcher;
	
	public SpongeCommands(final Plugin plugin) {
		this.dispatcher = new CommandDispatcher<>();
		
		// register commands
		new Commands<>(plugin, dispatcher, bukkitSender -> new SpongeCommandSource(plugin, bukkitSender));
	}
	
	public Collection<SpongeCommandProxy> getRootCommands(){
		Collection<SpongeCommandProxy> rootCommands = new ArrayList<>();
		
		for (CommandNode<CommandSource> node : this.dispatcher.getRoot().getChildren()) {
			rootCommands.add(new SpongeCommandProxy(node.getName()));
		}
		
		return rootCommands;
	}
	
	public class SpongeCommandProxy implements CommandCallable {

		private String label;
		
		protected SpongeCommandProxy(String label) {
			this.label = label;
		}
		
		@Override
		public CommandResult process(CommandSource source, String arguments) throws CommandException {
			String command = label;
			if (!arguments.isEmpty()) {
				command += " " + arguments;
			}
			
			try {
				return CommandResult.successCount(dispatcher.execute(command, source));
			} catch (CommandSyntaxException ex) {
				source.sendMessage(Text.of(TextColors.RED, ex.getRawMessage().getString()));
				
				String context = ex.getContext();
				if (context != null) source.sendMessage(Text.of(TextColors.GRAY, context));
				
				return CommandResult.empty();
			}
		}

		@Override
		public List<String> getSuggestions(CommandSource source, String arguments, Location<World> targetPosition) throws CommandException {
			String command = label;
			if (!arguments.isEmpty()) {
				command += " " + arguments;
			}

			List<String> completions = new ArrayList<>();

			try {
				Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(command, source)).get(100, TimeUnit.MILLISECONDS);
				for (Suggestion suggestion : suggestions.getList()) {
					String text = suggestion.getText();
	
					if (text.indexOf(' ') == -1) {
						completions.add(text);
					}
				}
			} catch (InterruptedException | ExecutionException | TimeoutException ignore) {}

			completions.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
			return completions;
		}

		@Override
		public boolean testPermission(CommandSource source) {
			return true;
		}

		@Override
		public Optional<Text> getShortDescription(CommandSource source) {
			return Optional.empty();
		}

		@Override
		public Optional<Text> getHelp(CommandSource source) {
			return Optional.empty();
		}

		@Override
		public Text getUsage(CommandSource source) {
			CommandNode<CommandSource> node = dispatcher.getRoot().getChild(label);
			if (node == null) return Text.of("/" + label);
			
			List<Text> lines = new ArrayList<>();
			for (String usageString : dispatcher.getSmartUsage(node, source).values()) {
				lines.add(Text.of(TextColors.WHITE, "/" + label + " ", TextColors.GRAY, usageString));
			}
			
			return Text.joinWith(Text.NEW_LINE, lines);
		}

		public String getLabel() {
			return label;
		}
		
	}
	
}