package leigh.minecraft.leighco;

import leigh.eo.EO;
import leigh.eo.EOLoop;
import leigh.eo.json.JsonInput;
import leigh.esp.sdk.EOResponseConsumer;
import leigh.esp.sdk.ESPClient;
import leigh.esp.sdk.ResponseConsumer;
import leigh.esp.sdk.link23.schema.HeartbeatElementDAO;
import leigh.mecha.cli.CommandFrame;
import leigh.mecha.cli.CommandHandler;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.UniversalJob;
import leigh.minecraft.leighco.cmd.LandCommand;
import leigh.minecraft.leighco.cmd.PosCommand;
import leigh.minecraft.link.MinecraftLinkConstants;
import leigh.minecraft.link.sdk.schema.v1.LandMemberAddRequestElementDAO;
import leigh.minecraft.link.sdk.schema.v1.LandMemberRemoveRequestElementDAO;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.UUID;

public class LeighCoApp implements Runnable {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(LeighCoApp.class);
    ESPClient esp = new ESPClient("https://leigh-co.com");
    CommandFrame<LeighCoState> frame = new CommandFrame<>();

    public LeighCoApp() {
        CommandFrame<LeighCoState> lcCmd = new CommandFrame<>();
        frame.putCommand("lc", lcCmd);

        lcCmd.putCommand("hello", new CommandHandler<LeighCoState>() {
            public void handle(String[] cmd, LeighCoState state) throws Exception {
                state.sendMessage("Hello " + state.getPlayerName() + "!");
            }
        });

        lcCmd.putCommand("land", new LandCommand());
        lcCmd.putCommand("pos", new PosCommand());
    }

    public static void main(String args[]) {
        UniversalJob.banner(logger, "LeighCoApp mk1");
        new LeighCoApp().run();
    }

    @Override
    public void run() {
        final LeighCoApp app = this;
        while (true) {
            try {
                logger.info("Attempting to call cmd/read on remote link...");
                esp.request("/minecraft/neweden/link/v1/cmd/read?_enc=ndj", new EO(), new ResponseConsumer() {
                    @Override
                    public void drainResponse(InputStream is) throws Exception {
                        JsonInput in = new JsonInput(is);
                        while (true) {
                            EO obj = in.readEo();

                            logger.info("Read command: {}", obj);

                            if(HeartbeatElementDAO.assertType(obj)) {
                                logger.info("Received station-keeping message.");
                                continue;
                            }

                            ArrayList<String> argList = new ArrayList<>();
                            // We want to build a C-style arguments stack
                            argList.add(obj.getValueString(MinecraftLinkConstants.CMD_KEY));
                            EOLoop args = obj.getValueLoop(MinecraftLinkConstants.ARGS_KEY);
                            for (EO arg : args) {
                                argList.add(arg.getValueString(MinecraftLinkConstants.ARG_KEY));
                            }
                            logger.info("Arguments found: {}", argList);

                            Location location = new Location(
                                    obj.getValueDouble(MinecraftLinkConstants.ARG_POS_X),
                                    obj.getValueDouble(MinecraftLinkConstants.ARG_POS_Y),
                                    obj.getValueDouble(MinecraftLinkConstants.ARG_POS_Z),
                                    obj.getValueFloat(MinecraftLinkConstants.ARG_POS_PITCH),
                                    obj.getValueFloat(MinecraftLinkConstants.ARG_POS_YAW));

                            frame.handle(argList.toArray(new String[0]),
                                    new LeighCoState(obj.getValueString(MinecraftLinkConstants.SRC_KEY), app,
                                            location, obj.getValueUUID("playerId")));

                            logger.info("Read response object: {}", obj);
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void sendMessage(String destination, String msg) throws Exception {
        EO request = new EO();
        request.setValue(MinecraftLinkConstants.DST_KEY, destination);
        request.setValue(MinecraftLinkConstants.MSG_KEY, msg);
        logger.info("Sending message to player... {}", request);
        esp.request("/minecraft/neweden/link/v1/msg/write", request);
    }

    public void sendMessageById(UUID destination, String msg) throws Exception {
        EO request = new EO();
        request.setValue("dstId", destination);
        request.setValue(MinecraftLinkConstants.MSG_KEY, msg);
        logger.info("Sending message to player... {}", request);
        esp.request("/minecraft/neweden/link/v1/msg/write", request);
    }

    public void landCreate(Lot lot, String player, int price) throws Exception {
        EO request = new EO();
        request.setValue(MinecraftLinkConstants.SRC_KEY, player);
        request.setValue(MinecraftLinkConstants.POS_X1, lot.getX1());
        request.setValue(MinecraftLinkConstants.POS_X2, lot.getX2());
        request.setValue(MinecraftLinkConstants.POS_Z1, lot.getZ1());
        request.setValue(MinecraftLinkConstants.POS_Z2, lot.getZ2());
        request.setValue(MinecraftLinkConstants.PRICE, price);

        logger.info("Sending message to player... {}", request);
        esp.apiRequest("/minecraft/neweden/link/v1/land/create", request, new EOResponseConsumer() {
            @Override
            public void handle(EO obj) throws Exception {
                logger.info("Read back: {}", obj);
                String msg = obj.getValueString(MinecraftLinkConstants.MSG_KEY);
                sendMessage(player, msg);
            }
        });
    }

    public void landMemberAdd(String lotName, UUID ownerName, String playerName) throws Exception {
        EO request = LandMemberAddRequestElementDAO.create();
        LandMemberAddRequestElementDAO.setLot(request, lotName);
        LandMemberAddRequestElementDAO.setSrc(request, ownerName);
        LandMemberAddRequestElementDAO.setPlayer(request, playerName);

        esp.apiRequest("/minecraft/neweden/link/v1/land/member/add", request, new EOResponseConsumer() {
            @Override
            public void handle(EO obj) throws Exception {
                logger.info("Read back: {}", obj);
                String msg = obj.getValueString(MinecraftLinkConstants.MSG_KEY);
                sendMessageById(ownerName, msg);
            }
        });
    }

    public void landMemberRemove(String lotName, UUID ownerName, String playerName) throws Exception {
        EO request = LandMemberRemoveRequestElementDAO.create();
        LandMemberRemoveRequestElementDAO.setLot(request, lotName);
        LandMemberRemoveRequestElementDAO.setSrc(request, ownerName);
        LandMemberRemoveRequestElementDAO.setPlayer(request, playerName);

        esp.apiRequest("/minecraft/neweden/link/v1/land/member/remove", request, new EOResponseConsumer() {
            @Override
            public void handle(EO obj) throws Exception {
                logger.info("Read back: {}", obj);
                String msg = obj.getValueString(MinecraftLinkConstants.MSG_KEY);
                sendMessageById(ownerName, msg);
            }
        });
    }
}
