/* -*- C++ -*- */
/*
 *   Copyright 2003-2004 The Apache Software Foundation.
// (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *
 * @author Sanjaya Sinharage (sanjayasing@opensource.lk)
 * @author Susantha Kumara (skumara@virtusa.com)
 * @author Samisa Abeysinghe (sabeysinghe@virtusa.com)
 */

// !!! This include file must be first thing in file !!!
#include "../platforms/PlatformAutoSense.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <string>
#include <map>
#include <iostream>

#include <axis/Axis.hpp>

#ifdef AXIS_CLIENT_LIB
#include <axis/client/Call.hpp>
#include "AxisEngine.h"
#else
#include "server/ServerAxisEngine.h"
#endif

#include <axis/AxisUserAPI.hpp>

#include "../soap/SoapFault.h"
#include "../soap/URIMapping.h"
#include "../soap/SoapKeywordMapping.h"
#include "HandlerLoader.h"
#include "AppScopeHandlerPool.h"
#include "RequestScopeHandlerPool.h"
#include "SessionScopeHandlerPool.h"
#include "HandlerPool.h"
#include "SerializerPool.h"
#include "DeserializerPool.h"
#include "../wsdd/WSDDDeployment.h"
#include "../common/AxisUtils.h"
#include "../common/AxisConfig.h"
#include "../wsdd/WSDDKeywords.h"
#include "SOAPTransportFactory.h"
#include "XMLParserFactory.h"

#include "../common/AxisTrace.h"


AXIS_CPP_NAMESPACE_USE

#define BYTESTOREAD 64
// The relative location of the wsdl files hardcoded
#define WSDLDIRECTORY "/wsdls/"

// Synchronized global variables.
HandlerLoader*           g_pHandlerLoader;
AppScopeHandlerPool*     g_pAppScopeHandlerPool;
RequestScopeHandlerPool* g_pRequestScopeHandlerPool;
SessionScopeHandlerPool* g_pSessionScopeHandlerPool;
DeserializerPool*        g_pDeserializerPool;
SerializerPool*          g_pSerializerPool;
HandlerPool*             g_pHandlerPool;
bool                     g_isRunning;

// Unsynchronized read-only global variables.
WSDDDeployment*          g_pWSDDDeployment;
AxisConfig*              g_pConfig;

//Keeps track of how many times initialize_module/uninitialize_module was called
static volatile long     g_uModuleInitialize = 0;


void ModuleInitialize ()
{    
    // synchronized global variables.
    g_pHandlerLoader = new HandlerLoader ();
    g_pAppScopeHandlerPool = new AppScopeHandlerPool ();
    g_pRequestScopeHandlerPool = new RequestScopeHandlerPool ();
    g_pSessionScopeHandlerPool = new SessionScopeHandlerPool ();
    g_pDeserializerPool = new DeserializerPool ();
    g_pSerializerPool = new SerializerPool ();
    g_pHandlerPool = new HandlerPool ();
    // unsynchronized read-only global variables.
    g_pWSDDDeployment = new WSDDDeployment ();
}

void ModuleUnInitialize ()
{
    // synchronized global variables.
    delete g_pAppScopeHandlerPool;
    delete g_pRequestScopeHandlerPool;
    delete g_pSessionScopeHandlerPool;
    delete g_pDeserializerPool;
    delete g_pSerializerPool;
    delete g_pHandlerPool;
    delete g_pHandlerLoader;
    
    // unsynchronized read-only global variables.
    delete g_pWSDDDeployment;
    delete g_pConfig;

    g_pConfig = NULL;
    g_pWSDDDeployment = NULL;
}

#ifndef AXIS_CLIENT_LIB

