/*
 * Copyright (C) 2015 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.
 */
#ifdef ESE_NFC_SYNCHRONIZATION
#include <linux/ese-nfc-sync.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <android-base/stringprintf.h>
#include <base/logging.h>
#include <phNxpConfig.h>
#include <phNxpExtns_MifareStd.h>
#include <phNxpLog.h>

using android::base::StringPrintf;

extern phNxpExtns_Context_t gphNxpExtns_Context;
extern phFriNfc_NdefMap_t* NdefMap;
extern phNci_mfc_auth_cmd_t gAuthCmdBuf;

static NFCSTATUS phNxpExtns_ProcessSysMessage(phLibNfc_Message_t* msg);
static NFCSTATUS phNxpExtns_SendMsg(phLibNfc_Message_t* sysmsg);

#ifdef ESE_NFC_SYNCHRONIZATION
/* timing calculation structure*/
typedef struct time_cal {
  struct timeval tv1;
  struct timeval tv2;
} TimeCal;
static int fd_ese_nfc_sync; /*file descriptor to hold sync driver handle*/
#endif

/*******************************************************************************
**
** Function         EXTNS_Init
**
** Description      This function Initializes Mifare Classic Extns. Allocates
**                  required memory and initializes the Mifare Classic Vars
**
** Returns          NFCSTATUS_SUCCESS if successfully initialized
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_Init(tNFA_DM_CBACK* p_nfa_dm_cback,
                     tNFA_CONN_CBACK* p_nfa_conn_cback) {
  NFCSTATUS status = NFCSTATUS_FAILED;

  /* reset config cache */
  resetNxpConfig();

  /* Initialize Log level */
  phNxpLog_InitializeLogLevel();

  /* Validate parameters */
  if ((!p_nfa_dm_cback) || (!p_nfa_conn_cback)) {
    LOG(ERROR) << StringPrintf("EXTNS_Init(): error null callback");
    goto clean_and_return;
  }

  gphNxpExtns_Context.p_dm_cback = p_nfa_dm_cback;
  gphNxpExtns_Context.p_conn_cback = p_nfa_conn_cback;

  if (NFCSTATUS_SUCCESS != phNxpExtns_MfcModuleInit()) {
    LOG(ERROR) << StringPrintf("ERROR: MFC Module Init Failed");
    goto clean_and_return;
  }
  gphNxpExtns_Context.Extns_status = EXTNS_STATUS_OPEN;

  status = NFCSTATUS_SUCCESS;
  return status;

clean_and_return:
  gphNxpExtns_Context.Extns_status = EXTNS_STATUS_CLOSE;
  return status;
}

/*******************************************************************************
**
** Function         EXTNS_Close
**
** Description      This function de-initializes Mifare Classic Extns.
**                  De-allocates memory
**
** Returns          None
**
*******************************************************************************/
void EXTNS_Close(void) {
  gphNxpExtns_Context.Extns_status = EXTNS_STATUS_CLOSE;
  phNxpExtns_MfcModuleDeInit();
  return;
}

