/* file: mbftApi.cpp */

#include "mbftpch.h"

#include "messages.hpp"
#include "mbftapi.hpp"


MBFT_SEND_FILE_INFO *AllocateSendFileInfo(LPSTR pszPathName);
void FreeSendFileInfo(MBFT_SEND_FILE_INFO *);


MBFTInterface::MBFTInterface
(
    IMbftEvents     *pEvents,
    HRESULT         *pHr
)
:
    CRefCount(MAKE_STAMP_ID('I','F','T','I')),
    m_pEvents(pEvents),
    m_pEngine(NULL),
    m_FileHandle(0),
    m_InStateMachine(FALSE),
    m_bFileOfferOK(TRUE),
    m_MBFTUserID(0),
    m_SendEventHandle(0),
    m_ReceiveEventHandle(0)
{
    // register window class first
    WNDCLASS wc;
    ::ZeroMemory(&wc, sizeof(wc));
    // wc.style         = 0;
    wc.lpfnWndProc      = MBFTNotifyWndProc;
    // wc.cbClsExtra    = 0;
    // wc.cbWndExtra    = 0;
    wc.hInstance        = g_hDllInst;
    // wc.hIcon         = NULL;
    // wc.hbrBackground = NULL;
    // wc.hCursor       = NULL;
    // wc.lpszMenuName  = NULL;
    wc.lpszClassName    = g_szMBFTWndClassName;

    ::RegisterClass(&wc);

    // Create a hidden window for notification
    m_hwndNotify = ::CreateWindowA(g_szMBFTWndClassName, NULL, WS_POPUP,
                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                        NULL, NULL, g_hDllInst, NULL);
    if (NULL != m_hwndNotify)
    {
        HANDLE hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
        if (NULL != hEvent)
        {
            if (::PostMessage(g_pFileXferApplet->GetHiddenWnd(), MBFTMSG_CREATE_ENGINE, (WPARAM) hEvent, (LPARAM) this))
            {
				DWORD dwRet = ::WaitForSingleObject(hEvent, 1000);
				ASSERT(WAIT_OBJECT_0 == dwRet);
            }
			else
			{
				WARNING_OUT(("MBFTInterface::MBFTInterface: PostMessage failed, err=%d", ::GetLastError()));
			}
           *pHr = (NULL != m_pEngine) ? S_OK : E_FAIL;
            ::CloseHandle(hEvent);
            return;
        }
    }
    *pHr = E_FAIL;
}


MBFTInterface::~MBFTInterface(void)
{
    if (NULL != m_pEngine)
    {
        m_pEngine->ClearInterfacePointer();
        m_pEngine = NULL;
    }

    if (NULL != m_hwndNotify)
    {
        ::DestroyWindow(m_hwndNotify);
    }
}


void MBFTInterface::ReleaseInterface(void)
{
    Release();
}


void MBFTInterface::Update(void)
{
//    DoStateMachine();
}


HRESULT MBFTInterface::AcceptFileOffer
(
    MBFT_FILE_OFFER    *pOffer,
    LPCSTR              pszRecDir,
    LPCSTR              pszFileName
)
{
    BOOL bAcceptFile = (NULL != pszRecDir) && FEnsureDirExists(pszRecDir);

    DBG_SAVE_FILE_LINE
    return m_pEngine->SafePostMessage(
                new FileTransferControlMsg(
                                pOffer->hEvent,
                                pOffer->lpFileInfoList->hFile,
                                pszRecDir,
                                pszFileName,
                                bAcceptFile ? FileTransferControlMsg::EnumAcceptFile 
                                            : FileTransferControlMsg::EnumRejectFile));
}


void MBFTInterface::RejectFileOffer
(
    MBFT_FILE_OFFER     *pOffer
)
{
    DBG_SAVE_FILE_LINE
    m_pEngine->SafePostMessage(
                new FileTransferControlMsg(
                            pOffer->hEvent,
                            pOffer->lpFileInfoList->hFile,
                            NULL,
                            NULL,
                            FileTransferControlMsg::EnumRejectFile));
}


void MBFTInterface::CancelFt
(
    MBFTEVENTHANDLE     hEvent,
    MBFTFILEHANDLE      hFile
)
{
    DBG_SAVE_FILE_LINE
    m_pEngine->SafePostMessage(
                new FileTransferControlMsg(
                                        hEvent,
                                        hFile,
                                        NULL,
                                        NULL,
                                        FileTransferControlMsg::EnumAbortFile));
}