STORAGE_CLASS_INFO int process_request(SOAPTransport* pStream)
{
    int Status = AXIS_FAIL;
    FILE* WsddFile = NULL;
    char ReadBuffer[BYTESTOREAD];
    ReadBuffer[0] = '\0';

    const WSDDServiceMap* pSrvMap = NULL;
    WSDDServiceMap::const_iterator iter;
    WSDDService* pService = NULL;

    /* If there is no transport object provided */
    if (!pStream)
        return AXIS_FAIL;

    switch (pStream->getProtocol())
    {
        case APTHTTP1_1:
        case APTHTTP1_0:
            // Handle the POST method
            if (AXIS_HTTP_POST == pStream->getSubProtocol())
            {
                AxisEngine *engine = new ServerAxisEngine ();
                if (engine)
                {
                    if (AXIS_SUCCESS == engine->initialize ())
                    {
                     try
                     {
                        Status = engine->process(pStream);
                       if (AXIS_SUCCESS == Status)
                            pStream->flushOutput();
                        else
                        {
                            ServerAxisEngine* pObjTempServer = (ServerAxisEngine*) engine;
                            pObjTempServer->setFaultOutputStream(Status, pStream);
                            pStream->flushOutput();
                            pObjTempServer = NULL;
                            Status = AXIS_SUCCESS;
                        }
                     }
                     catch(AxisException& e)
                     {
                            ServerAxisEngine* pObjTempServer = (ServerAxisEngine*) engine;
                            pObjTempServer->setFaultOutputStream(e, pStream);
                            pStream->flushOutput();
                            pObjTempServer = NULL;
                            Status = AXIS_SUCCESS;
                     }
                    }
                    delete engine;
                }
            }
            else if (AXIS_HTTP_GET == pStream->getSubProtocol())
            {
                // get the uri path
                // i.e "/abc/xyz/" part of http://somehost/abc/xyz/
                string sUriWOAxis = pStream->getTransportProperty(SERVICE_URI);
                string sServiceName;
                bool bNoExt = true;
                if (sUriWOAxis == "/")
                {
                    bNoExt = false;
                    sUriWOAxis = "";
                }

                if (sUriWOAxis.empty ())
                {
                    pSrvMap = g_pWSDDDeployment->getWSDDServiceMap ();
                    if (!pSrvMap)
                    {
                        // Samisa: the string continuation lines in the following
                        // code segments should *NOT* be indented
                        // in order to make sure the HTML is indented properly.
                        pStream->sendBytes("<html> \n\
\t<head>\n\
\t\t<title>Welcome to Axis C++</title>\n\
\t</head>\n\
\t<body>\n\
\t\t<h1 align=\"center\">Welcome to Axis C++</h1>\n\
\t\t<br>\n\
\t\t<h2>Deployment Descriptor Not Found</h2>\n\
\t\t<br>\n\
\t</body>\n\
</html>\n", NULL);
                        return AXIS_FAIL;
                    }
                    pStream->sendBytes("<html>\n\
\t<head>\n\
\t\t<title>Welcome to Axis C++</title>\n\
\t</head>\n\
\t<body>\n\
\t\t<h1 align=\"center\">Welcome to Axis C++</h1>\n\
\t\t<br>\n\
\t\t<h2 align=\"center\">List of Deployed Web services</h2>\n\
\t\t<br>\n\
\t\t<table width=\"100%\" border=1 align=\"center\">\n\
\t\t\t<tbody>\n", NULL);

                    pStream->sendBytes
                        ("\t\t\t\t<tr>\n\
\t\t\t\t\t<td width=\"20%\"><b>Web Service</b></td>\n\
\t\t\t\t\t<td width=\"10%\" align=\"left\"><b>WSDL</b></td>\n\
\t\t\t\t\t<td width=\"70%\"><b>Description</b></td>\n\
\t\t\t\t</tr>\n", NULL);
                    for (iter = pSrvMap->begin (); iter != pSrvMap->end ();
                        iter++)
                    {
                        pService = (*iter).second;
                        pStream->sendBytes("\t\t\t\t<tr>\n\
\t\t\t\t\t<td width=\"20%\">", NULL);
                        pStream->sendBytes((char*) pService->
                            getServiceName (), NULL);
                        pStream->sendBytes
                            ("</td>\n\
\t\t\t\t\t<td width=\"10%\" align=\"left\">\n\
\t\t\t\t\t\t<a href=\"./",
                            NULL);
                        //if (bNoExt) pStream->sendBytes("axis/", NULL);
                        pStream->sendBytes((char*) pService->
                            getServiceName (), NULL);
                        pStream->sendBytes("?wsdl", NULL);
                        pStream->sendBytes("\">wsdl</a>\n\
\t\t\t\t\t</td>\n\
\t\t\t\t\t<td width=\"70%\">", NULL);
                        pStream->sendBytes((char*) pService->
                            getDescription (), NULL);
                        pStream->sendBytes("</td>\n\
\t\t\t\t</tr>\n", NULL);
                    }
                    pStream->sendBytes("\t\t\t</tbody>\n\
\t\t</table>\n", NULL);
                    pStream->sendBytes
                        ("\t\t<br><p align=\"center\">Copyright  2001-2003 The Apache Software Foundation<br></p>\n\t</body>\n</html>\n", NULL);
                        Status = AXIS_SUCCESS;
                }
                else
                {
                    sServiceName = g_pConfig->getAxisConfProperty(AXCONF_AXISHOME);
                    sServiceName += WSDLDIRECTORY + sUriWOAxis + ".wsdl";
                    // Check whether wsdl file is available
                    if ((WsddFile = fopen (sServiceName.c_str (), "r")) == NULL)
                    {
                        pStream->sendBytes("<h3>Url not available</h3>", NULL);
                        Status = AXIS_SUCCESS;
                    }
                    else
                    {
                        int charcount = 0;
                        while ((charcount = fread (ReadBuffer, 1, BYTESTOREAD - 1, WsddFile)) != 0)
                        {
                            *(ReadBuffer + charcount) = '\0';
                            pStream->sendBytes(ReadBuffer, NULL);
                        }
                        Status = AXIS_SUCCESS;
                        fclose (WsddFile);
                    }
                }
            }
            
        break;

        default:
            pStream->sendBytes("Unknown Protocol", NULL);
            break;
    }

    return Status;
}

