#include <assert>
#include <fstream>
#include <iostream>
#include <strstream>

#include <vector>
#include <time.h>

//---< xal stuff >------------------------------------< begin >---
#include <util/PlatformUtils.hpp>

#include <PlatformSupport/DOMStringHelper.hpp>
#include <DOMSupport/DOMSupportDefault.hpp>

#include <XPath/XObjectFactoryDefault.hpp>
#include <XPath/XPathSupportDefault.hpp>
#include <XPath/XPathFactoryDefault.hpp>

#include <XSLT/StylesheetConstructionContextDefault.hpp>
#include <XSLT/StylesheetExecutionContextDefault.hpp>
#include <XSLT/StylesheetRoot.hpp>
#include <XSLT/XSLTEngineImpl.hpp>
#include <XSLT/XSLTInit.hpp>
#include <XSLT/XSLTInputSource.hpp>
#include <XSLT/XSLTProcessorEnvSupportDefault.hpp>
#include <XSLT/XSLTResultTarget.hpp>

#include <XercesParserLiaison/XercesDOMSupport.hpp>
#include <XercesParserLiaison/XercesParserLiaison.hpp>

#if !defined(XALAN_NO_NAMESPACES)
    using std::cerr;
    using std::cout;
    using std::endl;
    using std::ifstream;
    using std::ios_base;
    using std::ostrstream;
    using std::string;
#endif

// Used to hold compiled stylesheet and XML source document.
StylesheetRoot* glbStylesheetRoot;
XalanNode* glbDocSource;
//---< xal stuff >--------------------------------------< end >---



using namespace std;

#define THREAD_NUM 10


pthread_mutex_t     current_mutex;
pthread_mutex_t     cond_mutex;
pthread_cond_t      cond_var;
int         current_num = -1;
int         thread_hold = 1;
pthread_t       threads[THREAD_NUM];




void outputMessage(int my_number, char * msg)
{
    printf("\n %s Thread: %d", msg, my_number);
}

static void check(int status, char* msg)
{
    if (status != 0) {
    printf("\nStatus = %d.  ", status);
    perror(msg);
    }
}

static void unlock_cond(void *arg)
{
    int status = pthread_mutex_unlock(&cond_mutex);
    check(status, "3:Mutex_unlock bad status\n");
}

//---< xal stuff >------------------------------------< begin >---
static int do_all_operations(int my_number)
{
    // Create the support objects that are necessary for running the processor...
    XercesDOMSupport                theDOMSupport;
    XercesParserLiaison             theParserLiaison(theDOMSupport);
    XPathSupportDefault             theXPathSupport(theDOMSupport);
    XSLTProcessorEnvSupportDefault  theXSLTProcessorEnvSupport;
    XObjectFactoryDefault           theXObjectFactory;
    XPathFactoryDefault             theXPathFactory;

    // Create a processor...and output the start message.
    XSLTEngineImpl  theProcessor(
                    theParserLiaison,
                    theXPathSupport,
                    theXSLTProcessorEnvSupport,
                    theDOMSupport,
                    theXObjectFactory,
                    theXPathFactory);
    outputMessage(my_number,"\n     Starting");

    // Connect the processor to the support object...
    theXSLTProcessorEnvSupport.setProcessor(&theProcessor);

    // The execution context uses the same factory support objects as
    // the processor, since those objects have the same lifetime as
    // other objects created as a result of the execution.
    StylesheetExecutionContextDefault   ssExecutionContext(
                        theProcessor,
                        theXSLTProcessorEnvSupport,
                        theXPathSupport,
                        theXObjectFactory);

    // Set the XSLTInputSource...
    XSLTInputSource xslIn(glbDocSource);

    // Generate the output file name for this thread.
    ostrstream theFormatterOut;
    theFormatterOut << "birds" << my_number << ".out" << '\0';

    //Generate the XML output object.
    XSLTResultTarget    theResultTarget(theFormatterOut.str());

    // Unfreeze the ostrstream, so memory is returned...
    theFormatterOut.freeze(false);

    // Set the processor to use the compiled stylesheet. Then do the transform
    // with the process() method that uses the compiled stylesheet.
    // Report the start of the transformation and the termination of the thread.
    theProcessor.setStylesheetRoot(glbStylesheetRoot);
    outputMessage(my_number,"Transforming");
    theProcessor.process(xslIn,theResultTarget,ssExecutionContext);
    outputMessage(my_number,"   Finishing");
    return 0;
}
//---< xal stuff >--------------------------------------< end >---


