package leigh.continuity.cmd;

import leigh.continuity.line.BasicLineHandler;
import leigh.mecha.cli.CommandRoute;
import leigh.mecha.cli.CommandRouter;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.StringAccumulator;
import org.apache.commons.lang.WordUtils;

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 int consoleWidth = -1;
    private String unknownResponse = "Unknown entry.";
    private final BasicLineHandler lineHandler;

    public BasicCommandProcessor(BasicLineHandler lineHandler) {
        this.lineHandler = lineHandler;
    }

    public String getUnknownResponse() {
        return unknownResponse;
    }

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

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

    /**
     * Set the width of the remote console. Messages wider than this size will be wrapped at word boundaries.
     */
    public void setConsoleWidth(int consoleWidth) {
        this.consoleWidth = consoleWidth;
    }

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

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

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

        if (handlers == null) return getUnknownResponse();

        CommandHandler handler = null;

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

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

        String response;

        if (handler == null) {
            // TODO We can think of a better generic response than this
            response = getUnknownResponse();
        } else {
            response = handler.handle(cmd);
        }

        if (consoleWidth > 0) {
            // Note - this does not take control characters like colors into account

            // FIXME: This does not take \r\n into account
            response = WordUtils.wrap(response, consoleWidth);
        }

        return response;
    }

    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(String str) throws IOException {
        logger.info("Want to write: {}", this.lineHandler);
        lineHandler.write(str, false);
    }

    public void close() {
        lineHandler.close();
    }
}