#endif

#ifdef WIN32

static CRITICAL_SECTION g_initializationCriticalSection;
static BOOL g_bCSInitialized = (InitializeCriticalSection(&g_initializationCriticalSection), TRUE);
static void start_initializing()
{
    EnterCriticalSection(&g_initializationCriticalSection);
}
static void done_initializing()
{
    LeaveCriticalSection(&g_initializationCriticalSection);
}

#else

#include <pthread.h>
static pthread_mutex_t initializingMutex = PTHREAD_MUTEX_INITIALIZER;

static void start_initializing()
{
    pthread_mutex_lock(&initializingMutex);
}

static void done_initializing()
{
    pthread_mutex_unlock(&initializingMutex);
}

#endif
 


extern "C" {
STORAGE_CLASS_INFO
int initialize_module (int bServer)
{
    start_initializing();
    
    int status = AXIS_SUCCESS;

    try
    {
        if (g_uModuleInitialize == 0)
        {
            // Read config file and enable trace....this should be first thing that is done!!!
            // Read from the configuration file
            if( g_pConfig != NULL)
                g_pConfig = new AxisConfig( g_pConfig);
            else
                g_pConfig = new AxisConfig();
            
            status = g_pConfig->readConfFile (); 
            if (status == AXIS_SUCCESS)
            {
                // One can also start trace via Axis::startTrace().  If that has been done, 
                // ignore the config file. 
                if (!AxisTrace::isLoggingEnabled())
                {
                    AxisTrace::setLogFilter(g_pConfig->getAxisConfProperty(AXCONF_LOGFILTER));
                    
                    if (bServer)
                        AxisTrace::startTrace(g_pConfig->getAxisConfProperty(AXCONF_LOGPATH));
                    else
                        AxisTrace::startTrace(g_pConfig->getAxisConfProperty(AXCONF_CLIENTLOGPATH));
                }
                
                string configProperties = g_pConfig->toString();
                AxisTrace::writeTrace(configProperties.c_str(), configProperties.length());
            }
            
            // The entry log must start here - may revisit so as to start earlier. 
            logEntryEngine("initialize_module")
            
            // order of these initialization method invocation should not be changed
            AxisEngine::m_bServer = bServer;
            AxisUtils::initialize ();
            WSDDKeywords::initialize ();
            SoapKeywordMapping::initialize ();
            TypeMapping::initialize ();
            URIMapping::initialize ();
            SoapFault::initialize ();
            ModuleInitialize ();
            
            if (bServer) // no client side wsdd processing at the moment
            {
                if (status == AXIS_SUCCESS)
                {                 
                    try
                    {            
                        XMLParserFactory::initialize();
                    }
                    catch (AxisException& e)
                    {
                        logRethrowException()
                        
                        throw AxisEngineException(e.getExceptionCode(), e.what());
                    }

                    char *pWsddPath = g_pConfig->getAxisConfProperty(AXCONF_WSDDFILEPATH);

                    try
                    {            
                        if (AXIS_SUCCESS != g_pWSDDDeployment->loadWSDD (pWsddPath))
                            status = AXIS_FAIL;
                    }
                    catch (AxisException& e)
                    {
                        logRethrowException()
                        
                        throw AxisEngineException(e.getExceptionCode(), e.what());
                    }
                }
                else
                    status = AXIS_FAIL;
           }
           else if (bServer == 0)      // client side module initialization
           {
                if (status == AXIS_SUCCESS)
                {                    
                   XMLParserFactory::initialize();
                   SOAPTransportFactory::initialize();

                   char *    pClientWsddPath = g_pConfig->getAxisConfProperty(AXCONF_CLIENTWSDDFILEPATH);
    
                   // May be there is no client side handlers configured. So may not have CLIENTWSDDFILEPATH entry in axiscpp.conf 
                   if (pClientWsddPath)
                       if (AXIS_SUCCESS != g_pWSDDDeployment->loadWSDD (pClientWsddPath))
                           status = AXIS_FAIL;
                }
           }
           else
           {
               /* Ok if we can't read config file */
               status = AXIS_SUCCESS;
           }
            
           g_isRunning = true;
           
           logExitWithReturnCode(status)
       }
       else if (AxisEngine::m_bServer != bServer)
       {           
           throw AxisEngineException(SERVER_ENGINE_EXCEPTION);
       }
    }
    catch (...)
    {
        done_initializing();        
        throw;
    }

    ++g_uModuleInitialize;
    done_initializing();

    return status;
}
}