static void * performOperation(void *arg)
{
    int my_number = (int)arg;
    int status = pthread_mutex_lock(&cond_mutex);
    check(status, "1:Mutex_lock bad status\n");
    pthread_cleanup_push(unlock_cond, NULL);

  
    while (thread_hold)
    {
    status = pthread_cond_wait(&cond_var, &cond_mutex);
    check(status, "2:Mutex_lock bad status\n");
    }
    pthread_cleanup_pop(1);

    pthread_testcancel();
    status = pthread_mutex_lock(&current_mutex);
    check(status, "4:Mutex_lock bad status\n");

    
    do_all_operations(my_number);           // all activities...

    status = pthread_mutex_unlock(&current_mutex);
    check(status, "5:Mutex_unlock bad status\n");

    pthread_exit( (void*)my_number);
    return(void*)0;
}


void doThreads()
{
    int status=-1, i=0;
    void *exit_value;
    
    status = pthread_mutex_init(&cond_mutex, NULL);
    check(status, "6:Mutex_init bad status\n");

    status = pthread_mutex_init(&current_mutex, NULL);
    check(status, "7:Mutex_init bad status\n");

    status = pthread_cond_init(&cond_var, NULL);
    check(status, "8:Mutex_init bad status\n");

    for (i=0; i<THREAD_NUM; i++)
    {
    status = pthread_create(
            &threads[i],
            NULL,
            performOperation,
            (void*)i);
    check(status, "9:pthread_create bad status\n");
    }

    // clean up threads...
    status = pthread_mutex_lock(&cond_mutex);
    check(status, "10:Mutex_lock bad status\n");

    thread_hold = 0;
    status = pthread_cond_broadcast(&cond_var);
    status = pthread_mutex_unlock(&cond_mutex);
    check(status, "11:Mutex_unlock bad status\n");

    for (i=0; i<THREAD_NUM; i++)
    {
    status = pthread_join(
            threads[i],
            &exit_value);
    check(status, "12:Pthread_join bad status\n");

    if (exit_value == (void*)i)
        printf(".");
    else printf("Bad, bad, bad ...");
    }
}


int main(int argc, const char*[])
{

    if (argc != 1)
    {
        cerr << "Usage: ThreadTest" << endl;
    }
    else
    {
        try
        {
            // Call the static initializer for Xerces...
            XMLPlatformUtils::Initialize();
            

            // Initialize the Xalan XSLT subsystem...
            XSLTInit                        theInit;

            // Create the support objects required to run the processor...
            XercesDOMSupport                ssDOMSupport;
            XercesParserLiaison             ssParserLiaison(ssDOMSupport);
            XPathSupportDefault             ssXPathSupport(ssDOMSupport);
            XSLTProcessorEnvSupportDefault  ssXSLTProcessorEnvSupport;
            XObjectFactoryDefault           ssXObjectFactory;
            XPathFactoryDefault             ssXPathFactory;

            // Create a processor...
            // This processor is used to compile the stylesheet and the source document. 
            // Each thread uses its own processor to perform a transformation.

            XSLTEngineImpl  ssProcessor(
                    ssParserLiaison,
                    ssXPathSupport,
                    ssXSLTProcessorEnvSupport,
                    ssDOMSupport,
                    ssXObjectFactory,
                    ssXPathFactory);

            // Create a stylesheet construction context, using the
            // stylesheet's factory support objects.
            StylesheetConstructionContextDefault    ssConstructionContext(
                                                    ssProcessor,
                                                    ssXSLTProcessorEnvSupport,
                                                    ssXPathFactory);

            const XalanDOMString  theXSLFileName("birds.xsl");
            const XalanDOMString  theXMLFileName("birds.xml");

            // Our stylesheet XML input document and XSL stylesheet
            XSLTInputSource   xmlDocSource(c_wstr(theXMLFileName));
            XSLTInputSource     xslStylesheetSource(c_wstr(theXSLFileName));

            // Use the processor to create a StylesheetRoot for the specified
            // input XSL.  This is the compiled stylesheet.  We don't have to
            // delete it, since it is owned by the StylesheetConstructionContext
            // instance.
            glbStylesheetRoot = ssProcessor.processStylesheet(xslStylesheetSource,
                                                       ssConstructionContext);
            assert(glbStylesheetRoot != 0);
            
            // Compile the XML source document as well. All threads will use
            // this binary representation of the source tree.
            glbDocSource = ssProcessor.getSourceTreeFromInput(xmlDocSource);
            assert(glbDocSource != 0);


            //---< thread stuff >--------------------------------< begin >---
            printf("\n Clock before starting threads: %ld", clock());
            doThreads();
            printf("\n\n Clock after finishing threads: %ld", clock());
            //---< thread stuff >----------------------------------< end >---


            // Call the static terminator for Xerces...
            XMLPlatformUtils::Terminate();
        }
        catch(...)
        {
            cerr << "Exception caught!!!" << endl;
        }
    }

    return 0;
}
