/******************************************************************************
* Copyright (C) 2012-2013  Hussain Bohra and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Hussain Bohra <hussain.bohra@tavant.com> - initial API and implementation
*     Fabio Zadrozny <fabiofz@gmail.com>       - ongoing maintenance
******************************************************************************/
package org.python.pydev.debug.newconsole;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.model.AbstractDebugTarget;
import org.python.pydev.debug.model.PyStackFrame;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.model.remote.EvaluateConsoleExpressionCommand;
import org.python.pydev.debug.model.remote.ICommandResponseListener;
import org.python.pydev.shared_core.string.StringUtils;

/**
 * Class to exectute console command in the debugging context
 *
 * @author hussain.bohra
 * @author Fabio Zadrozny
 */
public class EvaluateDebugConsoleExpression implements ICommandResponseListener {

    String EMPTY = StringUtils.EMPTY;
    private String payload;
    private final PyStackFrame frame;

    public EvaluateDebugConsoleExpression(PyStackFrame frame) {
        Assert.isNotNull(frame);
        this.frame = frame;
    }

    /**
     * This method will get called from AbstractDebugTarget when
     * output arrives for the posted command
     */
    @Override
    public void commandComplete(AbstractDebuggerCommand cmd) {
        try {
            this.payload = ((EvaluateConsoleExpressionCommand) cmd).getResponse();
        } catch (CoreException e) {
            this.payload = e.getMessage();
        }
    }

    /**
     * Execute the line in selected frame context
     *
     * @param consoleId
     * @param command
     */
    public void executeCommand(String command, boolean bufferedOutput) {
        AbstractDebugTarget target = frame.getTarget();
        String locator = getLocator(frame.getThreadId(), frame.getId(), bufferedOutput ? "EVALUATE"
                : "EVALUATE_UNBUFFERED", command);
        AbstractDebuggerCommand cmd = new EvaluateConsoleExpressionCommand(target, locator,
                new ICommandResponseListener() {

                    @Override
                    public void commandComplete(AbstractDebuggerCommand cmd) {
                        frame.forceGetNewVariables();
                        EvaluateDebugConsoleExpression.this.commandComplete(cmd);
                    }
                });
        target.postCommand(cmd);
    }

    /**
     * Post the completions command
     *
     * @param consoleId
     * @param actTok
     * @param offset
     */
    public String getCompletions(String actTok, int offset) {
        AbstractDebugTarget target = frame.getTarget();
        String locator = getLocator(frame.getThreadId(), frame.getId(), "GET_COMPLETIONS", actTok);
        AbstractDebuggerCommand cmd = new EvaluateConsoleExpressionCommand(target, locator, this);
        target.postCommand(cmd);
        return waitForCommand();
    }

    /**
     * Keeps in a loop for 3 seconds or until the completions are found. If no
     * completions are found in that time, returns an empty array.
     */
    public String waitForCommand() {
        int timeout = PydevConsoleConstants.CONSOLE_TIMEOUT; // wait up to 3 seconds
        while (--timeout > 0 && payload == null) {
            try {
                Thread.sleep(10); // 10 millis
            } catch (InterruptedException e) {
                // ignore
            }
        }

        String temp = this.payload;
        this.payload = null;
        if (temp == null) {
            Log.logInfo("Timeout for waiting for debug completions elapsed (3 seconds).");
            return EMPTY;
        }
        return temp;
    }

    /**
     * join and return all locators with '\t'
     *
     * @param locators
     * @return
     */
    private String getLocator(String... locators) {
        return StringUtils.join("\t", locators);
    }

    /**
     * This class represent the console message to be displayed in the debug console.
     *
     * @author hussain.bohra
     *
     */
    public static class PydevDebugConsoleMessage {

        private boolean more;
        private StringBuilder outputMessage = new StringBuilder();
        private StringBuilder errorMessage = new StringBuilder();

        public boolean isMore() {
            return more;
        }

        public void setMore(boolean more) {
            this.more = more;
        }

        public void appendMessage(String output, boolean isError) {
            if (!isError) {
                outputMessage.append(output);
                outputMessage.append("\n");
            } else {
                errorMessage.append(output);
                errorMessage.append("\n");
            }
        }

        public StringBuilder getOutputMessage() {
            return outputMessage;
        }

        public StringBuilder getErrorMessage() {
            return errorMessage;
        }
    }
}
