/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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 Files
**-------------------------------------------------------------------------
*/
#include "nwx_defs_symbian.h"
#include "nwx_defs.h"
#include "nwx_ctx.h"
#include "nwx_psq.h"
#include "nwx_msg_api.h"
#include "nwx_datastruct.h"
#include "nwx_osu.h"
#include "nwx_logger.h"
#include "nwx_msg_dsp_api_epoc32.h"
#include "nwx_string.h"
#include "util_CDispatcherEng.h"
#include "BrsrStatusCodes.h"


/*
**-------------------------------------------------------------------------
**  Constants
**-------------------------------------------------------------------------
*/

/*
**-------------------------------------------------------------------------
**  Internal Types
**-------------------------------------------------------------------------
*/
typedef struct {
  CDispatcherEng* ipCDispatcherEng;
} NW_DspEng_Struct;

typedef NW_DspEng_Struct* NW_DspEng_t;

typedef struct {
  NW_Ds_DynamicArray_t *dispatchers;     /* dispatchers know to this guy */
  NW_Osu_Hwnd_t        hwnd;             /* Window handle for WndProc disp */
  NW_DspEng_t          DspEng;    /* Pointer to struct encapslate a C++ class */
} Msg_DspContext_Struct;

typedef Msg_DspContext_Struct Msg_DspContext_t;

/*
**-------------------------------------------------------------------------
**  Macros
**-------------------------------------------------------------------------
*/

/*
**-------------------------------------------------------------------------
**  Internal Prototypes
**-------------------------------------------------------------------------
*/
static Msg_DspContext_t *MsgDsp_GetThis();
static void MsgDsp_SetThis(Msg_DspContext_t *cxt);
static TBrowserStatusCode NW_Msg_DispatcherStart(void *);

/*
**-------------------------------------------------------------------------
**  File Scoped Static Variables
**-------------------------------------------------------------------------
*/

/*
**-------------------------------------------------------------------------
**  Global Variable Definitions
**-------------------------------------------------------------------------
*/

/*
**-------------------------------------------------------------------------
**  Internal Functions
**-------------------------------------------------------------------------
*/

static Msg_DspContext_t *MsgDsp_GetThis()
{
  return (Msg_DspContext_t *)NW_Ctx_Get(NW_CTX_MSG_DSP,0);
}

static void MsgDsp_SetThis(Msg_DspContext_t *ctx)
{
	NW_Ctx_Set(NW_CTX_MSG_DSP, 0, ctx);
};



/*********************************************************
**  Name:   NW_Msg_DispatcherStart
**  Description:  Dispatcher start routine.
**  Parameters:   *data - message dispatcher to start
**  Return Value: nominally, KBrsrSuccess (never returns)
**********************************************************/
static TBrowserStatusCode NW_Msg_DispatcherStart(void *data)
{
  NW_Msg_Dispatcher_t *dsp  = (NW_Msg_Dispatcher_t *)data;
  Msg_DspContext_t* cxt;

  NW_ASSERT (dsp != NULL);
  cxt = MsgDsp_GetThis();
  if (cxt == NULL)
    return KBrsrOutOfMemory;

  if (!(cxt->DspEng->ipCDispatcherEng->IsDspContext()))
    cxt->DspEng->ipCDispatcherEng->SetDspContext(dsp);
  /* Signal that we have started */
  //NW_Osu_NotifySignal(dsp->signal);

  cxt->DspEng->ipCDispatcherEng->RequestEvent();
 
  return KBrsrSuccess;
}


/*
**-------------------------------------------------------------------------
**  External Public (Exported) Functions
**-------------------------------------------------------------------------
*/