HRESULT MBFTInterface::SendFile
(
    LPCSTR              lpszFilePath,
	T120NodeID			nidReceiver,
    MBFTEVENTHANDLE    *lpEventHandle,
    MBFTFILEHANDLE     *lpFileHandle
)
{
    if (NULL != m_SendEventHandle)
    {
        // We are waiting for a timeout in a file sent to a 3rd party FT
        // that does not support our file end notification
        return E_PENDING;
    }

#ifdef ENABLE_CONDUCTORSHIP
    if( !m_pEngine->ConductedModeOK() )
    {
        return E_ACCESSDENIED;
    }
#endif

    ::EnterCriticalSection(&g_csWorkThread);

    // set event handle
    *lpEventHandle = ::GetNewEventHandle();

    ::LeaveCriticalSection(&g_csWorkThread);

    DBG_SAVE_FILE_LINE
    HRESULT hr = m_pEngine->SafePostMessage(
                                new CreateSessionMsg(MBFT_PRIVATE_SEND_TYPE, *lpEventHandle));
    if (S_OK == hr)
    {
        *lpFileHandle = ::GetNewFileHandle();

        ULONG cbSize = ::lstrlenA(lpszFilePath)+1;
        DBG_SAVE_FILE_LINE
        LPSTR pszPath = new char[cbSize];
        if (NULL != pszPath)
        {
            ::CopyMemory(pszPath, lpszFilePath, cbSize);

            DBG_SAVE_FILE_LINE
            hr = m_pEngine->SafePostMessage(
                                new SubmitFileSendMsg(
											0, // SDK only knows node ID
											nidReceiver,
                                            pszPath,
                                            *lpFileHandle,
                                            *lpEventHandle,
                                            FALSE));
            if (S_OK == hr)
            {
                m_SendEventHandle = *lpEventHandle;
            }
            else
            {
                delete [] pszPath;
            }
        }
    }

    return hr;
}


void MBFTInterface::DoStateMachine(MBFTMsg *pMsg)
{
    if(!m_InStateMachine)
    {
        BOOL fHeartBeat = FALSE;

        m_InStateMachine = TRUE;

        switch(pMsg->GetMsgType())
        {
        case EnumFileOfferNotifyMsg:
            HandleFileOfferNotify((FileOfferNotifyMsg *) pMsg);
            break;

        case EnumFileTransmitMsg:
            HandleProgressNotify((FileTransmitMsg *) pMsg);
            fHeartBeat = TRUE;
            break;

        case EnumFileErrorMsg:
            HandleErrorNotify((FileErrorMsg *) pMsg);
            break;

        case EnumPeerMsg:
            HandlePeerNotification((PeerMsg *) pMsg);
            break;

        case EnumInitUnInitNotifyMsg:
            HandleInitUninitNotification((InitUnInitNotifyMsg *) pMsg);
            break;

        case EnumFileEventEndNotifyMsg:
            HandleGenericNotification((FileEventEndNotifyMsg *) pMsg);
            break;

        default:
            ASSERT(0);
            break;
        } // switch

        m_InStateMachine = FALSE;

        if (fHeartBeat)
        {
            ::PostMessage(g_pFileXferApplet->GetHiddenWnd(), MBFTMSG_HEART_BEAT, 0, (LPARAM) m_pEngine);
        }
    }
}



void MBFTInterface::HandleFileOfferNotify(FileOfferNotifyMsg * lpNotifyMessage)
{
    if((m_ReceiveEventHandle == 0) || (m_ReceiveEventHandle != lpNotifyMessage->m_EventHandle))
    {
        TRACEAPI(" File Offer Notification for [%s], Event: [%ld], Size: [%ld], Handle [%Fp]\n",
                 lpNotifyMessage->m_szFileName,lpNotifyMessage->m_EventHandle,
                 lpNotifyMessage->m_FileSize,lpNotifyMessage->m_hFile);        

        MBFT_FILE_OFFER NewFileOffer;
        MBFT_RECEIVE_FILE_INFO FileData;
                                
        NewFileOffer.hEvent          = lpNotifyMessage->m_EventHandle;
        NewFileOffer.SenderID        = lpNotifyMessage->m_SenderID;
		NewFileOffer.NodeID          = lpNotifyMessage->m_NodeID;
        NewFileOffer.uNumFiles       = 1;
        NewFileOffer.lpFileInfoList  = &FileData;  
        NewFileOffer.bIsBroadcastEvent   = !lpNotifyMessage->m_bAckNeeded;  
                                
        ::lstrcpynA(FileData.szFileName, lpNotifyMessage->m_szFileName, sizeof(FileData.szFileName));
        FileData.hFile               = lpNotifyMessage->m_hFile;
        FileData.lFileSize           = lpNotifyMessage->m_FileSize;  
        FileData.FileDateTime        = lpNotifyMessage->m_FileDateTime;

        m_pEvents->OnFileOffer(&NewFileOffer);
    }
}                            

