#include "stdafx.h"
#ifdef ACTIVE_EDITOR

#include "acp_base.h"
#include <sys/timeb.h>
#include "itf/CPAProj.hpp"
#include "itf/DEVMul3D.hpp"
#include "itf/CPADD.hpp"
#include "itf/CPARes.h"

// For rectangles
static int gs_iNumOfRects = 0;
typedef struct DD_tdstRect_
{
    RECT    stRect;
    HWND    hWnd;
} DD_tdstRect;
#define DD_C_MaxRects   50
static DD_tdstRect  gs_a_stRects[DD_C_MaxRects];

// for timer
UINT gs_uiTimer;
UINT gs_uiElapseScroll = 500;
int  gs_iRect = -1;

//==================================================================
//                          REFERENCES
//==================================================================

// Search a reference to a window in array of all references
BOOL DD_bSearchReference(HWND hWnd, char *p_szName, UINT uiDataType, BOOL *p_bCopy)
{
    int     iIndex;

    for(iIndex = 0; iIndex < M_GetMainApp()->m_iNumRef; iIndex++)
    {
        if
        (
               (M_GetMainApp()->m_a_WndRef[iIndex].hWnd == hWnd)
            && (!strcmpi(p_szName, M_GetMainApp()->m_a_WndRef[iIndex].p_szEditorName))
            && (M_GetMainApp()->m_a_WndRef[iIndex].uiDataType == uiDataType)
        )
        {
            *p_bCopy = M_GetMainApp()->m_a_WndRef[iIndex].bCopyOnly;
            return TRUE;
        }
    }
    return FALSE;
}

// Add a reference to a window that can receive a dragdrop object
tdeDDError DD_eAddReference(HWND hWnd, char *p_szName, UINT uiDataType, BOOL bCopy)
{
    BOOL    bTemp;

    // Too much references already define
    if(M_GetMainApp()->m_iNumRef == C_ucMaxCWndRef)
        return E_DDERR_TooMuchRef;

    // Reference always exists
    if(DD_bSearchReference(hWnd, p_szName, uiDataType, &bTemp))
        return E_DDERR_None;

    // Add reference
    M_GetMainApp()->m_a_WndRef[M_GetMainApp()->m_iNumRef].hWnd = hWnd;
    M_GetMainApp()->m_a_WndRef[M_GetMainApp()->m_iNumRef].p_szEditorName = p_szName;
    M_GetMainApp()->m_a_WndRef[M_GetMainApp()->m_iNumRef].uiDataType = uiDataType;
    M_GetMainApp()->m_a_WndRef[M_GetMainApp()->m_iNumRef++].bCopyOnly = bCopy;
    return E_DDERR_None;
}

// Delete a reference to a window (all types)
tdeDDError DD_eDelReference(HWND hWnd)
{
    int     iIndex;
    BOOL    bOk = FALSE;

    for(iIndex = 0; iIndex < M_GetMainApp()->m_iNumRef; iIndex++)
    {
        if(M_GetMainApp()->m_a_WndRef[iIndex].hWnd == hWnd)
        {
            memmove(&M_GetMainApp()->m_a_WndRef[iIndex], &M_GetMainApp()->m_a_WndRef[iIndex+1], M_GetMainApp()->m_iNumRef - iIndex);
            M_GetMainApp()->m_iNumRef--;
            bOk = TRUE;
        }
    }
    if(bOk)
        return E_DDERR_None;
    else
        return E_DDERR_UnknownRef;
}

// Delete a reference to a window with a special type
tdeDDError DD_eDelReferenceType(HWND hWnd, char *p_szName, UINT uiDataType)
{
    int     iIndex;

    for(iIndex = 0; iIndex < M_GetMainApp()->m_iNumRef; iIndex++)
    {
        if
        (
               (M_GetMainApp()->m_a_WndRef[iIndex].hWnd == hWnd)
            && (!strcmpi(p_szName, M_GetMainApp()->m_a_WndRef[iIndex].p_szEditorName))
            && (M_GetMainApp()->m_a_WndRef[iIndex].uiDataType == uiDataType)
        )
        {
            memmove(&M_GetMainApp()->m_a_WndRef[iIndex], &M_GetMainApp()->m_a_WndRef[iIndex+1], M_GetMainApp()->m_iNumRef - iIndex);
            M_GetMainApp()->m_iNumRef--;
            return E_DDERR_None;
        }
    }
    return E_DDERR_UnknownRef;
}