/*********************************************************
**  Name:   NW_Msg_DispatcherInitialize
**  Description:  Creates a new messaging dispatching system.
**                There will be only one of these per process. 
**                It will know about all of the dispatchers in 
**                the process.
**  Parameters:   qName : Message queue name
**  Return Value: KBrsrSuccess or KBrsrOutOfMemory
**********************************************************/
TBrowserStatusCode NW_Msg_DispatcherInitialize(const char* qName)
{
  Msg_DspContext_t *ctx;
  
  /* Get a pointer to the context. If it is null
     create a new one and store it */
  ctx = MsgDsp_GetThis();
  if (ctx == NULL) 
  {
    ctx = (Msg_DspContext_t *)NW_Mem_Malloc(sizeof(Msg_DspContext_t));
    if (ctx == NULL)
    {
      return KBrsrOutOfMemory;
    }
    ctx->DspEng = (NW_DspEng_Struct*)NW_Mem_Malloc(sizeof(NW_DspEng_Struct));
    if (ctx->DspEng == NULL)
    {
      NW_Mem_Free(ctx);
      MsgDsp_SetThis(NULL);
      return KBrsrOutOfMemory;
    }

    ctx->DspEng->ipCDispatcherEng = new CDispatcherEng(CActive::EPriorityUserInput);
    if (ctx->DspEng->ipCDispatcherEng == NULL)
    {
      NW_Mem_Free(ctx->DspEng);
      ctx->DspEng = NULL;
      NW_Mem_Free(ctx);
      MsgDsp_SetThis(NULL);
      return KBrsrOutOfMemory;
    }

    ctx->dispatchers = NW_Ds_DarNew_DefaultHeap(3);
    if (ctx->dispatchers == NULL) 
    {
      delete ctx->DspEng->ipCDispatcherEng;
      NW_Mem_Free(ctx->DspEng);
      ctx->DspEng = NULL;
      NW_Mem_Free(ctx);
      MsgDsp_SetThis(NULL);
      return KBrsrOutOfMemory;
    }
    ctx->hwnd = NULL;
    MsgDsp_SetThis(ctx);

    /* Now call the startup routine */
    TBrowserStatusCode status = NW_Msg_Startup(qName) ;
    if (status == KBrsrOutOfMemory)
    {
      NW_Ds_DarDelete (ctx->dispatchers);
      ctx->dispatchers = NULL;
      delete ctx->DspEng->ipCDispatcherEng;
      NW_Mem_Free(ctx->DspEng);
      ctx->DspEng = NULL;
      NW_Mem_Free(ctx);
      MsgDsp_SetThis(NULL);
      return KBrsrOutOfMemory;
    }
  }

  return KBrsrSuccess;
}

/*********************************************************
**  Name:   NW_Msg_NewDispatcher
**  Description:  Creates and initializes a new message dispatcher
**  Parameters:   type - dispatcher type
**  Return Value: pointer to new dispatcher or NULL
**********************************************************/
NW_Msg_Dispatcher_t *NW_Msg_NewDispatcher(const NW_Msg_DispatcherType_t type,
                                          const char* qName)
{
  Msg_DspContext_t *ctx = MsgDsp_GetThis();
  NW_Msg_Dispatcher_t *tmp;

  NW_ASSERT (ctx != NULL);
  NW_ASSERT (type == NW_MSG_PSQ_DSP || type == NW_MSG_NATIVE_DSP);

  /* Allocate a dispatcher structure */
  tmp = (NW_Msg_Dispatcher_t *)NW_Mem_Malloc(sizeof(NW_Msg_Dispatcher_t));
  if (tmp != NULL) {
    tmp->type     = type;
    tmp->handlers = NULL;
    tmp->hwnd     = NULL;
    tmp->tmofunc  = NULL;
    tmp->wait     = NW_WAIT_FOREVER;

    /* Not used in EPOC implementation */
    tmp->signal   = NULL;

    switch (type) {
     case NW_MSG_PSQ_DSP:
      tmp->queue = NW_Psq_New(qName);
      if (tmp->queue != NULL) {
          NW_Msg_DispatcherStart(tmp);
          tmp->thread = NW_Osu_GetCurrentThreadId();
      }
      else {
        NW_Mem_Free(tmp);
        return NULL;
      }
      break;
     case NW_MSG_NATIVE_DSP:
      /* If we are using a WndProc dispatcher, just save the thread for */
      /* later use in GetThisDispatcher calls */
      tmp->thread = NW_Osu_GetCurrentThreadId();
      tmp->hwnd = ctx->hwnd;
      break;
     default:
      NW_Mem_Free(tmp);
      return NULL;
    }

    /* Add the new dispatcher to the context */
    if (NW_Ds_DarAppend(ctx->dispatchers, tmp) != KBrsrSuccess) {
      NW_Mem_Free(tmp);
      return NULL;
    }
  }

  return tmp;
}

/*********************************************************
**  Name:   NW_Msg_DispatcherRegister
**  Description:  Registers a message handler with a dispatcher.
**  Parameters:   *dsp - pointer to the dispatcher
**                *entry - pointer to the message map entry
**  Return Value: KBrsrSuccess or KBrsrOutOfMemory
**********************************************************/
TBrowserStatusCode NW_Msg_DispatcherRegister(NW_Msg_Dispatcher_t *dsp,
                                      const NW_Msg_MapEntry_t *entry)
{
  NW_Msg_Handler_t *handler;
  NW_Msg_Handler_t *tmp;

  NW_ASSERT (dsp != NULL);
  NW_ASSERT (entry != NULL);

  /* Create a handler structure */
  handler = (NW_Msg_Handler_t*)NW_Mem_Malloc(sizeof(NW_Msg_Handler_t));
  if (handler == NULL)
    return KBrsrOutOfMemory;

  handler->next = NULL;
  handler->type = entry->msgType;
  handler->name = entry->name;
  handler->func = entry->func;

  /* If the first one, just set it */
  if (dsp->handlers == NULL) {
    dsp->handlers = handler;
  } else {
    /* locate the last one in the last */
    tmp = dsp->handlers;
    while (tmp->next != NULL)
      tmp = (NW_Msg_Handler_t *)tmp->next;
    tmp->next = handler;
  }
  return KBrsrSuccess;
}

