package lc.mecha.cli.cmd;

import lc.mecha.cli.CommandRoute;
import lc.mecha.cli.CommandRouter;
import lc.mecha.cli.line.BasicLineHandler;
import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import lc.mecha.util.StringAccumulator;

import java.io.IOException;
import java.util.Map;

/**
 * Reference implementation of a generic line processor.
 *
 * @author Alex Leigh
 */
public class BasicCommandProcessor implements CommandProcessor {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(BasicCommandProcessor.class);
    private final CommandRouter<CommandHandler> router = new CommandRouter<>();
    private String unknownResponse = "Unknown entry.";

    public String getUnknownResponse() {
        return unknownResponse;
    }

    public void setUnknownResponse(String unknownResponse) {
        this.unknownResponse = unknownResponse;
    }

    public String clean(String s) {
        return s.trim().toUpperCase();
    }

    public CommandRouter<CommandHandler> getHandlers() {
        return router;
    }

    public void addCommandHandler(String cmd, CommandHandler handler) {
        router.putCommand(clean(cmd), handler);
    }

    @Override
    public void handleCommand(BasicLineHandler lineHandler, String[] cmd) throws Exception {
        CommandRoute<CommandHandler> route = router.route(clean(cmd[0]));
        if (route == null) {
            write(lineHandler, getUnknownResponse());
            return;
        }
        Map<String, CommandHandler> handlers = route.getCommands();
        logger.info("Command: {}", (Object) cmd);

        if (handlers == null) {
            write(lineHandler, getUnknownResponse());
            return;
        }

        CommandHandler handler = null;

        if (handlers.size() > 1) {
            write(lineHandler, buildAmbigResponse(route));
            return;
        }

        if (handlers.size() > 0) handler = (CommandHandler) handlers.values().toArray()[0];

        if (handler == null) {
            // TODO We can think of a better generic response than this
            write(lineHandler, getUnknownResponse());
        } else {
            try {
                handler.handle(lineHandler, cmd);
            } catch (Exception e) {
                write(lineHandler, "Error: " + e);
            }
        }
    }

    public String buildAmbigResponse(CommandRoute<CommandHandler> route) throws IOException {
        StringAccumulator sa = new StringAccumulator(",");
        for (String name : route.getCommands().keySet()) {
            sa.append(name);
        }

        return "Ambiguous command. Matches: " + sa.toString();
    }

    public void write(BasicLineHandler lineHandler, String str) throws Exception {
        logger.info("Want to write: {}", str);
        lineHandler.write(str, false);
    }
}