extern "C" {
STORAGE_CLASS_INFO
int uninitialize_module ()
{
    logEntryEngine("uninitialize_module")

    start_initializing();

    try
    {
        if (g_uModuleInitialize > 0)
        {
            if (--g_uModuleInitialize == 0)
            {
                g_isRunning = false;
                TypeMapping::uninitialize();
                URIMapping::uninitialize();
                if (!AxisEngine::m_bServer) 
                    SOAPTransportFactory::uninitialize();
                ModuleUnInitialize();
                SoapKeywordMapping::uninitialize();
                XMLParserFactory::uninitialize();
            }
        }
    }
    catch (...)
    {
        done_initializing();
        
        logRethrowException()
        
        throw;
    }

    done_initializing();
    
    logExitWithReturnCode(AXIS_SUCCESS)

    return AXIS_SUCCESS;
}
}


void Ax_Sleep (int nTime)
{
    PLATFORM_SLEEP(0);
}


// Axis class method implementations

void Axis::initialize(bool bIsServer)
{
    logEntryEngine("Axis::initialize")

    initialize_module(bIsServer);
    
    logExit()
}

void Axis::terminate()
{
    logEntryEngine("Axis::terminate")

    uninitialize_module();
    
    logExit()
}

void Axis::AxisDelete(void *pValue, XSDTYPE type)
{
    logEntryEngine("Axis::AxisDelete")

    if (pValue == NULL)
        return;
        
    switch (type)
    {
        case XSD_STRING:
        {
            delete [] (xsd__string) pValue;
            break;
        }
        case XSD_NORMALIZEDSTRING:
        {
            delete [] (xsd__normalizedString) pValue;
            break;
        }
        case XSD_FLOAT:
        {
            delete (xsd__float*) pValue;
            break;
        }
        case XSD_DECIMAL:
        {
            delete (xsd__decimal*) pValue;
            break;
        }
        case XSD_NONPOSITIVEINTEGER:
        {
            delete (xsd__nonPositiveInteger*) pValue;
            break;
        }
        case XSD_NEGATIVEINTEGER:
        {
            delete (xsd__negativeInteger*) pValue;
            break;
        }
        case XSD_INTEGER:
        {
            delete (xsd__integer*) pValue;
            break;
        }
        case XSD_LONG:
        {
            delete (xsd__long*) pValue;
            break;
        }
        case XSD_INT:
        {
            delete (xsd__int*) pValue;
            break;
        }
        case XSD_SHORT:
        {
            delete (xsd__short*) pValue;
            break;
        }
        case XSD_BYTE:
        {
            delete (xsd__byte*) pValue;
            break;
        }
        case XSD_NONNEGATIVEINTEGER:
        {
            delete (xsd__nonNegativeInteger*) pValue;
            break;
        }
        case XSD_UNSIGNEDLONG:
        {
            delete (xsd__unsignedLong*) pValue;
            break;
        }
        case XSD_UNSIGNEDINT:
        {
            delete (xsd__unsignedInt*) pValue;
            break;
        }
        case XSD_UNSIGNEDSHORT:
        {
            delete (xsd__unsignedShort*) pValue;
            break;
        }
        case XSD_UNSIGNEDBYTE:
        {
            delete (xsd__unsignedByte*) pValue;
            break;
        }
        case XSD_POSITIVEINTEGER:
        {
            delete (xsd__positiveInteger*) pValue;
            break;
        }
        case XSD_DOUBLE:
        {
            delete (xsd__double*) pValue;
            break;
        }        
        case XSD_DURATION:
        {
            delete (xsd__duration*) pValue;
            break;
        }
        case XSD_DATETIME:
        {
            delete (xsd__dateTime*) pValue;
            break;
        }
        case XSD_TIME:
        {
            delete (xsd__time*) pValue;
            break;
        }
        case XSD_DATE:
        {
            delete (xsd__date*) pValue;
            break;
        }
        case XSD_GYEARMONTH:
        {
            delete (xsd__gYearMonth*) pValue;
            break;
        }
        case XSD_GYEAR:
        {
            delete (xsd__gYear*) pValue;
            break;
        }
        case XSD_GMONTHDAY:
        {
            delete (xsd__gMonthDay*) pValue;
            break;
        }
        case XSD_GDAY:
        {
            delete (xsd__gDay*) pValue;
            break;
        }
        case XSD_GMONTH:
        {
            delete (xsd__gMonth*) pValue;
            break;
        }
        case XSD_TOKEN:
        {
            delete [] (xsd__token) pValue;
            break;
        }
        case XSD_LANGUAGE:
        {
            delete [] (xsd__language) pValue;
            break;
        }
        case XSD_NAME:
        {
            delete [] (xsd__Name) pValue;
            break;
        }
        case XSD_NCNAME:
        {
            delete [] (xsd__NCName) pValue;
            break;
        }
        case XSD_ID:
        {
            delete [] (xsd__ID) pValue;
            break;
        }
        case XSD_IDREF:
        {
            delete [] (xsd__IDREF) pValue;
            break;
        }
        case XSD_IDREFS:
        {
            delete [] (xsd__IDREFS) pValue;
            break;
        }
        case XSD_ENTITY:
        {
            delete [] (xsd__ENTITY) pValue;
            break;
        }
        case XSD_ENTITIES:
        {
            delete [] (xsd__ENTITIES) pValue;
            break;
        }
        case XSD_NMTOKEN:
        {
            delete [] (xsd__NMTOKEN) pValue;
            break;
        }
        case XSD_NMTOKENS:
        {
            delete [] (xsd__NMTOKENS) pValue;
            break;
        }
        case XSD_BOOLEAN:
        {
            delete (xsd__boolean*) pValue;
            break;
        }
        case XSD_BASE64BINARY:
        {
            delete (xsd__base64Binary*) pValue;
            break;
        }
        case XSD_HEXBINARY:
        {
            delete (xsd__hexBinary*) pValue;
            break;
        }
        case XSD_ANYURI:
        {
            delete [] (xsd__anyURI) pValue;
            break;
        }
        case XSD_QNAME:
        {
            delete [] (xsd__QName) pValue;
            break;
        }
        case XSD_NOTATION:
        {
            delete [] (xsd__NOTATION) pValue;
            break;
        }
        case USER_TYPE:
            // The USER_TYPE object should be cleared by the Application
            break;
        case XSD_ARRAY:
        {
            delete (Axis_Array*) pValue;
            break;
        }
        case XSD_ANYTYPE:
        {
            delete [] (xsd__anyType) pValue;
            break;
        }            
        case XSD_ANY:
        case ATTACHMENT:
        case XSD_UNKNOWN:
        default:
            ;
    }
    
    logExit()
}