/*******************************************************************************
**
** Function         EXTNS_MfcCallBack
**
** Description      Decodes Mifare Classic Tag Response
**                  This is called from NFA_SendRaw Callback
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcCallBack(uint8_t* buf, uint32_t buflen) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_RX_DATA;
  msg.pMsgData = buf;
  msg.Size = buflen;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }
  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcCheckNDef
**
** Description      Performs NDEF detection for Mifare Classic Tag
**
**                  Upon successful completion of NDEF detection, a
**                  NFA_NDEF_DETECT_EVT will be sent, to notify the application
**                  of the NDEF attributes (NDEF total memory size, current
**                  size, etc.).
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcCheckNDef(void) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;
  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_CHECK_NDEF;
  msg.pMsgData = NULL;
  msg.Size = 0;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcReadNDef
**
** Description      Reads NDEF message from Mifare Classic Tag.
**
**                  Upon receiving the NDEF message, the message will be sent to
**                  the handler registered with
*EXTNS_MfcRegisterNDefTypeHandler.
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcReadNDef(void) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_READ_NDEF;
  msg.pMsgData = NULL;
  msg.Size = 0;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}
/*******************************************************************************
**
** Function         EXTNS_MfcPresenceCheck
**
** Description      Do the check presence for Mifare Classic Tag.
**
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcPresenceCheck(void) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_PRESENCE_CHECK;
  msg.pMsgData = NULL;
  msg.Size = 0;

  gAuthCmdBuf.status = NFCSTATUS_FAILED;
  if (sem_init(&gAuthCmdBuf.semPresenceCheck, 0, 0) == -1) {
    LOG(ERROR) << StringPrintf("%s: semaphore creation failed (errno=%d)",
                               __func__, errno);
    return NFCSTATUS_FAILED;
  }

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
    sem_destroy(&gAuthCmdBuf.semPresenceCheck);
  }

  return status;
}

/*******************************************************************************
**
** Function        EXTNS_MfcSetReadOnly
**
**
** Description:
**      Sets tag as read only.
**
**      When tag is set as read only, or if an error occurs, the app will be
**      notified with NFA_SET_TAG_RO_EVT.
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcSetReadOnly(uint8_t* key, uint8_t len) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_READ_ONLY;
  msg.pMsgData = key;
  msg.Size = len;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcWriteNDef
**
** Description      Writes NDEF data to Mifare Classic Tag.
**
**                  When the entire message has been written, or if an error
**                  occurs, the app will be notified with NFA_WRITE_CPLT_EVT.
**
**                  p_data needs to be persistent until NFA_WRITE_CPLT_EVT
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcWriteNDef(uint8_t* p_data, uint32_t len) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_WRITE_NDEF;
  msg.pMsgData = p_data;
  msg.Size = len;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcFormatTag
**
** Description      Formats Mifare Classic Tag.
**
**                  The NFA_RW_FORMAT_CPLT_EVT, status is used to
**                  indicate if tag is successfully formated or not
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcFormatTag(uint8_t* key, uint8_t len) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_FORMAT_NDEF;
  msg.pMsgData = key;
  msg.Size = len;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcDisconnect
**
** Description      Disconnects Mifare Classic Tag.
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcDisconnect(void) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_DISCONNECT;
  msg.pMsgData = NULL;
  msg.Size = 0;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcActivated
**
** Description      Activates Mifare Classic Tag.
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcActivated(void) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;
  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_ACTIVATED;
  msg.pMsgData = NULL;
  msg.Size = 0;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcTransceive
**
** Description      Sends raw frame to Mifare Classic Tag.
**
** Returns          NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcTransceive(uint8_t* p_data, uint32_t len) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  phLibNfc_Message_t msg;

  msg.eMsgType = PH_NXPEXTNS_MIFARE_TRANSCEIVE;
  msg.pMsgData = p_data;
  msg.Size = len;

  status = phNxpExtns_SendMsg(&msg);
  if (NFCSTATUS_SUCCESS != status) {
    LOG(ERROR) << StringPrintf("Error Sending msg to Extension Thread");
  }

  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcInit
**
** Description      This function is used to Init Mifare Classic Extns.
**                  This function should be called when the tag detected is
**                  Mifare Classic.
**
** Returns          NFCSTATUS_SUCCESS
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcInit(tNFA_ACTIVATED activationData) {
  tNFC_ACTIVATE_DEVT rfDetail = activationData.activate_ntf;

  NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Sak =
      rfDetail.rf_tech_param.param.pa.sel_rsp;
  NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.AtqA[0] =
      rfDetail.rf_tech_param.param.pa.sens_res[0];
  NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.AtqA[1] =
      rfDetail.rf_tech_param.param.pa.sens_res[1];

  return NFCSTATUS_SUCCESS;
}

/*******************************************************************************
**
** Function         phNxpExtns_ProcessSysMessage
**
** Description      Internal function to route the request from JNI and Callback
**                  from NFA_SendRawFrame to right function
**
** Returns          NFCSTATUS_SUCCESS if valid request
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
static NFCSTATUS phNxpExtns_ProcessSysMessage(phLibNfc_Message_t* msg) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  if (gphNxpExtns_Context.Extns_status == EXTNS_STATUS_CLOSE) {
    return NFCSTATUS_FAILED;
  }

  switch (msg->eMsgType) {
    case PH_NXPEXTNS_RX_DATA:
      status = Mfc_RecvPacket((uint8_t*)msg->pMsgData, msg->Size);
      break;

    case PH_NXPEXTNS_MIFARE_CHECK_NDEF:
      pthread_mutex_init(&gAuthCmdBuf.syncmutex, NULL);
      pthread_mutex_lock(&gAuthCmdBuf.syncmutex);
      status = Mfc_CheckNdef();
      pthread_mutex_unlock(&gAuthCmdBuf.syncmutex);
      pthread_mutex_destroy(&gAuthCmdBuf.syncmutex);
      break;

    case PH_NXPEXTNS_MIFARE_READ_NDEF:
      status = Mfc_ReadNdef();
      break;

    case PH_NXPEXTNS_MIFARE_WRITE_NDEF:
      status = Mfc_WriteNdef((uint8_t*)msg->pMsgData, msg->Size);
      break;

    case PH_NXPEXTNS_MIFARE_FORMAT_NDEF:
      status = Mfc_FormatNdef((uint8_t*)msg->pMsgData, msg->Size);
      break;

    case PH_NXPEXTNS_DISCONNECT:
      Mfc_DeactivateCbackSelect();
      break;

    case PH_NXPEXTNS_ACTIVATED:
      Mfc_ActivateCback();
      break;

    case PH_NXPEXTNS_MIFARE_TRANSCEIVE:
      status = Mfc_Transceive((uint8_t*)msg->pMsgData, msg->Size);
      break;

    case PH_NXPEXTNS_MIFARE_READ_ONLY:
      status = Mfc_SetReadOnly((uint8_t*)msg->pMsgData, msg->Size);
      break;
    case PH_NXPEXTNS_MIFARE_PRESENCE_CHECK:
      pthread_mutex_init(&gAuthCmdBuf.syncmutex, NULL);
      pthread_mutex_lock(&gAuthCmdBuf.syncmutex);
      status = Mfc_PresenceCheck();
      pthread_mutex_unlock(&gAuthCmdBuf.syncmutex);
      pthread_mutex_destroy(&gAuthCmdBuf.syncmutex);
      break;
    default:
      status = NFCSTATUS_FAILED;
      LOG(ERROR) << StringPrintf("Illegal Command for Extension");
      break;
  }

  return status;
}

/*******************************************************************************
**
** Function         phNxpExtns_SendMsg
**
** Description      unlocks phNxpExtns_ProcessSysMessage with a valid message
**
** Returns          NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
static NFCSTATUS phNxpExtns_SendMsg(phLibNfc_Message_t* sysmsg) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  status = phNxpExtns_ProcessSysMessage(sysmsg);

  return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcRegisterNDefTypeHandler
**
** Description      This function allows the applications to register for
**                  specific types of NDEF records.
**
**                  For records types which were not registered, the record will
**                  be sent to the default handler.
**
** Returns          NFCSTATUS_SUCCESS
**
*******************************************************************************/
NFCSTATUS
EXTNS_MfcRegisterNDefTypeHandler(tNFA_NDEF_CBACK* ndefHandlerCallback) {
  NFCSTATUS status = NFCSTATUS_FAILED;
  if (NULL != ndefHandlerCallback) {
    gphNxpExtns_Context.p_ndef_cback = ndefHandlerCallback;
    status = NFCSTATUS_SUCCESS;
  }

  return status;
}