void MBFTInterface::HandleProgressNotify(FileTransmitMsg * lpProgressMessage)
{
    MBFT_NOTIFICATION wMBFTCode = (MBFT_NOTIFICATION)lpProgressMessage->m_TransmitStatus;

    TRACEAPI(" Notification [%x] from Event [%ld], Handle: [%ld] FileSize: [%ld], Bytes Xfered[%ld]\n",
             wMBFTCode,lpProgressMessage->m_EventHandle,
             lpProgressMessage->m_hFile,
             lpProgressMessage->m_FileSize,
             lpProgressMessage->m_BytesTransmitted);

    switch (wMBFTCode)
    {
    case iMBFT_FILE_RECEIVE_BEGIN:
        if(!m_ReceiveEventHandle)
        {
            m_ReceiveEventHandle = lpProgressMessage->m_EventHandle;
        }            
        //m_bFileOfferOK = FALSE;
        break;

    case iMBFT_FILE_RECEIVE_PROGRESS:
    case iMBFT_FILE_SEND_PROGRESS:
        {
            MBFT_FILE_PROGRESS Progress;
            Progress.hEvent             = lpProgressMessage->m_EventHandle;
            Progress.hFile              = lpProgressMessage->m_hFile;
            Progress.lFileSize          = lpProgressMessage->m_FileSize;
            Progress.lBytesTransmitted  = lpProgressMessage->m_BytesTransmitted;
            Progress.bIsBroadcastEvent  = lpProgressMessage->m_bIsBroadcastEvent;

            m_pEvents->OnFileProgress(&Progress);
        }
        break;

    case iMBFT_FILE_RECEIVE_END:
        //m_bFileOfferOK = TRUE;   
        if(m_ReceiveEventHandle == lpProgressMessage->m_EventHandle)
        {
            m_ReceiveEventHandle = 0;
        }
        // fall through
    case iMBFT_FILE_SEND_END:
        m_pEvents->OnFileEnd(lpProgressMessage->m_hFile);
        break;

    default:
        ASSERT(iMBFT_FILE_SEND_BEGIN == wMBFTCode);
        break;
    }
}    
    
void MBFTInterface::HandleErrorNotify(FileErrorMsg * lpErrorMessage) 
{

    TRACEAPI(" Error Notification, Event: [%ld], Handle [%ld], IsLocal = [%d]\n",
             lpErrorMessage->m_EventHandle,lpErrorMessage->m_hFile,
             lpErrorMessage->m_bIsLocalError);        

    
    MBFT_EVENT_ERROR Error;
        
    Error.hEvent         = lpErrorMessage->m_EventHandle;
    Error.bIsLocalError  = lpErrorMessage->m_bIsLocalError;
    Error.UserID         = lpErrorMessage->m_UserID;                            
    Error.hFile          = lpErrorMessage->m_hFile;      
    Error.bIsBroadcastEvent = lpErrorMessage->m_bIsBroadcastEvent;
    
    if(LOWORD(Error.hFile) == LOWORD(_iMBFT_PROSHARE_ALL_FILES))
    {
        Error.hFile = _iMBFT_PROSHARE_ALL_FILES;
    }

    Error.eErrorType     = (MBFT_ERROR_TYPES)lpErrorMessage->m_ErrorType;
    Error.eErrorCode     = (MBFT_ERROR_CODE)lpErrorMessage->m_ErrorCode;  
    
    m_pEvents->OnFileError(&Error);
}                            