/*********************************************************
**  Name:   NW_Msg_DispatcherSend
**  Description:  Sends a message to a dispatcher.
**  Parameters:   *dsp - pointer to the dispatcher
**                *msg - pointer to the message
**  Return Value: KBrsrSuccess or KBrsrUnexpectedError
**********************************************************/
TBrowserStatusCode NW_Msg_DispatcherSend(NW_Msg_Dispatcher_t *dsp, NW_Msg_t *msg)
{
  Msg_DspContext_t *ctx = MsgDsp_GetThis();

  if (ctx == NULL)
    return KBrsrOutOfMemory;

  NW_ASSERT (dsp != NULL);
  NW_ASSERT (msg != NULL);
  NW_ASSERT (dsp->type == NW_MSG_PSQ_DSP || dsp->type == NW_MSG_NATIVE_DSP);

  switch (dsp->type) {
   case NW_MSG_PSQ_DSP:
    NW_Psq_AddTail(dsp->queue, (NW_Node_t *)msg);
    ctx->DspEng->ipCDispatcherEng->RequestEvent();
    break;
   case NW_MSG_NATIVE_DSP:
    break;
   default:
    return KBrsrUnexpectedError;
  }

  return KBrsrSuccess;
}

/*********************************************************
**  Name:   NW_Msg_LookupDispatcherByName
**  Description:  Lookup the message dispatcher from the name.
**  Parameters:   name - name known to sender
**  Return Value: pointer to the dispatcher or NULL
**********************************************************/
NW_Msg_Dispatcher_t *NW_Msg_LookupDispatcherByName(const NW_Msg_RName_t name)
{
  Msg_DspContext_t *ctx = MsgDsp_GetThis();
  NW_Msg_Handler_t *handler;
  NW_Msg_Dispatcher_t *dsp;
  NW_Int16 i, j;

  /* Go through the list of dispatchers and find the one requested */
  if (ctx != NULL) {
    j = NW_Ds_DarGetCount(ctx->dispatchers);
    for (i = 0; i < j; i++)  {
      dsp = (NW_Msg_Dispatcher_t *)NW_Ds_DarGetElement(ctx->dispatchers, i);
      handler = dsp->handlers;
      while (handler != NULL) {
        if (handler->name == name)
          return dsp;
        else
          handler = (NW_Msg_Handler_t *)handler->next;
      }
    }
  }

  return NULL;
}

/*********************************************************
**  Name:   NW_Msg_GetThisDispatcher
**  Description:  Get the message dispatcher for current thread.
**  Parameters:   none
**  Return Value: pointer to the dispatcher or NULL
**********************************************************/
NW_Msg_Dispatcher_t *NW_Msg_GetThisDispatcher()
{
  Msg_DspContext_t *ctx = MsgDsp_GetThis();
  NW_Osu_ThreadId_t thread = NW_Osu_GetCurrentThreadId();
  NW_Msg_Dispatcher_t *dsp;
  NW_Uint16 i, j;

  /* Go through the list of dispatchers and find the one requested */
  if (ctx != NULL) {
    j = NW_Ds_DarGetCount(ctx->dispatchers);
    for (i = 0; i < j; i++)  {
      dsp = (NW_Msg_Dispatcher_t *)NW_Ds_DarGetElement(ctx->dispatchers, i);
      if (dsp->thread == thread)
        return dsp;
    }
  }

  return NULL;
}

/*********************************************************
**  Name:   NW_Msg_NYI
**  Description:  Default message handler for unimplemented types.
**  Parameters:   *msg - pointer to the message
**  Return Value: KBrsrSuccess
**********************************************************/
TBrowserStatusCode NW_Msg_NYI(NW_Msg_t *msg)
{
  NW_REQUIRED_PARAM(msg);
  NW_ASSERT (msg != NULL);

  NW_LOG1(NW_LOG_LEVEL5, "NYI Message Handler %d\n", msg->type);

  return KBrsrSuccess;
}

/*********************************************************
**  Name:   NW_Msg_DispatcherShutdown()
**  Description:  Rainbow Message Dispatcher Shutdown.
**                Free's all memory allocated in NW_Msg_DispatcherInitialize()
**  Parameters:   none 
**  Return Value: none 
**********************************************************/
void NW_Msg_DispatcherShutdown() 
{
    // Free up Dispatching System from NW_Msg_DispatcherInitialize()
    Msg_DspContext_t *dsp_ctx = MsgDsp_GetThis();
    if (dsp_ctx == NULL)
      return;

    dsp_ctx->DspEng->ipCDispatcherEng->Cancel();
    delete dsp_ctx->DspEng->ipCDispatcherEng;
    NW_Mem_Free(dsp_ctx->DspEng);
    NW_Ds_DarDelete(dsp_ctx->dispatchers);
    NW_Mem_Free(dsp_ctx);
    dsp_ctx = NULL;
    NW_Ctx_Set(NW_CTX_MSG_DSP,0, NULL);
}
