/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <glib.h>

#include <exception>

#include "cpixsyncpool.h"

#include "cpixerror.h"
#include "cpixthreadlocaldata.h"
#include "common/cpixlog.h"
#include "initparams.h"

#include "CLucene.h"


wchar_t ET_STD_CPP_EXC_NAME[]      = L"Std C++ exception";
wchar_t ET_CPIX_EXC_NAME[]         = L"CPix exception";
wchar_t ET_CLUCENE_EXC_NAME[]      = L"CLucene exception";
wchar_t ET_UNKNOWN_EXC_NAME[]      = L"Unknown exception";
wchar_t ET_CPTSYNTAX_EXC_NAME[]    = L"Syntax exception";
wchar_t ET_CPT_EXC_NAME[]          = L"Cpt exception";
wchar_t ET_OS_EXC_NAME[]           = L"OS exception";

const wchar_t * GetErrorTypeName(cpix_ErrorType errorType)
    {
        wchar_t
            * rv = ET_UNKNOWN_EXC_NAME;

        if (errorType & ET_STD_CPP_EXC)
            {
                rv = ET_STD_CPP_EXC_NAME;
            }
        else if (errorType & ET_CPIX_EXC)
            {
                rv = ET_CPIX_EXC_NAME;
            }
        else if (errorType & ET_CLUCENE_EXC)
            {
                rv = ET_CLUCENE_EXC_NAME;
            }
        else if (errorType == ET_CPTSYNTAX_EXC)
            {
                rv = ET_CPTSYNTAX_EXC_NAME;
            }
        else if (errorType & ET_CPT_EXC)
            {
                rv = ET_CPT_EXC_NAME;
            }
        else if (errorType & ET_OS_EXC)
            {
                rv = ET_OS_EXC_NAME;
            }

        return rv;
    }



/**
 * This class is used for relaying error messages for asynchronous
 * calls. It derives from cpix_Error, and thus IS-A a cpix_Error, and
 * can be used with the rest of the API.
 *
 * Rationale: an error status / message must be available as long as
 * the client does not collect the results of an async call (or has
 * cancelled it).
 */
struct ErrorInfo : public cpix_Error
{
private:
    //
    // private members
    //
    wchar_t      buf_[Cpix::ERRORMSG_BUFSIZE];


public:
    //
    // public operators
    //

    /**
     * Sets the error type (type_ in parent class) to the given type
     * and the message (msg_ in parent class) to NULL.
     *
     * @param errorType the error type to set
     */
    void setInfo(cpix_ErrorType errorType)
    {
        setInfo(errorType,
                static_cast<const wchar_t*>(NULL));
    }


    /**
     * Sets the error type (type_ in parent class) to the given type
     * and message (msg_ in parent class) to the given message.
     *
     *
     * @param errorType the error type to set
     *
     * @param msg the detailed message of the error
     */
    void setInfo(cpix_ErrorType   errorType,
                 const wchar_t  * msg)
    {
        type_ = errorType;
        if (msg == NULL)
            {
                msg_ = NULL;
            }
        else
            {
                wcsncpy(buf_,
                        msg,
                        sizeof(buf_)/sizeof(wchar_t) - 1);
                buf_[sizeof(buf_)/sizeof(wchar_t) - 1] 
                    = wchar_t(0);
                msg_ = buf_;
            }
        impl_ = this;
    }

    
    /**
     * Same as overloaded method, but for char instead of wchar_t.
     */
    void setInfo(cpix_ErrorType     errorType,
                 const char       * msg)
    {
        type_ = errorType;
        if (msg == NULL)
            {
                msg_ = NULL;
            }
        else
            {
                mbstowcs(buf_,
                         msg,
                         sizeof(buf_)/sizeof(wchar_t) - 1);
                buf_[sizeof(buf_)/sizeof(wchar_t) - 1] 
                    = wchar_t(0);
                msg_ = buf_;
            }
        impl_ = this;
    }


    /**
     * Constructor (default). In fact, it initializes the public
     * members of the super class (which is a C struct part of the
     * public API).
     */
    ErrorInfo()
    {
        type_ = ET_UNKNOWN_EXC;
        msg_ = NULL;
        impl_ = this;
    }
};



/**
 * This error info instance is used with ALL synchronous API calls,
 * but never with asynchronous API calls.
 *
 * NOTE: This C++ instance has in practice a problem getting
 * constructed properly with the ARM rvctc 2.2 compiler, but it should
 * not matter - an error info should be set up (setInfo) before use.
 */
ErrorInfo SyncErrorInfo;


/**
 * A singleton pool of ErrorInfo objects that are used only with
 * asynchronous calls, never with synchronous calls.
 *
 * TODO as a debug measure, set a max limit, and warn/assert when it
 * is exceeded, to pinpoint leaking error infos.
 */
