package leigh.continuity.line;

import leigh.mecha.lang.DangerousRunnable;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * This class implements the skeleton for a line handler. The remote is expected to send lines of text,
 * which are processed, with results returned to the caller. An asynchronous output mechanism is provided
 * so that text can be sent to the caller without a correlating command having been received.
 *
 * @author Alex Leigh
 */
public class BasicLineHandler implements DangerousRunnable {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(BasicLineHandler.class);
    private final Socket s;
    private BufferedOutputStream os;
    private String prompt = ">";
    private LineProcessor lineProcessor;

    public BasicLineHandler(Socket s) {
        this.s = s;
    }

    public void setLineProcessor(LineProcessor lineProcessor) {
        this.lineProcessor = lineProcessor;
    }

    /**
     * Write a string to the client. We check for null & blank here, so it's OK to call this with sketchy arguments.
     */
    public void write(String s, boolean doPrompt) throws IOException {
        if (s != null && !s.isBlank()) {
            os.write(s.getBytes());
            os.write("\r\n".getBytes());
        }
        if (doPrompt) {
            os.write("\r\n".getBytes());
            os.write(prompt.getBytes());
            os.write(" ".getBytes());
        }
        os.flush();
    }

    @Override
    public void runDangerously() throws Exception {
        os = new BufferedOutputStream(s.getOutputStream());

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()))) {
            while (true) {
                String input = reader.readLine();

                if (input == null) {
                    logger.warn("Shutting down. [s: {}]", s);
                    return;
                }

                String response = lineProcessor.handleLine(input);
                if (response == null) {
                    response = "";
                }
                write(response, true);
            }
        }
    }

    public void close() {
        try {
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            runDangerously();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