// Delete all references
void DD_vDelAllReferences(void)
{
    M_GetMainApp()->m_iNumRef = 0;
}

//==================================================================
// Drag & Drop
//==================================================================

// Determins what type of mouse pointer must be drawn regardless to window
// under mouse.
// Set M_GetMainApp()->m_bCanDrop global variable.
void DD_vCanDrop(HWND hWnd, char *p_szEditorName, UINT uiDataType)
{
	HCURSOR	hcursor;
    BOOL    bCopy;

    // Is Window reference ?
    if(hWnd && (DD_bSearchReference(hWnd, p_szEditorName, uiDataType, &bCopy)))
    {
        M_GetMainApp()->m_bCanDrop = TRUE;

        // Copy if CONTROL key is pressed or if window only accept copy objects
        if(M_GetMainApp()->m_bControl || bCopy)
        {
		    hcursor = AfxGetApp()->LoadCursor(CPA_IDC_DRAGCOPY);
            M_GetMainApp()->m_bCopy = TRUE;
        }
        else
        {
		    hcursor = AfxGetApp()->LoadCursor(CPA_IDC_DRAGMOVE);
            M_GetMainApp()->m_bCopy = FALSE;
        }
    }
    else
    {
        M_GetMainApp()->m_bCanDrop = FALSE;
        M_GetMainApp()->m_bCopy = FALSE;
		hcursor = AfxGetApp()->LoadCursor(CPA_IDC_DRAGNONE);
	}

	::SetCursor(hcursor);
}

// Test if p_Wnd is loosing or gaining the focus of drag & drop
// That function is call only if cursor pos has changed
void DD_vDeterminsFocus(HWND hWnd, DD_Object *p_stDDObject)
{
    if (hWnd != M_GetMainApp()->m_LastFocusWnd)
    {
        // Send a loosing focus message to last window
        if(M_GetMainApp()->m_LastFocusWnd)
            ::SendMessage(M_GetMainApp()->m_LastFocusWnd, WM_DRAGDROP_LOOSEFOCUS, 0, (LPARAM) p_stDDObject);

        // If there's a new window that accept drop, we send a message to it
        if(hWnd && M_GetMainApp()->m_bCanDrop)
            ::SendMessage(hWnd, WM_DRAGDROP_GAINFOCUS, 0, (LPARAM) p_stDDObject);
    }
    else if (hWnd)
    {
        // Mouse move in the same window than last one
        ::SendMessage(hWnd, WM_DRAGDROP_MOVE, 0, (LPARAM) p_stDDObject);
    }

    // Last focus window is now new one
    M_GetMainApp()->m_LastFocusWnd = hWnd;
}

// Get mouse position with a LPARAM parameter
// Set m_stMousePos with current value of mouse position
void DD_vGetMousePos(LPARAM lParam, DD_Object *p_stDDObject)
{
	GetCursorPos(&M_GetMainApp()->m_stMousePos);
/*    M_GetMainApp()->m_stMousePos.x = LOWORD(lParam);
    M_GetMainApp()->m_stMousePos.y = HIWORD(lParam);
    p_stDDObject->fn_p_stGetOwner()->ClientToScreen(&M_GetMainApp()->m_stMousePos);*/
    p_stDDObject->m_stMousePos.x = M_GetMainApp()->m_stMousePos.x;
    p_stDDObject->m_stMousePos.y = M_GetMainApp()->m_stMousePos.y;
}

// Finish a drag & drop operation
// Send a message to current window if it accepts drag & drop
// Restore mouse cursor
void DD_vFinish(DD_Object *p_stDDObject)
{
    // Send a end of drag & drop message to current window if it's possible, else
    // send a loose focus
    if(M_GetMainApp()->m_bCanDrop)
        ::SendMessage(M_GetMainApp()->m_LastFocusWnd, WM_DRAGDROP_END, (WPARAM) M_GetMainApp()->m_bCopy, (LPARAM) p_stDDObject);
    else
        ::SendMessage(M_GetMainApp()->m_LastFocusWnd, WM_DRAGDROP_LOOSEFOCUS, 0, (LPARAM) p_stDDObject);

    // Restore initial mouse cursor
    ::SetCursor(M_GetMainApp()->m_hFirstCursor);
}

