// @(#)root/win32gdk:$Name:  $:$Id: TGWin32ProxyBase.cxx,v 1.14 2004/05/10 15:06:35 brun Exp $
// Author: Valeriy Onuchin  08/08/2003

/*************************************************************************
 * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//
// Proxy classes provide thread-safe interface to global objects.
//
// For example: TGWin32VirtualXProxy (to gVirtualX),  
//              TGWin32InterpreterProxy (to gInterpreter).
//
// Proxy object creates callback object and posts a windows message to 
// "processing thread". When windows message is received callback 
// ("real method") is executed.
// 
// For example: 
//    gVirtualX->ClearWindow()
//
//    - callback object created containing pointer to function
//      corresponding TGWin32::ClearWindow() method
//    - message to "processing thread" (main thread) is posted
//    - TGWin32::ClearWindow() method is executed inside main thread
//    - thread containing gVirtualX proxy object waits for reply
//      from main thread that TGWin32::ClearWindow() is completed. 
//
// Howto create proxy class:
//
//  1. Naming. 
//       name of proxy = TGWin32 + the name of "virtual base class" + Proxy
//
//       e.g. TGWin32VirtualXProxy = TGWin32 + VirtualX + Proxy
//
//  2. Definition of global object
//       As example check definition and implementation of 
//       gVirtualX, gInterpreter global objects
//
//  3. Class definition.
//       proxy class must be inherited from "virtual base class" and
//       TGWin32ProxyBase class. For example:
//
//       class TGWin32VirtualX : public TVirtualX , public  TGWin32ProxyBase
//
//  4. Constructors, destructor, extra methods.
//     - constructors and destructor of proxy class do nothing
//     - proxy class must contain two extra static methods 
//       RealObject(), ProxyObject(). Each of them return pointer to object
//       of virtual base class.
//
//     For example:
//       static TInterpreter *RealObject();
//       static TInterpreter *ProxyObject();
//
//  5. Implementation
//       TGWin32ProxyDefs.h file contains a set of macros which very
//       simplify implementation.
//     - RETURN_PROXY_OBJECT macro implements ProxyObject() method, e.g.
//       RETURN_PROXY_OBJECT(Interpreter)         
//     - the names of other macros say about itself.
//
//       For example:
//          VOID_METHOD_ARG0(Interpreter,ClearFileBusy,1)
//             void TGWin32InterpreterProxy::ClearFileBusy()
//  
//          RETURN_METHOD_ARG0_CONST(VirtualX,Visual_t,GetVisual)
//             Visual_t TGWin32VirtualXProxy::GetVisual() const
//
//          RETURN_METHOD_ARG2(VirtualX,Int_t,OpenPixmap,UInt_t,w,UInt_t,h)
//             Int_t TGWin32VirtualXProxy::OpenPixmap,UInt_t w,UInt_t h)
//
//     - few methods has _LOCK part in the name
//          VOID_METHOD_ARG1_LOCK(Interpreter,CreateListOfMethods,TClass*,cl)
//   
//
///////////////////////////////////////////////////////////////////////////////

#include "Windows4Root.h"
#include <windows.h>

#include "TGWin32ProxyBase.h"
#include "TRefCnt.h"
#include "TList.h"
#include "TGWin32.h"


////////////////////////////////////////////////////////////////////////////////
class TGWin32CallBackObject : public TObject {
public:
   TGWin32CallBack   fCallBack;  // callback function (called by GUI thread)
   void              *fParam;    // arguments passed to/from callback function

   TGWin32CallBackObject(TGWin32CallBack cb,void *p):fCallBack(cb),fParam(p) {}
   ~TGWin32CallBackObject() { if (fParam) delete fParam; }
};

////////////////////////////////////////////////////////////////////////////////
class TGWin32ProxyBasePrivate {
public:
   HANDLE   fEvent;   // event used for syncronization
   TGWin32ProxyBasePrivate();
   ~TGWin32ProxyBasePrivate();
};

//______________________________________________________________________________
TGWin32ProxyBasePrivate::TGWin32ProxyBasePrivate()
{
   // ctor

   fEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}

//______________________________________________________________________________
TGWin32ProxyBasePrivate::~TGWin32ProxyBasePrivate()
{
   // dtor

   if (fEvent) ::CloseHandle(fEvent);
   fEvent = 0;
}


ULong_t TGWin32ProxyBase::fgPostMessageId = 0;
ULong_t TGWin32ProxyBase::fgPingMessageId = 0;
ULong_t TGWin32ProxyBase::fgMainThreadId = 0;
Long_t TGWin32ProxyBase::fgLock = 0;

////////////////////////////////////////////////////////////////////////////////
//______________________________________________________________________________
TGWin32ProxyBase::TGWin32ProxyBase()
{
   // ctor

   fCallBack = 0;
   fParam = 0;
   fListOfCallBacks = new TList();
   fBatchLimit = 100;
   fId = ::GetCurrentThreadId();
   fPimpl = new TGWin32ProxyBasePrivate();

   if (!fgPostMessageId) fgPostMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Post");
   if (!fgPingMessageId) fgPingMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Ping");
}

//______________________________________________________________________________
TGWin32ProxyBase::~TGWin32ProxyBase()
{
   // dtor

   fListOfCallBacks->Delete();
   delete fListOfCallBacks;
   fListOfCallBacks = 0;

   delete fPimpl;
}

//______________________________________________________________________________
void TGWin32ProxyBase::Lock()
{
   // enter critical section

   TGWin32::Lock();
}

//______________________________________________________________________________
void TGWin32ProxyBase::Unlock()
{
   // leave critical section

   TGWin32::Unlock();
}

//______________________________________________________________________________
void TGWin32ProxyBase::GlobalLock()
{
   // lock any proxy (client thread)

   if (IsGloballyLocked()) return;
   ::InterlockedIncrement(&fgLock);
}

//______________________________________________________________________________
void TGWin32ProxyBase::GlobalUnlock()
{
   //  unlock any proxy (client thread)

   if (!IsGloballyLocked()) return;
   ::InterlockedDecrement(&fgLock);
}

//______________________________________________________________________________
Bool_t TGWin32ProxyBase::Ping()
{
   // send ping messsage to server thread

   return ::PostThreadMessage(fgMainThreadId, fgPingMessageId, (WPARAM)0, 0L);
}

//______________________________________________________________________________
Double_t TGWin32ProxyBase::GetMilliSeconds()
{
   // returns elapsed time in milliseconds with microseconds precision

   static LARGE_INTEGER freq;
   static Bool_t first = kTRUE;
   LARGE_INTEGER count;
   static Double_t overhead = 0;

   if (first) {
      LARGE_INTEGER count0;
      ::QueryPerformanceFrequency(&freq);
      ::QueryPerformanceCounter(&count0);
      if (1) {
         Double_t dummy;
         dummy = ((Double_t)count0.QuadPart - overhead)*1000./((Double_t)freq.QuadPart);
      }
      ::QueryPerformanceCounter(&count);
      overhead = (Double_t)count.QuadPart - (Double_t)count0.QuadPart;
      first = kFALSE;
   }

   ::QueryPerformanceCounter(&count);
   return ((Double_t)count.QuadPart - overhead)*1000./((Double_t)freq.QuadPart);
}

//______________________________________________________________________________
void TGWin32ProxyBase::ExecuteCallBack(Bool_t sync)
{
   // Executes all batched callbacks and the latest callback
   // This method is executed by server thread

   // process batched callbacks
   if (fListOfCallBacks && fListOfCallBacks->GetSize()) {
      TIter next(fListOfCallBacks);
      TGWin32CallBackObject *obj;

      while ((obj = (TGWin32CallBackObject*)next())) {
         obj->fCallBack(obj->fParam);  // execute callback
      }
   }
   if (sync) {
      if (fCallBack) fCallBack(fParam);
      ::SetEvent(fPimpl->fEvent);
   }
}

//______________________________________________________________________________
Bool_t TGWin32ProxyBase::ForwardCallBack(Bool_t sync)
{
   // if sync is kTRUE:
   //    - post message to main thread.
   //    - execute callbacks from fListOfCallBacks
   //    - wait for response
   // else
   //    -  add callback to fListOfCallBacks
   //
   // returns kTRUE if callback execution is delayed (batched)

   Int_t wait = 0;

   if (!fgMainThreadId) return kFALSE;

   while (IsGloballyLocked()) {
      Ping();
      ::SleepEx(10, 1); // take a rest
      if (!fgMainThreadId) return kFALSE; // server thread terminated 
   }

   Bool_t batch = !sync &&  (fListOfCallBacks->GetSize()<fBatchLimit);

   if (batch) {
      fListOfCallBacks->Add(new TGWin32CallBackObject(fCallBack, fParam));
      return kTRUE;
   }

   while (!::PostThreadMessage(fgMainThreadId, fgPostMessageId, (WPARAM)this, 0L)) {
      // wait because there is a chance that message queue does not exist yet
      ::SleepEx(50, 1);
      if (wait++ > 5) return kFALSE; // failed to post
   }

   // limiting wait time
   DWORD res = ::WaitForSingleObject(fPimpl->fEvent, fMaxResponseTime);
   ::ResetEvent(fPimpl->fEvent);

   if (res == WAIT_TIMEOUT) { // server thread is blocked
      GlobalLock();
      return kTRUE;    
   }

   fListOfCallBacks->Delete();
   return kFALSE;
}

//______________________________________________________________________________
void TGWin32ProxyBase::SendExitMessage()
{
   // send exit message to server thread

   ::PostThreadMessage(fgMainThreadId, WM_QUIT, 0, 0L);
}
