/**
 * Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package org.python.pydev.debug.model.remote;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.python.pydev.core.log.Log;

/**
 * Writer writes debugger commands to the network. Use postCommand to put new
 * ones in queue.
 */
public class DebuggerWriter implements Runnable {

    /**
     * connection socket
     */
    private Socket socket;

    /**
     * a list of RemoteDebuggerCommands
     */
    private List<AbstractDebuggerCommand> cmdQueue = new ArrayList<AbstractDebuggerCommand>();

    private OutputStreamWriter out;

    /**
     * Volatile, as multiple threads may ask it to be 'done'
     */
    private volatile boolean done = false;

    /**
     * Lock object for sleeping.
     */
    private Object lock = new Object();

    public DebuggerWriter(Socket s) throws IOException {
        socket = s;
        out = new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8);
    }

    /**
     * Add command for processing
     */
    public void postCommand(AbstractDebuggerCommand cmd) {
        synchronized (cmdQueue) {
            cmdQueue.add(cmd);
        }
    }

    public void done() {
        this.done = true;
    }

    /**
     * Loops and writes commands to the output
     */
    @Override
    public void run() {
        while (!done) {
            AbstractDebuggerCommand cmd = null;
            synchronized (cmdQueue) {
                if (cmdQueue.size() > 0) {
                    cmd = cmdQueue.remove(0);
                }
            }
            try {
                if (cmd != null) {
                    String outgoing;
                    try {
                        outgoing = cmd.getOutgoing();
                        if (outgoing == null) {
                            continue;
                        }
                    } catch (Throwable e) {
                        Log.log(e);
                        continue;
                    }

                    cmd.aboutToSend();
                    out.write(outgoing);
                    out.write("\n");
                    out.flush();
                }
                synchronized (lock) {
                    Thread.sleep(100);
                }
            } catch (InterruptedException | IOException e) {
                done = true;
            } catch (Throwable e1) {
                Log.log(e1); //Unexpected error (but not done).
            }
            if ((socket == null) || !socket.isConnected()) {
                done = true;
            }
        }
    }
}