// Register a special rectangle
void DD_vRegisterRect(RECT *p_stRect, HWND hWnd)
{
    if(gs_iNumOfRects == DD_C_MaxRects)
        return;
    memcpy(&gs_a_stRects[gs_iNumOfRects].stRect, p_stRect, sizeof(RECT));
    gs_a_stRects[gs_iNumOfRects++].hWnd = hWnd;
}

// change timer elapse time
void DD_vSetScrollTime( UINT uiTime )
{
	gs_uiElapseScroll = uiTime;
}

// Determins if mouse is in a rect
int fn_iIsInRect(void)
{
    int i;

    for(i = 0; i < gs_iNumOfRects; i++)
    {
        if(gs_a_stRects[i].hWnd == M_GetMainApp()->m_LastFocusWnd)
        {
            POINT   pt;
            RECT    *r = &gs_a_stRects[i].stRect;

            GetCursorPos(&pt);
            if((pt.x >= r->left ) && (pt.x <= r->right) && (pt.y >= r->top) && (pt.y <= r->bottom))
                return i;
        }
    }
    return gs_iNumOfRects;
}

// Treat registered rectangles
void fn_vTreatRects(DD_Object *p_stDDObject)
{
	int iRes;

    iRes = fn_iIsInRect();
    if(iRes < gs_iNumOfRects)
    {
		if (gs_iRect == -1)
		{
			gs_uiTimer = SetTimer(NULL, 1, gs_uiElapseScroll, NULL );
			gs_iRect = iRes;
		}
    }
	else
	{
		if (gs_iRect != -1)
		{
			KillTimer( NULL, gs_uiTimer );
			gs_iRect = -1;
		}
	}
}