/*******************************************************************************
**                     Synchronizing Functions                                **
**            Synchronizes Callback in JNI and MFC Extns                      **
*******************************************************************************/

bool_t EXTNS_GetConnectFlag(void) { return (gphNxpExtns_Context.ExtnsConnect); }

void EXTNS_SetConnectFlag(bool_t flagval) {
  gphNxpExtns_Context.ExtnsConnect = flagval;
}

bool_t EXTNS_GetDeactivateFlag(void) {
  return (gphNxpExtns_Context.ExtnsDeactivate);
}

void EXTNS_SetDeactivateFlag(bool_t flagval) {
  gphNxpExtns_Context.ExtnsDeactivate = flagval;
}

bool_t EXTNS_GetCallBackFlag(void) {
  return (gphNxpExtns_Context.ExtnsCallBack);
}

void EXTNS_SetCallBackFlag(bool_t flagval) {
  gphNxpExtns_Context.ExtnsCallBack = flagval;
}
NFCSTATUS EXTNS_GetPresenceCheckStatus(void) {
  struct timespec ts;

  clock_gettime(CLOCK_REALTIME, &ts);
  ts.tv_sec += 0;
  ts.tv_nsec += 100 * 1000 * 1000;  // 100 milisec
  if (ts.tv_nsec >= 1000 * 1000 * 1000) {
    ts.tv_sec += 1;
    ts.tv_nsec = ts.tv_nsec - (1000 * 1000 * 1000);
  }

  if (sem_timedwait(&gAuthCmdBuf.semPresenceCheck, &ts)) {
    LOG(ERROR) << StringPrintf("%s: failed to wait (errno=%d)", __func__,
                               errno);
    sem_destroy(&gAuthCmdBuf.semPresenceCheck);
    gAuthCmdBuf.auth_sent = false;
    return NFCSTATUS_FAILED;
  }
  if (sem_destroy(&gAuthCmdBuf.semPresenceCheck)) {
    LOG(ERROR) << StringPrintf(
        "%s: Failed to destroy check Presence semaphore (errno=%d)", __func__,
        errno);
  }
  return gAuthCmdBuf.status;
}

