/*
 * Copyright (C) 2010 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.
 */

#include <semaphore.h>

#include "com_android_nfc.h"

static sem_t *nfc_jni_llcp_send_sem;
static sem_t *nfc_jni_llcp_receive_sem;
static NFCSTATUS nfc_jni_cb_status = NFCSTATUS_FAILED;
static uint8_t receivedSsap;

namespace android {

/*
 * Callbacks
 */

static void nfc_jni_receive_callback(void* pContext, uint8_t ssap, NFCSTATUS status)
{  
   uint8_t* receiveSsap = (uint8_t*)pContext;
   
   LOG_CALLBACK("nfc_jni_receiveFrom_callback", status);

   nfc_jni_cb_status = status;
   
   if(status == NFCSTATUS_SUCCESS)
   {
      *receiveSsap = ssap;
      TRACE("RECEIVE UI_FRAME FROM SAP %d OK \n",*receiveSsap);
   }
   
   sem_post(nfc_jni_llcp_receive_sem);
}

static void nfc_jni_send_callback(void *pContext, NFCSTATUS status)
{     
   PHNFC_UNUSED_VARIABLE(pContext);
   
   LOG_CALLBACK("nfc_jni_sendTo_callback", status);

   nfc_jni_cb_status = status;
   
   sem_post(nfc_jni_llcp_send_sem);
}

/*
* Methods
*/
static jboolean com_android_nfc_NativeLlcpConnectionlessSocket_doSendTo(JNIEnv *e, jobject o, jint nsap, jbyteArray data)
{
   NFCSTATUS ret;
   struct timespec ts;  
   phLibNfc_Handle hLlcpSocket;
   phNfc_sData_t sSendBuffer;
   
   /* Retrieve socket handle */
   hLlcpSocket = nfc_jni_get_nfc_socket_handle(e,o);
   
   sSendBuffer.buffer = (uint8_t*)e->GetByteArrayElements(data, NULL);
   sSendBuffer.length = (uint32_t)e->GetArrayLength(data);   

   TRACE("phLibNfc_Llcp_SendTo()");
   REENTRANCE_LOCK();
   ret = phLibNfc_Llcp_SendTo(hLlcpSocket,
                              nsap,
                              &sSendBuffer,
                              nfc_jni_send_callback,
                              (void*)hLlcpSocket);
   REENTRANCE_UNLOCK();
   if(ret != NFCSTATUS_PENDING)
   {
      LOGE("phLibNfc_Llcp_SendTo() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
      return FALSE;
   } 
   TRACE("phLibNfc_Llcp_SendTo() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
   
   /* Wait for callback response */
   if(sem_wait(nfc_jni_llcp_send_sem) == -1)
      return FALSE;   


   if(nfc_jni_cb_status == NFCSTATUS_SUCCESS)
   {
      return TRUE; 
   }
   else
   {
      return FALSE;    
   }  
}

static jobject com_android_nfc_NativeLlcpConnectionlessSocket_doReceiveFrom(JNIEnv *e, jobject o, jint linkMiu)
{
   NFCSTATUS ret;
   struct timespec ts;
   uint8_t ssap;
   jobject llcpPacket = NULL;
   phLibNfc_Handle hLlcpSocket;
   phNfc_sData_t sReceiveBuffer;
   jclass clsLlcpPacket;
   jfieldID f;
   jbyteArray receivedData;
   
   /* Create new LlcpPacket object */
   if(nfc_jni_cache_object(e,"android/nfc/LlcpPacket",&(llcpPacket)) == -1)
   {
      LOGE("Find LlcpPacket class error");
      return NULL;           
   }
   
   /* Get NativeConnectionless class object */
   clsLlcpPacket = e->GetObjectClass(llcpPacket);
   if(e->ExceptionCheck())
   {
      LOGE("Get Object class error");
      return NULL;  
   } 
   
   /* Retrieve socket handle */
   hLlcpSocket = nfc_jni_get_nfc_socket_handle(e,o);
   TRACE("phLibNfc_Llcp_RecvFrom(), Socket Handle = 0x%02x, Link LIU = %d", hLlcpSocket, linkMiu);
   
   sReceiveBuffer.buffer = (uint8_t*)malloc(linkMiu);
   sReceiveBuffer.length = linkMiu;
      
   REENTRANCE_LOCK();
   ret = phLibNfc_Llcp_RecvFrom(hLlcpSocket,
                                &sReceiveBuffer,
                                nfc_jni_receive_callback,
                                &ssap);
   REENTRANCE_UNLOCK();
   if(ret != NFCSTATUS_PENDING)
   {
      LOGE("phLibNfc_Llcp_RecvFrom() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
      return NULL;
   } 
   TRACE("phLibNfc_Llcp_RecvFrom() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
   
   /* Wait for callback response */
   if(sem_wait(nfc_jni_llcp_receive_sem) == -1)
      return NULL; 
      

   if(nfc_jni_cb_status == NFCSTATUS_SUCCESS)
   {
      TRACE("Data Received From SSAP = %d\n, length = %d", ssap, sReceiveBuffer.length);
      
      /* Set Llcp Packet remote SAP */
      f = e->GetFieldID(clsLlcpPacket, "mRemoteSap", "I");
      e->SetIntField(llcpPacket, f,(jbyte)ssap);
      
      /* Set Llcp Packet Buffer */
      LOGD("Set LlcpPacket Data Buffer\n");       
      f = e->GetFieldID(clsLlcpPacket, "mDataBuffer", "[B");
      receivedData = e->NewByteArray(sReceiveBuffer.length);
      e->SetByteArrayRegion(receivedData, 0, sReceiveBuffer.length,(jbyte *)sReceiveBuffer.buffer);      
      e->SetObjectField(llcpPacket, f, receivedData); 

      return llcpPacket;
   }
   else
   {
      return FALSE;    
   }    
}

static jboolean com_android_nfc_NativeLlcpConnectionlessSocket_doClose(JNIEnv *e, jobject o)
{
   NFCSTATUS ret;
   phLibNfc_Handle hLlcpSocket;
   TRACE("Close Connectionless socket");
   
   /* Retrieve socket handle */
   hLlcpSocket = nfc_jni_get_nfc_socket_handle(e,o);

   TRACE("phLibNfc_Llcp_Close()");
   REENTRANCE_LOCK();
   ret = phLibNfc_Llcp_Close(hLlcpSocket);
   REENTRANCE_UNLOCK();
   if(ret == NFCSTATUS_SUCCESS)
   {
      TRACE("phLibNfc_Llcp_Close() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
      return TRUE;
   }
   else
   {
      LOGE("phLibNfc_Llcp_Close() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
      return FALSE;
   }
}


/*
 * JNI registration.
 */
static JNINativeMethod gMethods[] =
{
   {"doSendTo", "(I[B)Z", (void *)com_android_nfc_NativeLlcpConnectionlessSocket_doSendTo},
      
   {"doReceiveFrom", "(I)Landroid/nfc/LlcpPacket;", (void *)com_android_nfc_NativeLlcpConnectionlessSocket_doReceiveFrom},
      
   {"doClose", "()Z", (void *)com_android_nfc_NativeLlcpConnectionlessSocket_doClose},
};


int register_com_android_nfc_NativeLlcpConnectionlessSocket(JNIEnv *e)
{
    nfc_jni_llcp_send_sem = (sem_t *)malloc(sizeof(sem_t));
    nfc_jni_llcp_receive_sem = (sem_t *)malloc(sizeof(sem_t));
   if(sem_init(nfc_jni_llcp_send_sem, 0, 0) == -1)
      return -1;
      
   if(sem_init(nfc_jni_llcp_receive_sem, 0, 0) == -1)
      return -1;

   return jniRegisterNativeMethods(e,
      "com/android/nfc/NativeLlcpConnectionlessSocket",
      gMethods, NELEM(gMethods));
}

} // android namespace