/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 *  Communicate with a peer using NFC-DEP, LLCP, SNEP.
 */
#pragma once
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include "SyncEvent.h"
#include "NfcJniUtil.h"
#include <string>
extern "C"
{
    #include "nfa_p2p_api.h"
}

class P2pServer;
class P2pClient;
class NfaConn;
#define MAX_NFA_CONNS_PER_SERVER    5

/*****************************************************************************
**
**  Name:           PeerToPeer
**
**  Description:    Communicate with a peer using NFC-DEP, LLCP, SNEP.
**
*****************************************************************************/
class PeerToPeer
{
public:
    typedef unsigned int tJNI_HANDLE;

    /*******************************************************************************
    **
    ** Function:        PeerToPeer
    **
    ** Description:     Initialize member variables.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    PeerToPeer ();


    /*******************************************************************************
    **
    ** Function:        ~PeerToPeer
    **
    ** Description:     Free all resources.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    ~PeerToPeer ();


    /*******************************************************************************
    **
    ** Function:        getInstance
    **
    ** Description:     Get the singleton PeerToPeer object.
    **
    ** Returns:         Singleton PeerToPeer object.
    **
    *******************************************************************************/
    static PeerToPeer& getInstance();


    /*******************************************************************************
    **
    ** Function:        initialize
    **
    ** Description:     Initialize member variables.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void initialize ();


    /*******************************************************************************
    **
    ** Function:        llcpActivatedHandler
    **
    ** Description:     Receive LLLCP-activated event from stack.
    **                  nat: JVM-related data.
    **                  activated: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void llcpActivatedHandler (nfc_jni_native_data* nativeData, tNFA_LLCP_ACTIVATED& activated);


    /*******************************************************************************
    **
    ** Function:        llcpDeactivatedHandler
    **
    ** Description:     Receive LLLCP-deactivated event from stack.
    **                  nat: JVM-related data.
    **                  deactivated: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void llcpDeactivatedHandler (nfc_jni_native_data* nativeData, tNFA_LLCP_DEACTIVATED& deactivated);

    void llcpFirstPacketHandler(nfc_jni_native_data* nativeData);

    /*******************************************************************************
    **
    ** Function:        connectionEventHandler
    **
    ** Description:     Receive events from the stack.
    **                  event: Event code.
    **                  eventData: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* eventData);


    /*******************************************************************************
    **
    ** Function:        registerServer
    **
    ** Description:     Let a server start listening for peer's connection request.
    **                  jniHandle: Connection handle.
    **                  serviceName: Server's service name.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool registerServer (tJNI_HANDLE jniHandle, const char* serviceName);


    /*******************************************************************************
    **
    ** Function:        deregisterServer
    **
    ** Description:     Stop a P2pServer from listening for peer.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool deregisterServer (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        accept
    **
    ** Description:     Accept a peer's request to connect.
    **                  serverJniHandle: Server's handle.
    **                  connJniHandle: Connection handle.
    **                  maxInfoUnit: Maximum information unit.
    **                  recvWindow: Receive window size.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool accept (tJNI_HANDLE serverJniHandle, tJNI_HANDLE connJniHandle, int maxInfoUnit, int recvWindow);


    /*******************************************************************************
    **
    ** Function:        createClient
    **
    ** Description:     Create a P2pClient object for a new out-bound connection.
    **                  jniHandle: Connection handle.
    **                  miu: Maximum information unit.
    **                  rw: Receive window size.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool createClient (tJNI_HANDLE jniHandle, UINT16 miu, UINT8 rw);


    /*******************************************************************************
    **
    ** Function:        connectConnOriented
    **
    ** Description:     Establish a connection-oriented connection to a peer.
    **                  jniHandle: Connection handle.
    **                  serviceName: Peer's service name.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool connectConnOriented (tJNI_HANDLE jniHandle, const char* serviceName);


    /*******************************************************************************
    **
    ** Function:        connectConnOriented
    **
    ** Description:     Establish a connection-oriented connection to a peer.
    **                  jniHandle: Connection handle.
    **                  destinationSap: Peer's service access point.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool connectConnOriented (tJNI_HANDLE jniHandle, UINT8 destinationSap);


    /*******************************************************************************
    **
    ** Function:        send
    **
    ** Description:     Send data to peer.
    **                  jniHandle: Handle of connection.
    **                  buffer: Buffer of data.
    **                  bufferLen: Length of data.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool send (tJNI_HANDLE jniHandle, UINT8* buffer, UINT16 bufferLen);


    /*******************************************************************************
    **
    ** Function:        receive
    **
    ** Description:     Receive data from peer.
    **                  jniHandle: Handle of connection.
    **                  buffer: Buffer to store data.
    **                  bufferLen: Max length of buffer.
    **                  actualLen: Actual length received.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool receive (tJNI_HANDLE jniHandle, UINT8* buffer, UINT16 bufferLen, UINT16& actualLen);


    /*******************************************************************************
    **
    ** Function:        disconnectConnOriented
    **
    ** Description:     Disconnect a connection-oriented connection with peer.
    **                  jniHandle: Handle of connection.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool disconnectConnOriented (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        getRemoteMaxInfoUnit
    **
    ** Description:     Get peer's max information unit.
    **                  jniHandle: Handle of the connection.
    **
    ** Returns:         Peer's max information unit.
    **
    *******************************************************************************/
    UINT16 getRemoteMaxInfoUnit (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        getRemoteRecvWindow
    **
    ** Description:     Get peer's receive window size.
    **                  jniHandle: Handle of the connection.
    **
    ** Returns:         Peer's receive window size.
    **
    *******************************************************************************/
    UINT8 getRemoteRecvWindow (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        setP2pListenMask
    **
    ** Description:     Sets the p2p listen technology mask.
    **                  p2pListenMask: the p2p listen mask to be set?
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void setP2pListenMask (tNFA_TECHNOLOGY_MASK p2pListenMask);


    /*******************************************************************************
    **
    ** Function:        getP2pListenMask
    **
    ** Description:     Get the set of technologies that P2P is listening.
    **
    ** Returns:         Set of technologies.
    **
    *******************************************************************************/
    tNFA_TECHNOLOGY_MASK getP2pListenMask ();


    /*******************************************************************************
    **
    ** Function:        resetP2pListenMask
    **
    ** Description:     Reset the p2p listen technology mask to initial value.
    **
    ** Returns:         None.
    **
    *******************************************************************************/
    void resetP2pListenMask ();

    /*******************************************************************************
    **
    ** Function:        enableP2pListening
    **
    ** Description:     Start/stop polling/listening to peer that supports P2P.
    **                  isEnable: Is enable polling/listening?
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void enableP2pListening (bool isEnable);


    /*******************************************************************************
    **
    ** Function:        handleNfcOnOff
    **
    ** Description:     Handle events related to turning NFC on/off by the user.
    **                  isOn: Is NFC turning on?
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void handleNfcOnOff (bool isOn);


    /*******************************************************************************
    **
    ** Function:        getNextJniHandle
    **
    ** Description:     Get a new JNI handle.
    **
    ** Returns:         A new JNI handle.
    **
    *******************************************************************************/
    tJNI_HANDLE getNewJniHandle ();

    /*******************************************************************************
    **
    ** Function:        nfaServerCallback
    **
    ** Description:     Receive LLCP-related events from the stack.
    **                  p2pEvent: Event code.
    **                  eventData: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    static void nfaServerCallback  (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA *eventData);


    /*******************************************************************************
    **
    ** Function:        nfaClientCallback
    **
    ** Description:     Receive LLCP-related events from the stack.
    **                  p2pEvent: Event code.
    **                  eventData: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    static void nfaClientCallback  (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA *eventData);

private:
    static const int sMax = 10;
    static PeerToPeer sP2p;

    // Variables below only accessed from a single thread
    UINT16          mRemoteWKS;                 // Peer's well known services
    bool            mIsP2pListening;            // If P2P listening is enabled or not
    tNFA_TECHNOLOGY_MASK    mP2pListenTechMask; // P2P Listen mask

    // Variable below is protected by mNewJniHandleMutex
    tJNI_HANDLE     mNextJniHandle;

    // Variables below protected by mMutex
    // A note on locking order: mMutex in PeerToPeer is *ALWAYS*
    // locked before any locks / guards in P2pServer / P2pClient
    Mutex                    mMutex;
    android::sp<P2pServer>   mServers [sMax];
    android::sp<P2pClient>   mClients [sMax];

    // Synchronization variables
    SyncEvent       mSetTechEvent;              // completion event for NFA_SetP2pListenTech()
    SyncEvent       mSnepDefaultServerStartStopEvent; // completion event for NFA_SnepStartDefaultServer(), NFA_SnepStopDefaultServer()
    SyncEvent       mSnepRegisterEvent;         // completion event for NFA_SnepRegisterClient()
    Mutex           mDisconnectMutex;           // synchronize the disconnect operation
    Mutex           mNewJniHandleMutex;         // synchronize the creation of a new JNI handle


    /*******************************************************************************
    **
    ** Function:        ndefTypeCallback
    **
    ** Description:     Receive NDEF-related events from the stack.
    **                  ndefEvent: Event code.
    **                  eventData: Event data.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    static void ndefTypeCallback   (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *evetnData);


    /*******************************************************************************
    **
    ** Function:        findServer
    **
    ** Description:     Find a PeerToPeer object by connection handle.
    **                  nfaP2pServerHandle: Connectin handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pServer>   findServerLocked (tNFA_HANDLE nfaP2pServerHandle);


    /*******************************************************************************
    **
    ** Function:        findServer
    **
    ** Description:     Find a PeerToPeer object by connection handle.
    **                  serviceName: service name.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pServer>   findServerLocked (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        findServer
    **
    ** Description:     Find a PeerToPeer object by service name
    **                  serviceName: service name.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pServer>   findServerLocked (const char *serviceName);


    /*******************************************************************************
    **
    ** Function:        removeServer
    **
    ** Description:     Free resources related to a server.
    **                  jniHandle: Connection handle.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void        removeServer (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        removeConn
    **
    ** Description:     Free resources related to a connection.
    **                  jniHandle: Connection handle.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void        removeConn (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        createDataLinkConn
    **
    ** Description:     Establish a connection-oriented connection to a peer.
    **                  jniHandle: Connection handle.
    **                  serviceName: Peer's service name.
    **                  destinationSap: Peer's service access point.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool        createDataLinkConn (tJNI_HANDLE jniHandle, const char* serviceName, UINT8 destinationSap);


    /*******************************************************************************
    **
    ** Function:        findClient
    **
    ** Description:     Find a PeerToPeer object with a client connection handle.
    **                  nfaConnHandle: Connection handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pClient>   findClient (tNFA_HANDLE nfaConnHandle);


    /*******************************************************************************
    **
    ** Function:        findClient
    **
    ** Description:     Find a PeerToPeer object with a client connection handle.
    **                  jniHandle: Connection handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pClient>   findClient (tJNI_HANDLE jniHandle);


    /*******************************************************************************
    **
    ** Function:        findClientCon
    **
    ** Description:     Find a PeerToPeer object with a client connection handle.
    **                  nfaConnHandle: Connection handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<P2pClient>   findClientCon (tNFA_HANDLE nfaConnHandle);


    /*******************************************************************************
    **
    ** Function:        findConnection
    **
    ** Description:     Find a PeerToPeer object with a connection handle.
    **                  nfaConnHandle: Connection handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<NfaConn>     findConnection (tNFA_HANDLE nfaConnHandle);


    /*******************************************************************************
    **
    ** Function:        findConnection
    **
    ** Description:     Find a PeerToPeer object with a connection handle.
    **                  jniHandle: Connection handle.
    **
    ** Returns:         PeerToPeer object.
    **
    *******************************************************************************/
    android::sp<NfaConn>     findConnection (tJNI_HANDLE jniHandle);
};


/*****************************************************************************
**
**  Name:           NfaConn
**
**  Description:    Store information about a connection related to a peer.
**
*****************************************************************************/
class NfaConn : public android::RefBase
{
public:
    tNFA_HANDLE         mNfaConnHandle;         // NFA handle of the P2P connection
    PeerToPeer::tJNI_HANDLE         mJniHandle;             // JNI handle of the P2P connection
    UINT16              mMaxInfoUnit;
    UINT8               mRecvWindow;
    UINT16              mRemoteMaxInfoUnit;
    UINT8               mRemoteRecvWindow;
    SyncEvent           mReadEvent;             // event for reading
    SyncEvent           mCongEvent;             // event for congestion
    SyncEvent           mDisconnectingEvent;     // event for disconnecting


    /*******************************************************************************
    **
    ** Function:        NfaConn
    **
    ** Description:     Initialize member variables.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    NfaConn();
};


/*****************************************************************************
**
**  Name:           P2pServer
**
**  Description:    Store information about an in-bound connection from a peer.
**
*****************************************************************************/
class P2pServer : public android::RefBase
{
public:
    static const std::string sSnepServiceName;

    tNFA_HANDLE     mNfaP2pServerHandle;    // NFA p2p handle of local server
    PeerToPeer::tJNI_HANDLE     mJniHandle;     // JNI Handle
    SyncEvent       mRegServerEvent;        // for NFA_P2pRegisterServer()
    SyncEvent       mConnRequestEvent;      // for accept()
    std::string     mServiceName;

    /*******************************************************************************
    **
    ** Function:        P2pServer
    **
    ** Description:     Initialize member variables.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    P2pServer (PeerToPeer::tJNI_HANDLE jniHandle, const char* serviceName);

    /*******************************************************************************
    **
    ** Function:        registerWithStack
    **
    ** Description:     Register this server with the stack.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool registerWithStack();

    /*******************************************************************************
    **
    ** Function:        accept
    **
    ** Description:     Accept a peer's request to connect.
    **                  serverJniHandle: Server's handle.
    **                  connJniHandle: Connection handle.
    **                  maxInfoUnit: Maximum information unit.
    **                  recvWindow: Receive window size.
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    bool accept (PeerToPeer::tJNI_HANDLE serverJniHandle, PeerToPeer::tJNI_HANDLE connJniHandle,
            int maxInfoUnit, int recvWindow);

    /*******************************************************************************
    **
    ** Function:        unblockAll
    **
    ** Description:     Unblocks all server connections
    **
    ** Returns:         True if ok.
    **
    *******************************************************************************/
    void unblockAll();

    /*******************************************************************************
    **
    ** Function:        findServerConnection
    **
    ** Description:     Find a P2pServer that has the handle.
    **                  nfaConnHandle: NFA connection handle.
    **
    ** Returns:         P2pServer object.
    **
    *******************************************************************************/
    android::sp<NfaConn> findServerConnection (tNFA_HANDLE nfaConnHandle);

    /*******************************************************************************
    **
    ** Function:        findServerConnection
    **
    ** Description:     Find a P2pServer that has the handle.
    **                  jniHandle: JNI connection handle.
    **
    ** Returns:         P2pServer object.
    **
    *******************************************************************************/
    android::sp<NfaConn> findServerConnection (PeerToPeer::tJNI_HANDLE jniHandle);

    /*******************************************************************************
    **
    ** Function:        removeServerConnection
    **
    ** Description:     Remove a server connection with the provided handle.
    **                  jniHandle: JNI connection handle.
    **
    ** Returns:         True if connection found and removed.
    **
    *******************************************************************************/
    bool removeServerConnection(PeerToPeer::tJNI_HANDLE jniHandle);

private:
    Mutex           mMutex;
    // mServerConn is protected by mMutex
    android::sp<NfaConn>     mServerConn[MAX_NFA_CONNS_PER_SERVER];

    /*******************************************************************************
    **
    ** Function:        allocateConnection
    **
    ** Description:     Allocate a new connection to accept on
    **                  jniHandle: JNI connection handle.
    **
    ** Returns:         Allocated connection object
    **                  NULL if the maximum number of connections was reached
    **
    *******************************************************************************/
    android::sp<NfaConn> allocateConnection (PeerToPeer::tJNI_HANDLE jniHandle);
};


/*****************************************************************************
**
**  Name:           P2pClient
**
**  Description:    Store information about an out-bound connection to a peer.
**
*****************************************************************************/
class P2pClient : public android::RefBase
{
public:
    tNFA_HANDLE           mNfaP2pClientHandle;    // NFA p2p handle of client
    bool                  mIsConnecting;          // Set true while connecting
    android::sp<NfaConn>  mClientConn;
    SyncEvent             mRegisteringEvent;      // For client registration
    SyncEvent             mConnectingEvent;       // for NFA_P2pConnectByName or Sap()
    SyncEvent             mSnepEvent;             // To wait for SNEP completion

    /*******************************************************************************
    **
    ** Function:        P2pClient
    **
    ** Description:     Initialize member variables.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    P2pClient ();


    /*******************************************************************************
    **
    ** Function:        ~P2pClient
    **
    ** Description:     Free all resources.
    **
    ** Returns:         None
    **
    *******************************************************************************/
    ~P2pClient ();


    /*******************************************************************************
    **
    ** Function:        unblock
    **
    ** Description:     Unblocks any threads that are locked on this connection
    **
    ** Returns:         None
    **
    *******************************************************************************/
    void unblock();
};