class ErrorInfoPool : public Cpt::SyncPool<ErrorInfo>
{
private:
    //
    // private members
    //

    static ErrorInfoPool      * instance_;

public:
    //
    // public operators
    //
    ErrorInfoPool()
        : Cpt::SyncPool<ErrorInfo>(Cpix::MIN_ERRORINFO_COUNT)
    {
        ;
    }


    static ErrorInfoPool * instance()
    {
        if (instance_ == NULL)
            {
                instance_  = new ErrorInfoPool;
            }

        return instance_;
    }
    
    static void shutdown()
    {
        delete instance_;
        instance_ = NULL; 
    }
    
};

namespace Cpix {

    void InitErrorInfoPool()
    {
        ErrorInfoPool::instance();
    }


    void ShutdownErrorInfoPool()
    {
        ErrorInfoPool::shutdown();
    }
}



ErrorInfoPool * ErrorInfoPool::instance_ = NULL;



ErrorInfo * AcquireErrorInfo()
{
    bool
        isSlaveThread = false;

    void
        * key = pthread_getspecific(Cpix::ThreadLocalDataKey);

    if (key == NULL)
        {
            logMsg(CPIX_LL_ERROR,
                   "No ThreadLocalData in this thread (cpix_init() not called?)");
        }
    else
        {
            Cpix::ThreadLocalData
                * tld = reinterpret_cast<Cpix::ThreadLocalData*>(key);
            isSlaveThread = tld->isSlaveThread();
        }
            
    ErrorInfo
        * rv = NULL;

    if (isSlaveThread)
        {
            // for slave threads (async api), we must use an error
            // info instance from the pool that noone else is using
            rv = ErrorInfoPool::instance()->acquire();
        }
    else
        {
            // for master thread (sync api), we must use the global
            // instance
            rv = & SyncErrorInfo;
        }

    return rv;
}


void ReleaseErrorInfo(ErrorInfo * errorInfo)
{
    if (errorInfo != &SyncErrorInfo)
        {
            ErrorInfoPool::instance()->release(errorInfo);
        }
    else
        {
            ; // SyncErrorInfo is static, nothing to release about it
        }
}



cpix_Error * CreateError(cpix_ErrorType   errorType,
                         const wchar_t  * msg)
{
    ErrorInfo
        * errorInfo = NULL;

    try 
        {
            errorInfo = AcquireErrorInfo();
            errorInfo->setInfo(errorType,
                               msg);
        }
    catch (...)
        {
            logMsg(CPIX_LL_ERROR,
                   "!!! Failed to create error struct for type %d ...",
                   errorType);
            logMsg(CPIX_LL_ERROR,
                   "!!! ... (cont'd) msg: %S. Will report CPIX error.",
                   msg == NULL ? L"(null)" : msg);
            
            errorInfo = & SyncErrorInfo;
            errorInfo->setInfo(ET_CPIX_EXC);
        }

    cpix_Error
        * rv = static_cast<cpix_Error*>(errorInfo);

    return rv;
}



cpix_Error * CreateError(cpix_ErrorType    errorType,
                         const char      * msg)
{
    ErrorInfo
        * errorInfo = NULL;

    try
        {
            errorInfo = AcquireErrorInfo();
            errorInfo->setInfo(errorType,
                               msg);
        }
    catch (...)
        {
            logMsg(CPIX_LL_ERROR,
                   "!!! Failed to create error struct for type %d ...",
                   errorType);
            logMsg(CPIX_LL_ERROR,
                   "!!! ... (cont'd) msg: %s. Will report CPIX error.",
                   msg == NULL ? "(null)" : msg);

            errorInfo = & SyncErrorInfo;
            errorInfo->setInfo(ET_CPIX_EXC);
        }

    cpix_Error
        * rv = static_cast<cpix_Error*>(errorInfo);

    return rv;
}



void cpix_Error_report(cpix_Error    * thisError,
                       wchar_t       * target,
                       size_t          targetLength)
{

    if (thisError != NULL)
        {
            const wchar_t
                * errorTypeName = GetErrorTypeName(thisError->type_);
            const wchar_t
                * detail = thisError->msg_;

            if (detail == NULL)
                {
                    detail = L"No details";
                }

            snwprintf(target,
                      targetLength,
                      L"Error type: %S. %S\n",
                      errorTypeName,
                      detail);
        }
    else
        {
            *target = 0;
        }
}



cpix_Error * cpix_Error_destroy(cpix_Error * thisError)
{
    if (thisError != NULL)
        {
            ReleaseErrorInfo(reinterpret_cast<ErrorInfo*>(thisError->impl_));
        }

    return NULL;
}

