/**
 * Copyright (c) 2013 by EA (Electronic Arts), 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;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.python.pydev.core.log.Log;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_ui.utils.RunInUiThread;

/**
 * Handles any exception raised while evaluating the conditional breakpoint
 *
 * @author hussain.bohra
 * @author Fabio Zadrozny
 *
 */
public class PyConditionalBreakPointManager {

    private static final String DELIMETER = "\t";
    private static final int ERROR_CODE = 1;
    private static final String PYTHON_TRACEBACK = "Traceback (most recent call last):";
    private static final String SHELL_TEXT = "Conditional Breakpoint";
    private static final String PID = "Error";
    private static final String ERROR_MESSAGE = "An exception has occurred when evaluating a conditional breakpoint:\n\n";
    private static final String TITLE = "Error in executing conditional breakpoint";

    private static PyConditionalBreakPointManager pyConditionalBreakPointManager;

    /**
     * Singleton: private constructor.
     */
    private PyConditionalBreakPointManager() {

    }

    public static synchronized PyConditionalBreakPointManager getInstance() {
        if (pyConditionalBreakPointManager == null) {
            pyConditionalBreakPointManager = new PyConditionalBreakPointManager();
        }
        return pyConditionalBreakPointManager;
    }

    /**
     * Represents Python stacktrace
     *
     * @author hussain.bohra
     *
     */
    @SuppressWarnings("unused")
    static class ExceptionStackTrace {
        private AbstractDebugTarget target;
        private String filename;
        private int lineNo;
        private String methodName;
        private String methodObj;

        public ExceptionStackTrace(AbstractDebugTarget target, String filename, int lineNo,
                String methodName, String methodObj) {
            this.target = target;
            this.filename = filename;
            this.lineNo = lineNo;
            this.methodName = methodName;
            this.methodObj = methodObj;
        }

        @Override
        public String toString() {
            return StringUtils.join("", "tFile ", filename, "\n line ", lineNo, ", in", methodName, ", ", methodObj);
        }
    }

    /**
     * Display an error dialog and traceback raised while evaluating the
     * conditional breakpoint.
     *
     * @param payload
     *      would contain exception_type + "\t" + stacktrace_xml
     */
    public void handleBreakpointException(final AbstractDebugTarget target,
            final String payload) {
        if (payload.indexOf(DELIMETER) > 0) {
            // exceptionDetailList = ["exceptionType", "<frame>traceback</frame>"]
            final String[] exceptionDetailList = payload.split(DELIMETER);

            RunInUiThread.async(new Runnable() {
                @Override
                public void run() {
                    // adding exception detail with error message
                    String errorMessage = ERROR_MESSAGE + "\n" + exceptionDetailList[0];
                    List<ExceptionStackTrace> exceptionStackTraceList = new ArrayList<ExceptionStackTrace>();
                    Shell shell = new Shell(Display.getCurrent());
                    shell.setText(SHELL_TEXT);
                    MultiStatus multiStatusInfo = new MultiStatus(PID, ERROR_CODE, errorMessage, null);

                    multiStatusInfo.add(new Status(IStatus.ERROR, PID, ERROR_CODE, PYTHON_TRACEBACK, null));
                    try {
                        // Parse traceback xml
                        exceptionStackTraceList = XMLUtils.getExceptionStackTrace(target, exceptionDetailList[1]);
                    } catch (Exception e) {
                        Log.log(e);
                    }
                    if (exceptionStackTraceList != null) {
                        // Adding traceback details to multiStatusInfo
                        for (ExceptionStackTrace exceptionStackTrace : exceptionStackTraceList) {
                            multiStatusInfo.add(new Status(IStatus.ERROR, PID, ERROR_CODE, exceptionStackTrace
                                    .toString(),
                                    null));
                        }
                    }
                    ErrorDialog.openError(shell, TITLE, null, multiStatusInfo);
                }
            });
        }
    }

}
