
/***************************************************************************
                          debug.c  -  main functions for debugger use
                             -------------------
    begin                : Fri Nov 2 2001
    copyright            : (C) 2001 by Keith Isdale
    email                : k_isdale@tpg.com.au
 ***************************************************************************/

#include "xsldbg.h"
#include "debug.h"
#include "debugXSL.h"           /* needed for debugXSLBreak function */
#include "breakpoint.h"
#include "callstack.h"
#include "files.h"
#include "options.h"

#include <libxslt/xsltutils.h>  /* need for breakpoint callback support */

/* setup debugger callbacks */
struct DebuggerCallbacks {
    xsltHandleDebuggerCallback debuggercallback;
    xsltAddCallCallback addcallback;
    xsltDropCallCallback dropcallback;
} debuggerDriver;


/* -----------------------------------------
              Private functions
 -------------------------------------------*/

/**
 * debugHandleDebugger:
 * @cur : source node being executed
 * @node : data node being processed
 * @templ : temlate that applies to node
 * @ctxt : the xslt transform context 
 * 
 * If either cur or node are a breakpoint, or xslDebugStatus in state 
 *   where debugging must occcur at this time, then transfer control
 *   to the debugXSLBreak function
 */
void debugHandleDebugger(xmlNodePtr cur, xmlNodePtr node,
                         xsltTemplatePtr templ,
                         xsltTransformContextPtr ctxt);

/* ------------------------------------- 
    End private functions
---------------------------------------*/


/*-----------------------------------------------------------
       Main debugger functions
-----------------------------------------------------------*/



/**
 * debugInit :
 *
 * Initialize debugger
 * Returns 1 on success,
 *         0 otherwise
 */
int
debugInit(void)
{
    int result;

    xslDebugStatus = DEBUG_NONE;
    result = breakPointInit();
    result = result && callStackInit();

    /* setup debugger callbacks */
    debuggerDriver.debuggercallback = debugHandleDebugger;
    debuggerDriver.addcallback = callStackAdd;
    debuggerDriver.dropcallback = callStackDrop;
    xsltSetDebuggerCallbacks(3, &debuggerDriver);
    return result;
}


/**
 * debugFree :
 *
 * Free up any memory taken by debugging
 */
void
debugFree(void)
{
    breakPointFree();
    callStackFree();
}


/** 
 * debugGotControl:
 * @reached: 1 if debugger has received control, -1 to read its value,
               0 to clear the flag
 *
 * Set flag that debugger has received control to value of @reached
 *
 * Returns 1 if any break point was reached previously,
 *         0 otherwise
 */
int
debugGotControl(int reached)
{
    static int hasReached;
    int result = hasReached;

    if (reached != -1)
        hasReached = reached;
    return result;
}


/**
 * debugHandleDebugger:
 * @cur : source node being executed
 * @node : data node being processed
 * @templ : temlate that applies to node
 * @ctxt : the xslt transform context 
 * 
 * If either cur or node are a breakpoint, or xslDebugStatus in state 
 *   where debugging must occcur at this time then transfer control
 *   to the debugXSLBreak function
 */
void
debugHandleDebugger(xmlNodePtr cur, xmlNodePtr node,
                    xsltTemplatePtr templ, xsltTransformContextPtr ctxt)
{

    if (!cur && !node) {
        xsldbgGenericErrorFunc(i18n("Error: XSLT source and XML data are empty. Cannot enter the debugger.\n"));
    } else {
	if (optionsGetIntOption(OPTIONS_GDB)){
	    int doValidation = 0;
	    switch(xsldbgValidateBreakpoints){
		case BREAKPOINTS_ARE_VALID:
		    if (!filesGetStylesheet() || !filesGetMainDoc()) {
			xsldbgValidateBreakpoints = BREAKPOINTS_NEED_VALIDATION;
			doValidation = 1;
		    }
			
		break;
	    
		case BREAKPOINTS_NEED_VALIDATION:
		    if (filesGetStylesheet() && filesGetMainDoc() && templ){ 
			xsldbgValidateBreakpoints = BREAKPOINTS_BEING_VALIDATED;
			doValidation = 1;
		    }
		break;
    
		case BREAKPOINTS_BEING_VALIDATED:
		    /*should never be in the state for any length of time */
#ifdef WITH_XSLDBG_DEBUG_BREAKPOINTS
		     xsltGenericError(xsltGenericErrorContext, 
			"Error: Unexpected breakpoint validation state %d", xsldbgValidateBreakpoints);	    
#endif
		break;
	    }
	    if (doValidation){
		    /* breakpoints will either be marked as orphaned or not as needed */
		    xsldbgValidateBreakpoints = BREAKPOINTS_BEING_VALIDATED;
		    walkBreakPoints((xmlHashScanner)
			    xslDbgShellValidateBreakPoint, ctxt);
		    if (filesGetStylesheet() && filesGetMainDoc() && templ){
			xsldbgValidateBreakpoints = BREAKPOINTS_ARE_VALID;
		    }else{
			xsldbgValidateBreakpoints = BREAKPOINTS_NEED_VALIDATION;
		    }
	    }
	}
        switch (xslDebugStatus) {

                /* A temparary stopping point */
            case DEBUG_WALK:
            case DEBUG_TRACE:
                /* only allow breakpoints at xml elements */
                if (xmlGetLineNo(cur) != -1)
                    debugXSLBreak(cur, node, templ, ctxt);
                break;

            case DEBUG_STOP:
                xslDebugStatus = DEBUG_CONT;
                /* only allow breakpoints at xml elements */
                if (xmlGetLineNo(cur) != -1)
                    debugXSLBreak(cur, node, templ, ctxt);
                break;

            case DEBUG_STEP:
                /* only allow breakpoints at xml elements */
                if (xmlGetLineNo(cur) != -1)
                    debugXSLBreak(cur, node, templ, ctxt);
                break;

            case DEBUG_CONT:
                {
                    breakPointPtr breakPtr = NULL;
                    xmlChar *baseUri = NULL;

                    if (cur) {
                        breakPtr =
                            breakPointGet(cur->doc->URL,
                                          xmlGetLineNo(cur));

                        if (breakPtr && (breakPtr->flags & BREAKPOINT_ENABLED) ){
			    debugXSLBreak(cur, node, templ, ctxt);
			    return;
			}
                    }
		    if (node) {
			baseUri = filesGetBaseUri(node);
                        if (baseUri != NULL) {
                            breakPtr =
                                breakPointGet(baseUri, xmlGetLineNo(node));
                        } else {
                            breakPtr =
                                breakPointGet(node->doc->URL,
                                              xmlGetLineNo(node));
                        }
                        if (breakPtr) {
                            if (breakPtr->flags & BREAKPOINT_ENABLED) {
                                debugXSLBreak(cur, node, templ, ctxt);
			    }
			}
                        if (baseUri)
                            xmlFree(baseUri);
                    }
                }
                break;
        }
    }
}