// Begin a drag & drop operation
// p_Rect :     Rect to begin drag & drop
// p_stObject : Object to transmit
HWND DD_hBeginDragDrop(RECT *p_Rect, DD_Object *p_stDDObject)
{
 BOOL    bOutOfRect = FALSE;
 BOOL    bEndOfLoop = FALSE;
 BOOL    bCanDispatch;
 MSG     stMsg;

 // Inits
 //------
 M_GetMainApp()->m_bCanDrop = FALSE;
 M_GetMainApp()->m_bCopy = FALSE;
 M_GetMainApp()->m_bControl = FALSE;
 M_GetMainApp()->m_hFirstCursor = ::GetCursor();

 // Wait mouse to go out of first rect area
 //----------------------------------------
	while (bEndOfLoop == FALSE)
	{
  ::GetMessage(&stMsg, NULL, 0, 0);

  switch(stMsg.message)
  {
  case WM_MOUSEMOVE:
   DD_vGetMousePos(stMsg.lParam, p_stDDObject);
   if (!::PtInRect(p_Rect, M_GetMainApp()->m_stMousePos))
   {
    bOutOfRect = TRUE;
    bEndOfLoop = TRUE;
    break;
   }
   break;

  case WM_LBUTTONUP:
   bEndOfLoop = TRUE;
   break;
  }

  ::TranslateMessage(&stMsg);
  ::DispatchMessage(&stMsg);
	}

 // A drag & drop is beginnig if bOutOfRect is set to TRUE
 //-------------------------------------------------------
 if (bOutOfRect)
 {
  // Source window capture mouse
  //----------------------------
  ASSERT(p_stDDObject->fn_p_stGetOwner() != NULL);
  p_stDDObject->m_p_oGrabCaptureFor(p_stDDObject->fn_p_stGetOwner());

  bEndOfLoop = FALSE;
  // The last wnd under mouse is window that owns object
  M_GetMainApp()->m_LastFocusWnd = p_stDDObject->fn_p_stGetOwner()->GetSafeHwnd();
  while(bEndOfLoop == FALSE)
  {
   HWND        hWndUnder;          // Current window under mouse

   ::GetMessage(&stMsg, NULL, 0, 0);
   bCanDispatch = TRUE;    // Can dispatch messages ?
   switch(stMsg.message)
   {
   case WM_TIMER:
    if (stMsg.wParam == gs_uiTimer)
    {
     // we are in a scroll rect, send DRAGDROP_SCROLL message
     ::SendMessage(M_GetMainApp()->m_LastFocusWnd, WM_DRAGDROP_SCROLL, 0, (LPARAM) &gs_a_stRects[gs_iRect].stRect);
     // Don't dispatch the message
     bCanDispatch = FALSE;
    }
				break;

   case WM_MOUSEMOVE:
    // Coords of mouse
    DD_vGetMousePos(stMsg.lParam, p_stDDObject);
    // Get window under mouse
	hWndUnder = WindowFromPoint(M_GetMainApp()->m_stMousePos);
	// specific for 2nd screen
    if (M_GetMainApp()->GetInterface()->fn_bGetAutomaticSwap())
	{
		// get active device & viewport
		DEV_MultiDevice3D * pMulti = (DEV_MultiDevice3D *) M_GetMainDevice2();
		DEV_ViewPort3D * pViewport = (DEV_ViewPort3D *) M_GetActiveViewport(pMulti);
		// call this function to detect swap
		pViewport->DetectMouseSwap();
		// update active window
		if (!M_GetMainApp()->m_bCursorInFirstScreen)
			hWndUnder = pViewport->GetSafeHwnd();
		else if (hWndUnder == pViewport->GetSafeHwnd())
			hWndUnder = NULL;
	}
    // Window under mouse accept drag & drop ?
    DD_vCanDrop(hWndUnder, p_stDDObject->fn_p_szGetEditorName(), p_stDDObject->fn_uiGetDataType());
    // Treat focus messages
    DD_vDeterminsFocus(hWndUnder, p_stDDObject);
    // Don't dispatch message
    bCanDispatch = FALSE;
    break;

   case WM_RBUTTONDOWN:
    M_GetMainApp()->m_bCanDrop = FALSE;
    DD_vFinish(p_stDDObject);
    bEndOfLoop = TRUE;
    // Don't dispatch message
    bCanDispatch = FALSE;
    break;

   case WM_LBUTTONUP:
    DD_vFinish(p_stDDObject);
    bEndOfLoop = TRUE;
    // Don't dispatch message
    bCanDispatch = FALSE;
    break;

   // For copying object instead of moving it
   case WM_KEYDOWN:
    if(stMsg.wParam == VK_CONTROL)
    {
     M_GetMainApp()->m_bControl = TRUE;
     // To redraw mouse cursor
     DD_vCanDrop(M_GetMainApp()->m_LastFocusWnd, p_stDDObject->fn_p_szGetEditorName(), p_stDDObject->fn_uiGetDataType());
     // Don't dispatch message
     bCanDispatch = FALSE;
    }
    break;

   case WM_KEYUP:
    if(stMsg.wParam == VK_CONTROL)
    {
     M_GetMainApp()->m_bControl = FALSE;
     DD_vCanDrop(M_GetMainApp()->m_LastFocusWnd, p_stDDObject->fn_p_szGetEditorName(), p_stDDObject->fn_uiGetDataType());
     // Don't dispatch message
     bCanDispatch = FALSE;
    }
    break;
   }

   // Can we dispatch message ?
   if(bCanDispatch)
   {
    ::TranslateMessage(&stMsg);
    ::DispatchMessage(&stMsg);
   }

   // Treat registered rectangles
   if(bEndOfLoop == FALSE)
    fn_vTreatRects(p_stDDObject);
  }

  // We restore mouse capture
  //-------------------------
  p_stDDObject->m_p_oSurrenderCapture();
 }

	// No rects
 gs_iNumOfRects = 0;

	// set elapse time to default value
	gs_uiElapseScroll = 500;

	// kill timer if still in a rectangle
	if (gs_iRect != -1)
	{
		KillTimer( NULL, gs_uiTimer );
		gs_iRect = -1;
	}

 // Return handle of window that accepted Drag & Drop
 if(M_GetMainApp()->m_bCanDrop)
  return M_GetMainApp()->m_LastFocusWnd;
 else
  return NULL;
}
#endif // ACTIVE_EDITOR
