/********************************************************************** 
* test program for basic and 'ppembed' extended API calls.
* this includes a "main" function: C is on top in this program.
**********************************************************************/

#include <ppembed.h>
#define TRACE(msg) printf("\n%s\n", msg);


main(argc, argv)
int argc;
char **argv;
{
    printf("Hello from C.\n");
    Py_Initialize();              /* initialize embedded python libraries */
    PySys_SetArgv(argc, argv);    /* optional: export command args to python */
    BASIC_EMBEDDING();
    EMBEDDED_CALL_API();
    printf("Bye from C.\n");
    Py_Exit(0);                   /* optional: runs sys.exitfunc handler */
}


BASIC_EMBEDDING()         /* test low-level interfaces */
{
    PyObject *module, *dict, *pstack;

    /********************************************************************
     * run code-strings in default namespace (module '__main__'); 
     * uses embedding + extending: os/sys use C extension modules;
     * can call PyRun_SimpleString, PyRun_String, PyEval_CallObject..
     ********************************************************************/

    TRACE("SIMPLE STRINGS")
    PyRun_SimpleString("import sys, os");
    PyRun_SimpleString("print 'Hello,', os.environ['USER'] + '.'"); 
    PyRun_SimpleString("print 'Args: ', sys.argv");
    PyRun_SimpleString("print 'Plat: ', sys.platform");
    PyRun_SimpleString("print 'Path: ', sys.path[0:3], '...'");

    /********************************************************************
     * use the stack-type extension in embedded code; PyRun_String uses
     * explicit name-spaces and parse-modes; the PyRun_String results 
     * are None, but they should really be DECREF'd  and return values 
     * should be checked for NULL (a python error/exception occurred);
     ********************************************************************/

    TRACE("STACK EXTENSION")
    module = PyImport_AddModule("__main__");             /* fetch a module */
    dict   = PyModule_GetDict(module);                   /* get its __dict__ */

    PyRun_String("from stacktype import *", Py_file_input, dict, dict);
    PyRun_String("x = Stack()", Py_file_input, dict, dict);

    PyRun_String("for c in 'SPAM':\n"                           /* multi-line */
                   "\tx.push(c)\n", Py_file_input, dict, dict); /* stmts okay */
    PyRun_String("print x, len(x)", Py_file_input, dict, dict);     

    /* make a new instance */              
    pstack = PyRun_String("Stack()", Py_eval_input, dict, dict); 
    PyObject_Print(pstack, stdout, 0);
    Py_DECREF(pstack);
}


EMBEDDED_CALL_API()                  /* test ppembed enhanced wrappers */
{
    int i, status, result;
    char *strres;                    /* actual text is stored in python */ 
    PyObject *pobject1, *pobject2;   /* raw python objects: 'O' output codes */
    
    /********************************************************************
     * use the run_codestr wrapper like PyRun_SimpleString:
     * run string in module __main__ (since the module name argument is 
     * NULL), with no result (since this is a statement: 2nd NULL arg);
     * also see inventory examples for dummy-module creation and use; 
     ********************************************************************/

    PP_Run_Codestr(PP_STATEMENT, "print 'Hello api world'", NULL, "", NULL);

    /********************************************************************
     * turn on dynamic reload-on-call mode, and call a Python function in 
     * a module file; the file can be changed between the five calls here;
     * calls "testapi.func(4, 8)": see testapi_c.py for equiv in Python;
     * note that this imports testapi in C, and adds it to sys.modules, 
     * but doesn't add the name "testapi" to any other Python module:
     ********************************************************************/

    TRACE("DYNAMIC RELOADING")
    PP_RELOAD = 1;
    for (i=0; i < 5; i++) {
        status = PP_Run_Function("testapi", "func", "i", &result, "(ii)", 4, 8);
        if (status == -1) {
            printf("Error running func\n");
            PyErr_Print();                       /* python stack dump */
        }
        else
            printf("result => %d\n", result);
        if (i < 4) {
            printf("change testapi.py now...");     /* edit in another window */
            getchar();                              /* wait for a keypress */
        }
    }

    /********************************************************************
     * turn on dynamic debugging, run string, function, bytecode, object; 
     * pdb stops execution as soon as the embedded code starts: set 
     * breakponts, step through code, etc.; all run "testapi.func(4, 8)",
     * the Python "func" function which is defined in file testapi.py;
     ********************************************************************/

#define announce(test) result=-1; printf("Debug %s--type s to step...\n", test)
#define showresult printf("C status, result => %d, %d\n\n", status, result)

    TRACE("DYNAMIC DEBUGGING")
    PP_RELOAD = 0;
    PP_DEBUG  = 1;       /* could do both at once */

    announce("code string");
    status = PP_Run_Codestr(PP_EXPRESSION, 
                           "func(4, 8)", "testapi", "i", &result);
    showresult;

    announce("module function call");
    status = PP_Run_Function("testapi", "func", "i", &result, "(ii)", 4, 8);
    showresult;

    announce("compiled bytecode");
    pobject1 = PP_Compile_Codestr(PP_EXPRESSION, "func(4, 8)");
    status   = PP_Run_Bytecode(pobject1, "testapi", "i", &result);
    Py_XDECREF(pobject1);
    showresult;

    /* could also fetch with "func" string in "testapi", or PP_Get_Global */
    announce("direct object call");
    pobject1 = PP_Load_Attribute("testapi", "func");
    status   = PP_Run_Known_Callable(pobject1, "i", &result, "(ii)", 4, 8);
    Py_XDECREF(pobject1);
    showresult;

    PP_DEBUG = 0;

    /********************************************************************
     * a few object attribute tests: fetches sys module by name, which 
     * was already imported in module __main__ by the basic embedding 
     * tests; in general, return values should be checked for errors too;
     * the interactive command line also runs in module __main__; 
     ********************************************************************/
 
    TRACE("OBJECT ATTRIBUTES")
    PP_Run_Command_Line("check sys.version");                    /* __main__ */
    PP_Run_Codestr(PP_EXPRESSION, "sys", NULL, "O", &pobject1);  /*  .sys */
    PP_Get_Member(pobject1, "version", "s", &strres);            /*  .version */
    printf("fetched sys.version => %.50s...\n", strres);
    free(strres);

    PP_Set_Member(pobject1, "version", "s", "2.0 (2001?)");      /* change it */
    PP_Run_Codestr(PP_STATEMENT, 
        "print 'changed sys.version =>', sys.version", NULL, "", NULL);

    /* run sys.modules.has_key('testapi') */
    PP_Get_Member(pobject1, "modules", "O", &pobject2);

    PP_Run_Method(pobject2, "has_key", "i", &result, "(s)", "testapi"); 
    printf("sys.modules.has_key('testapi')  result => %d\n", result);

    PP_Run_Method(pobject2, "has_key", "i", &result, "(s)", "nonesuch"); 
    printf("sys.modules.has_key('nonesuch') result => %d\n", result);
    Py_XDECREF(pobject2);

    /* run sys.stdout.write('<stdout text>\n') */
    printf("Sending text to sys.stdout.write...");
    PP_Get_Member(pobject1, "stdout", "O", &pobject2); 
    PP_Run_Method(pobject2, "write", "", NULL, "(s)", "<stdout text>\n");
   
    Py_XDECREF(pobject1);       /* in functions, free raw python objects */
    Py_XDECREF(pobject2);       /* passed out with "O" format code */
}                               /* plus any strings from "s" code  */