void MBFTInterface::HandlePeerNotification(PeerMsg * lpNewMessage)
{

    TRACEAPI(" Peer Notification, Node [%u], UserID [%u], IsProshare = [%u], Added = [%u]\n",
             lpNewMessage->m_NodeID,
             lpNewMessage->m_MBFTPeerID,lpNewMessage->m_bIsProsharePeer,
             lpNewMessage->m_bPeerAdded);        

    
    MBFT_PEER_INFO PeerInfo;

    PeerInfo.NodeID          = lpNewMessage->m_NodeID;  
    PeerInfo.MBFTPeerID      = lpNewMessage->m_MBFTPeerID;  
    PeerInfo.bIsProShareApp  = lpNewMessage->m_bIsProsharePeer;
    PeerInfo.MBFTSessionID   = lpNewMessage->m_MBFTSessionID;
    
    //PeerInfo.bIsLocalPeer    = lpNewMessage->m_bIsLocalPeer;
    
    ::lstrcpynA(PeerInfo.szAppKey,lpNewMessage->m_szAppKey, sizeof(PeerInfo.szAppKey));

    //lstrcpyn(PeerInfo.szProtocolKey,lpNewMessage->m_szProtocolKey, sizeof(PeerInfo.szProtocolKey));

    if(!lpNewMessage->m_bIsLocalPeer)
    {
        TRACEAPI("Delivering PEER Notification\n");
        if (lpNewMessage->m_bPeerAdded)
        {
            m_pEvents->OnPeerAdded(&PeerInfo);
        }
        else
        {
            m_pEvents->OnPeerRemoved(&PeerInfo);
        }
    }  
    
    if(lpNewMessage->m_bIsLocalPeer) 
    {
        if(lpNewMessage->m_bPeerAdded)
        {
            m_MBFTUserID    = PeerInfo.MBFTPeerID;
                
            m_pEvents->OnInitializeComplete();
        }
    }
}


void MBFTInterface::HandleInitUninitNotification(InitUnInitNotifyMsg * lpNewMessage)
{
    if (lpNewMessage->m_iNotifyMessage == EnumInvoluntaryUnInit)
    {
        if (NULL != m_pEvents)
        {
            m_pEvents->OnSessionEnd();
        }
    }
}

void MBFTInterface::HandleGenericNotification(FileEventEndNotifyMsg * lpNewMessage)
{
    if (m_SendEventHandle == lpNewMessage->m_EventHandle)
    {
        m_SendEventHandle  = 0;
    }
    m_pEvents->OnFileEventEnd(lpNewMessage->m_EventHandle);
}



MBFT_SEND_FILE_INFO *AllocateSendFileInfo(LPSTR pszPathName)
{
    MBFT_SEND_FILE_INFO *p = new MBFT_SEND_FILE_INFO;
    if (NULL != p)
    {
        ::ZeroMemory(p, sizeof(*p));
        ULONG cb = ::lstrlenA(pszPathName) + 1;
        p->lpszFilePath = new char[cb];
        if (NULL != p->lpszFilePath)
        {
            ::CopyMemory(p->lpszFilePath, pszPathName, cb);
#ifdef BUG_INTL
            ::AnsiToOem(p->lpszFilePath, p->lpszFilePath);
#endif /* BUG_INTL */
        }
        else
        {
            delete p;
            p = NULL;
        }
    }
    return p;
}

void FreeSendFileInfo(MBFT_SEND_FILE_INFO *p)
{
    if (NULL != p)
    {
        delete p->lpszFilePath;
        delete p;
    }
}


HRESULT MBFTInterface::SafePostNotifyMessage(MBFTMsg *pMsg)
{
    if (NULL != pMsg)
    {
        AddRef();
        ::PostMessage(m_hwndNotify, MBFTNOTIFY_BASIC, (WPARAM) pMsg, (LPARAM) this);
        return S_OK;
    }
    ERROR_OUT(("MBFTInterface::SafePostNotifyMessage: null msg ptr"));
    return E_OUTOFMEMORY;
}


LRESULT CALLBACK
MBFTNotifyWndProc
(
    HWND            hwnd,
    UINT            uMsg,
    WPARAM          wParam,
    LPARAM          lParam
)
{
    switch (uMsg)
    {
    case MBFTNOTIFY_BASIC:
        {
            MBFTInterface *pIntf = (MBFTInterface *) lParam;
            MBFTMsg *pMsg = (MBFTMsg *) wParam;
            ASSERT(NULL != pIntf);
            ASSERT(NULL != pMsg);
            pIntf->DoStateMachine(pMsg);
            delete pMsg;
            pIntf->Release();
        }
        break;

    case WM_CLOSE:
        ::DestroyWindow(hwnd);
        break;

    default:
        return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return 0;
}