package riomhaire.lti.core.business;

import org.springframework.web.servlet.ModelAndView;
import riomhaire.lti.core.model.LTIMessage;
import riomhaire.lti.core.model.interfaces.ApplicationRegistry;
import riomhaire.lti.core.model.interfaces.Command;
import riomhaire.lti.core.model.interfaces.CommandDispatcher;

import java.util.HashMap;

/**
 * This is a simple implementation of the classic command matcher pattern where
 * we map a message to one command handler. This allows us to decouple the handler of the message from the sender.
 * We could extend this where necessary
 */
public class LTIMessageDispatcher implements CommandDispatcher<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> {
    // This stores the mapping of command to command handler
    private final HashMap<String, Command<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage>> commands = new HashMap<>();

    // This is the action that is (optionally) executed if no match is made
    private Command<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> defaultCommand;


    /**
     * This adds a mapping of a message type to the command that can handle that type
     * At the moment its a 1-1 mapping
     * @param commandName  name of command ... we allow this because we want a command to potentially respond to more than one message type
     * @param command   implementor
     * @return  Command dispatcher so we can have some form of fluent style construction.
     */
    @Override
    public CommandDispatcher<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> addCommand(String commandName, Command<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> command) {
        commands.put(commandName, command);
        return this;
    }


    /**
     * This is called by an invoker with
     *
     * @param registry  to execute within
     * @param message  the message to process.. the implementer of this interface should have a strategy of mapping message to command
     * @return response from executing the command
     */
    @Override
    public ModelAndView dispatch(ApplicationRegistry<ModelAndView,LTIMessage> registry, LTIMessage message) {
        var mav = new ModelAndView("nop");
        // If we have a handler for this command execute it
        if (message != null && commands.containsKey(message.getMessageType())) {
            mav = commands.get(message.getMessageType()).execute(registry, message);
        } else if (defaultCommand != null) {
            mav = defaultCommand.execute(registry, message);
        }
        return mav;
    }

    /**
     * This implementation allows a default action to be taken  if no mapping is found. This is the setter for that
     *
     * @param defaultCommand  what command/action to execute if no mapping is found - something to generate an error
     */
    public void setDefaultCommand(Command<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> defaultCommand) {
        this.defaultCommand = defaultCommand;
    }

}