void MfcPresenceCheckResult(NFCSTATUS status) {
  gAuthCmdBuf.status = status;
  EXTNS_SetCallBackFlag(true);
  sem_post(&gAuthCmdBuf.semPresenceCheck);
}
void MfcResetPresenceCheckStatus(void) { gAuthCmdBuf.auth_sent = false; }
/*******************************************************************************
**
** Function         EXTNS_CheckMfcResponse
**
** Description      This function is called from JNI Transceive for Mifare
**                  Classic Tag status interpretation and to send the required
**                  status to application
**
** Returns          NFCSTATUS_SUCCESS
**                  NFCSTATUS_FAILED
**
*******************************************************************************/
NFCSTATUS EXTNS_CheckMfcResponse(uint8_t** sTransceiveData,
                                 uint32_t* sTransceiveDataLen) {
  NFCSTATUS status = NFCSTATUS_SUCCESS;

  if (*sTransceiveDataLen == 3) {
    if ((*sTransceiveData)[0] == 0x10 && (*sTransceiveData)[1] != 0x0A) {
      LOG(ERROR) << StringPrintf("Mifare Error in payload response");
      *sTransceiveDataLen = 0x1;
      *sTransceiveData += 1;
      return NFCSTATUS_FAILED;
    }
  }
  if ((*sTransceiveData)[0] == 0x40) {
    *sTransceiveData += 1;
    *sTransceiveDataLen = 0x01;
    if ((*sTransceiveData)[0] == 0x03) {
      *sTransceiveDataLen = 0x00;
      status = NFCSTATUS_FAILED;
    }
  } else if ((*sTransceiveData)[0] == 0x10) {
    *sTransceiveData += 1;
    *sTransceiveDataLen = 0x10;
  }

  return status;
}