bool Axis::isRunning()
{
    return g_isRunning;
}

void Axis::stopAxis()
{
    logEntryEngine("Axis::stopAxis")

    start_initializing();
    g_isRunning = false;
    done_initializing();
    
    logExit()
}

int Axis::
startTrace(const char* logFilePath, const char *logFilter)
{
    AxisTrace::setLogFilter(logFilter);
    return AxisTrace::startTrace(logFilePath);
}

void Axis::
stopTrace()
{
    AxisTrace::stopTrace();
}

void Axis::
writeTrace(AXIS_TRACE_TYPE type, const char* functionName, const char * fmt, ...)
{
    // If logging is not enabled, just return.
    if (!AxisTrace::isLoggingEnabled() || !AxisTrace::isStubLoggingEnabled())
        return;
    
    // Construct final formatter
    string myfmt;
    string blank = " ";
    
    char *traceType = TRACE_TYPE_DEBUG;
    if (type == AXIS_TRACE_TYPE_ENTRY)
        traceType = TRACE_TYPE_ENTRY;
    else if (type == AXIS_TRACE_TYPE_EXIT)
        traceType = TRACE_TYPE_EXIT;
    else if (type == AXIS_TRACE_TYPE_EXCEPTION)
        traceType = TRACE_TYPE_EXCEPT;
    
    if (NULL == fmt)
        fmt = "";
    myfmt += TRACE_COMPONENT_STUB + blank;
    myfmt += traceType + blank;
    myfmt += functionName;
    myfmt += "(): ";
    myfmt += fmt;
        
    va_list vargs;
    va_start(vargs,fmt);
    AxisTrace::writeTrace(myfmt.c_str(), vargs);        
    va_end(vargs);
}