/*******************************************************************************
* Project:      Amped'RF Bluetooth
* File name:    App_Main.c
* Description:  Various Examples
*
* Copyright (c) 2016 Amped'RF Technology Inc, All rights reserved.
*******************************************************************************/
#ifdef SDK
#include "SDK_Common.h"
#include "App_Main.h"
#if APP_EXAMPLE==0

extern void AT_InitDefaultBtiSettings(void);

//---------------------------------------------
//   Defines
//---------------------------------------------
#define DEMO_HELLO
#define DEMO_ACK
//#define MULTIPOINT
//#define SDK_MESSAGE_TEST
//#define DEFAULT_GAP_SETTINGS_EXAMPLE
//#define CONNECT_DISCONNECT_EXAMPLE
//#define GPIO_INTERRUPT_EXAMPLE
//#define WAKEUP_EXAMPLE

#ifdef CONNECT_DISCONNECT_EXAMPLE
uint64 Connect_BD_ADDR=0x00043e083613;
#endif


/*****************************************************************************************
  This define enables an ACK for the door opener application, to simulate a successful
    open event
*****************************************************************************************/
//#define ACK_CUSTOM

/*****************************************************************************************
  This define enables an example that support a custom list of devices, saved in NVM
    The structure includes: 10-digit phone number, BD Address, and iOS Type
*****************************************************************************************/
//#define CUSTOM_DEVICE_LIST_DEMO

/*****************************************************************************************
*  This define enables an example that supports an automatic reconnect to an iOS device
*    Each IOS type of device found in the custom device table, stored in NVM, will
*    be periodically paged in sequence
******************************************************************************************/
//#define RECONNECT_EXAMPLE

/*****************************************************************************************
*  This define is used when an Apple CP chip v2.0B is connected and requires the low power
*  mode internal to the chip.  Defined in App_Main.h
****************************************************************************************/
#ifdef APPLE_CP_SLEEP
typedef enum { False = 0, True  = !False } BOOL;
extern BOOL SendToAuthenticationControl(uint8 event);
static void AppleCPSleep(void);
#endif


#define REMOTE_DEVICE_ADDR  0x00043e218247
#define AT_GAP_RESULT_DELETEDB_BDADDR    103

//---------------------------------------------
//   Globals
//---------------------------------------------
tAppVar AppVar;

extern void Targ_EnableGpioEvent(uint8 PortSource, uint8 PinSource, uint8 trigger);
static uint16 GetDeviceList(tDeviceDBInformation **ppInfo, uint32 *NxtSeq);
bool OS_IsHandleConnected(uint16 handle);

#ifdef CONNECT_DISCONNECT_EXAMPLE
uint8 PortName[AMP_PORT_NAME_LENGTH];
uint8 PortRole;
#endif

#ifdef CUSTOM_DEVICE_LIST_DEMO
static uint8 CustAddDevice(tApp_DeviceRecord *pNewRecord);
//static bool CustRemoveDeviceByPhoneNumber(uint8 *pPhoneNum);
static char TestPhoneNum[10] = {'4','0','8','2','1','3','9','5','3','2'};
#endif

#ifdef RECONNECT_EXAMPLE
static void IapConnect(tBdAddr *pAddr);
#endif

#ifdef SDK_MESSAGE_TEST
void SDK_MessageTest(void);
#endif

//---------------------------------------------
// Prototypes
//---------------------------------------------
void ShowDeviceRecords(void);



/*******************************************************************************
* Structure for message passing
*******************************************************************************/
typedef struct {
    uint32 Transac;
    uint16 Length;
    char * pBuffer;
} tTextMsg;



/*******************************************************************************
* Function Name  : SppConnect
* Description    : Make a connection
*******************************************************************************/
#ifdef CONNECT_DISCONNECT_EXAMPLE
void SppConnect(uint64 RemoteAddress)
{
    tOS_Signal                *pSignal;
    tSIG_AMPSppConnectRequest *pConnectReq;
    //tSIG_BtiSppConnectRequest *p;

    pSignal = BP_AllocSignal(SIG_AMP_SPP_CONNECT_REQ, sizeof(tSIG_AMPSppConnectRequest));
    pConnectReq = (tSIG_AMPSppConnectRequest *)OS_GetSignalDataPtr(pSignal);

    memset(pConnectReq, 0, sizeof(tSIG_AMPSppConnectRequest));      // zero structure
    memcpy(&pConnectReq->BdAddr, &Connect_BD_ADDR, BD_ADDR_LEN);    // set bd addr
    strcpy((char *)pConnectReq->PortName, "Test");
    pConnectReq->ServiceId = BTM_SEC_SERVICE_SERIAL_PORT;

    TM_print("Connecting to ");
    for (int i=0; i<BD_ADDR_LEN; i++) {
        TM_print("%02x", pConnectReq->BdAddr.Byte[BD_ADDR_LEN-1-i]);
    }
    TM_printlf("");
    TM_printflush();

    OS_Send(OS_AMP, pSignal);                                       // send
}
#endif


/*******************************************************************************
* Function Name  : SppDisconnect
* Description    : Disconnect a connection
*******************************************************************************/
#ifdef CONNECT_DISCONNECT_EXAMPLE
void SppDisconnect(uint8 *portname, uint8 role)
{
    tOS_Signal                   *pSignal;
    tSIG_AMPSppDisconnectRequest *pDisconReq;

    pSignal= BP_AllocSignal(SIG_BTI_SPP_DISCONNECT_REQ, sizeof(tSIG_AMPSppDisconnectRequest));
    pDisconReq = (tSIG_AMPSppDisconnectRequest*)OS_GetSignalDataPtr(pSignal);
    memset(pDisconReq, 0, sizeof(tSIG_AMPSppDisconnectRequest));
    memcpy(pDisconReq->PortName, portname, AMP_PORT_NAME_LENGTH);
    pDisconReq->SppRole = role;
    OS_Send(OS_AMP, pSignal);
}
#endif




/*******************************************************************************
* Function Name  : SendMessage
* Description    : Send a message using an allocated buffer
*******************************************************************************/
void SendMessage(char *s)
{
    tOS_Signal  * pSignal;
    tTextMsg    * pTextMsg;

    pSignal = OS_AllocSignal(SIG_APP_MESSAGE, sizeof(tTextMsg));
    pTextMsg = (tTextMsg *)OS_GetSignalDataPtr(pSignal);
    memset(pTextMsg, 0, sizeof(tTextMsg));
    if (pTextMsg->pBuffer = GKI_getbuf(50))
    {
        pTextMsg->Length = strlen(s);
        strcpy(pTextMsg->pBuffer, s);
        OS_Send(OS_APP, pSignal);
    }
    else {
        OS_ReleaseSignal(pSignal);
    }
}


/*******************************************************************************
* Function Name  : ShowMessage
* Description    : Show a message in an allocated buffer within a signal
*******************************************************************************/
void ShowMessage(tOS_Signal * pSignal)
{
    tTextMsg    * pTextMsg;

    if (pTextMsg = (tTextMsg *)OS_GetSignalDataPtr(pSignal)) {
        TM_printlf("MsgLen=%d, MsgText=\"%s\"", pTextMsg->Length, pTextMsg->pBuffer);
        TM_printflush();
        OS_Release(pTextMsg->pBuffer);
    }
}



/*******************************************************************************
* Function Name  : CommandComplete
* Description    : Results of command complete
********************************************************************************/
void App_CommandComplete(tHciEvent *pEvt)
{
    tUART_Command_Complete_Event *cc = &(pEvt->EvtType.Command_Complete);
    uint8 i;

    if (cc->Opcode && pEvt->Code==HCI_COMMAND_COMPLETE_EVT)
    {
        switch (cc->Opcode) {
        case HCI_READ_BD_ADDR:
            TM_print("BD_ADDR = ");
            for (i=0; i<6; i++) {
                TM_print("%02x", cc->Params[6-i]);
            }
            break;
        }
        TM_printlf("");
        TM_printflush();
    }
}



/*******************************************************************************
* Function Name  : APP_ReceiveSignal
* Description    : Target supervisor module signal dispatch function
********************************************************************************/
void APP_ReceiveSignal(tOS_Signal *pSignal)
{
    uint16 SigNo = OS_GetSignalNo(pSignal);
    tOS_Signal        * pNewSignal;
    tApp_HCI_Message  * pAppHciMessage;
    tSIG_AMPGapBond   * pBondMsg;
    tSIG_AMPGapResult * pGapResult;
    tSIG_ExtInterrupt * pExtInterrupt;
    uint64 BondAddress = REMOTE_DEVICE_ADDR;
    char * Pin = "1234";
    uint8 LastValue=0xFF;
    tSIG_AMPSppRecv          * pAMPSppRecv;
#if defined(CONNECT_DISCONNECT_EXAMPLE) || defined(RECONNECT_EXAMPLE)
    tSIG_AMPSppListenCon     * pSppListenCon;
    tSIG_AMPSppConnectCon    * pSppConnectCon;
#endif
    tSIG_AMPSppDisconnectCon * pSppDisconnectCon;
#ifdef DEMO_HELLO
    const char Hello[]="Hello World";
#endif
#ifdef DEMO_ACK
    char msg[30];
    uint16 PortHandle;
#endif
#ifdef ACK_CUSTOM
    char msg[30];
    uint16 PortHandle;
#endif
#ifdef RECONNECT_EXAMPLE
    uint8               ProfileID;
    //tDiscoveryReq   * pDiscoveryReq;
    tApp_DeviceTable  * pDeviceTable;
    tApp_DeviceRecord * pFlashRecord;
    uint8 j;
    static uint8 reconn_dev_index=0;
#endif
    char ch;
    uint8 Node;

    // Catch for NULL signals
    if (pSignal == NULL) {
        return;
    }


    switch (SigNo)
    {
    case SIG_OS_STARTUP:
        OS_TimerRegisterRequest(0, APP_TMRID_STARTED, 2000, SIG_APP_STARTED);
#ifdef SDK_MESSAGE_TEST
        OS_TimerRegisterRequest(0, APP_TMRID_MESSAGE_TEST, APP_MESSAGE_TEST_INTERVAL, SIG_APP_MESSAGE_TEST);
#endif

#ifdef DEMO_HELLO
        OS_TimerRegisterRequest((uint32)Hello, APP_TMRID_HELLO, 3000, SIG_APP_HELLO);
#endif
#ifdef RECONNECT_EXAMPLE
//        OS_TimerRegisterRequest(1, APP_TMRID_CONNECT_INTERVAL, APP_CONNECT_INTERVAL, SIG_APP_CONNECT_INTERVAL_TIMEOUT);
#endif
        // Reserve AMP Signals
        //OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_GAP_RESULT);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_SPP_LISTEN_CON);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_SPP_CONNECT_CON);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_SPP_DISCONNECT_IND);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_SPP_DISCONNECT_CON);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_GAP_LL_CONN_DOWN);
        OS_BindAmpSignal(SIGNAL_REPEAT, SIG_AMP_IAP_AUTH_COMPLETE);

#ifdef APPLE_CP_SLEEP
        AppleCPSleep();  // force apple cp to sleep when not in use
#endif
#ifdef APPLE_CP_PWR_CONTROL
        //TM_printlf("apple cp pwr off");
        //TM_printflush();
        TM_ResetPio(APPLE_CP_PWR_PIN);  // turn off the pwr transistor for the Apple CP
#endif
        break;

    case SIG_APP_STARTED:
        TM_printlf("\r\nAmped RF DevKit - Version %s  [App_Main.c]\r\n",
                   BUILD_VERSION);
        TM_printflush();

#ifdef CONNECT_DISCONNECT_EXAMPLE
        SppConnect(Connect_BD_ADDR);
#endif
#ifdef DEFAULT_GAP_SETTINGS_EXAMPLE
        WriteConfigVarFlag(VAR_ID_InquiryScan, false);  // Default Discoverable
        WriteConfigVarFlag(VAR_ID_PageScan, true);      // Default Connectable
        SetupGapDiscoveryOff();                         // Turn off Discovery
#endif
#ifdef RECONNECT_EXAMPLE
        pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);

        TM_printlf("Table of iAP devices");

        // Check for empty
        if (pDeviceTable==NULL || pDeviceTable->RecordsInUse==0) {
            TM_printlf(" Empty");
            TM_printflush();
            break;
        }

        // Loop by number of records or until a candidate is found
        for (j=0; j<pDeviceTable->RecordsInUse; j++)
        {
            pFlashRecord = &pDeviceTable->DeviceRecord[j];
            TM_print(" %d. ", j);
            for (int i=0; i<6; i++) {
                TM_print("%02x", pFlashRecord->DeviceAddress.Byte[5-i]);
            }
            TM_print(" %d [", pFlashRecord->DeviceType);
            for (int i=0; i<PHONE_NUMBER_LEN; i++) {
                ch = pFlashRecord->PhoneNumber[i];
                TM_print("%c", ch>' ' && ch<=126 ? ch : '.');
            }
            TM_printlf("]");
        }
        TM_printflush();
#endif
        break;

#ifdef RECONNECT_EXAMPLE
    case SIG_APP_CONNECT_INTERVAL_TIMEOUT:
        // get the pointer for the flash table
        pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);

        // Check for empty
        if (pDeviceTable==NULL || pDeviceTable->RecordsInUse==0) {
            OS_TimerRegisterRequest(1, APP_TMRID_CONNECT_INTERVAL, APP_CONNECT_INTERVAL, SIG_APP_CONNECT_INTERVAL_TIMEOUT);
            break;
        }

        // Loop by number of records or until a candidate is found
        for (j=0; j<pDeviceTable->RecordsInUse; j++)
        {
            // Incr and save index
            ++reconn_dev_index;
            reconn_dev_index = (reconn_dev_index >= pDeviceTable->RecordsInUse) ? 0 : reconn_dev_index;
            pFlashRecord = &pDeviceTable->DeviceRecord[reconn_dev_index];

            TM_print("Checking %d. ", reconn_dev_index);
            for (int i=0; i<6; i++)
                TM_print("%02x", pFlashRecord->DeviceAddress.Byte[5-i]);
            TM_printlf("");
            TM_printflush();

            if(pFlashRecord->DeviceType == IOS_DEVICE)
            {
                // check if the number of connections is < 4
                // also check if the device is already connected
                if (!OS_IsConnected(&pFlashRecord->DeviceAddress) && (OS_ConnectionCount() < 4))
                {
                    IapConnect(&pFlashRecord->DeviceAddress); // connect to the device now
                    break;
                }
            }
        }

        // restart another timer now
        OS_TimerRegisterRequest(1, APP_TMRID_CONNECT_INTERVAL, APP_CONNECT_INTERVAL, SIG_APP_CONNECT_INTERVAL_TIMEOUT);
        break;
#endif

    case SIG_OS_SHUTDOWN:
        OS_ReportTaskShutdownDone();
        break;

    case SIG_APP_ANOTHER:
        TM_printlf("**Another**");
        TM_printflush();
        break;

    case SIG_APP_HELLO:
        TM_printlf("Msg: %s", (char *)pSignal->Data.Transac);
        TM_printflush();

        // Create signal to send
        pNewSignal = OS_AllocSignal(SIG_SEND_HCI_MESSAGE, sizeof(tApp_HCI_Message));
        pAppHciMessage = (tApp_HCI_Message *)OS_GetSignalDataPtr(pNewSignal);
        memset(pAppHciMessage, 0, sizeof(tApp_HCI_Message));

        // Fill in the message fields and send the message
        pAppHciMessage->Opcode = HCI_READ_BD_ADDR;
        pAppHciMessage->Length = 0;
        OS_Send(OS_APP, pNewSignal);

        // Setup interrupt on GPIO 4, Set as a weak pull up IO
#ifdef GPIO_INTERRUPT_EXAMPLE
        Targ_EnablePioInterrupt(4, GPIO_Mode_IPU, SIG_APP_GPIO_INTERRUPT);
#endif
        break;

    case SIG_SEND_HCI_MESSAGE:
        // Get message from signal and send an HCI command
        pAppHciMessage = (tApp_HCI_Message*)OS_GetSignalDataPtr(pSignal);
        bti_SendCommand(pAppHciMessage->Opcode,
                        pAppHciMessage->Length,
                        pAppHciMessage->Params,
                        App_CommandComplete);
        // Send a timed signal
        OS_TimerRegisterRequest(1, APP_TMRID_BOND, 2000, SIG_APP_BOND);
        break;

    case SIG_APP_BOND:
        TM_printlf("Bonding...");
        TM_printflush();
        // Create signal to send
        pNewSignal = OS_AllocSignal(SIG_AMP_GAP_BOND, sizeof(tSIG_AMPGapBond));
        pBondMsg = (tSIG_AMPGapBond *)OS_GetSignalDataPtr(pNewSignal);
        memset(pBondMsg, 0, sizeof(tSIG_AMPGapBond));
        // Fill in the message fields and send the message
        pBondMsg->BdAddr = *(tBdAddr *)&BondAddress;
        pBondMsg->PinLength = strlen(Pin);
        memcpy(pBondMsg->Pin, Pin, strlen(Pin));
        OS_Send(OS_AMP, pNewSignal);
        //---------------------------------------
        SendMessage("Text in buffer. Success!");
        break;

    case SIG_APP_MESSAGE:
        ShowMessage(pSignal);
        break;

    case SIG_AMP_GAP_RESULT:
        pGapResult = (tSIG_AMPGapResult *)OS_GetSignalDataPtr(pSignal);
        switch(pGapResult->ResultCode)
        {
        case AMP_RESULT_ILLEGAL_OPERATION:
            TM_printlf("Illegal operation");
            TM_printflush();
            break;

        case AMP_RESULT_PENDING:
            TM_printlf("Result pending");
            TM_printflush();
            break;

        case AMP_WRONG_MODE:
            TM_printlf("Wrong Mode");
            TM_printflush();
            break;
        }
        break;

    case SIG_APP_GPIO_DEBOUNCE:
        LastValue = pSignal->Data.Transac;
        TM_printlf(LastValue ? "High":"Low", LastValue);
        TM_printflush();
        break;

    case SIG_APP_GPIO_INTERRUPT:
        pExtInterrupt = (tSIG_ExtInterrupt *)OS_GetSignalDataPtr(pSignal);
        if (pExtInterrupt->PioValue == LastValue) {
            TM_CancelTmrRequest(APP_TMRID_DEBOUNCE);
        }
        else {
            OS_TimerRegisterRequest(pExtInterrupt->PioValue, APP_TMRID_DEBOUNCE,
                                100, SIG_APP_GPIO_DEBOUNCE);
        }
        break;

    case SIG_AMP_SPP_RECV:
        pAMPSppRecv = (tSIG_AMPSppRecv*)OS_GetSignalDataPtr(pSignal);

        TM_print("BytesRead:  %d [", pAMPSppRecv->BytesRead);
        for (int i=0; i<pAMPSppRecv->BytesRead; i++) {
            int ch = pAMPSppRecv->pDataHdr->pData[i];
            TM_print("%c", (ch >=' ' && ch<='~') ? ch : '.');
        }
        TM_printlf("]");
        TM_printflush();
        if (pAMPSppRecv->BytesRead>0) {
            ch = pAMPSppRecv->pDataHdr->pData[0];
            Node = ch>='0' && ch<='9' ? ch-'0' : 0;
        }

#ifdef CUSTOM_DEVICE_LIST_DEMO
        // Add device record
        ProfileID = OS_GetProfileID(pAMPSppRecv->PortHandle);
        if (ProfileID == IAP_PROFILE)
        {
            tApp_DeviceRecord NewPhone;
            tBdAddr RcvAddr;

            // get the BD address
            RcvAddr = OS_GetBdAddressFromHandle(pAMPSppRecv->PortHandle);
            NewPhone.DeviceAddress = RcvAddr;

            memcpy(NewPhone.DeviceAddress.Byte,RcvAddr.Byte,6);

            memcpy(NewPhone.PhoneNumber,TestPhoneNum,10);
            NewPhone.PhoneNumber[9] = pAMPSppRecv->pDataHdr->pData[0];  // use data as the last phone number digit
            NewPhone.DeviceType = IOS_DEVICE;
            CustAddDevice(&NewPhone);
            //CustRemoveDeviceByPhoneNumber(NewPhone.PhoneNumber);
        }
#endif
        OS_Release(pAMPSppRecv->pDataHdr);

        //----------------------------------------------------
#ifdef ACK_CUSTOM
        if (PortHandle = OS_GetCurrentPortHandle())
        {
            msg[0] = 0x00;
            msg[1] = 0x05;
            msg[2] = 0x04;
            msg[3] = 0x00;
            msg[4] = 0x24;
            AT_SendDataToHandle(PortHandle, msg, 5);
        }
#endif
#ifdef DEMO_ACK
#ifdef MULTIPOINT
        //char txt[32];
        sprintf(msg, "Got %d chars\r\n", pAMPSppRecv->BytesRead);
        //sprintf(msg, "%d%03d%s", Node, strlen(txt), txt);
        AT_SendDataToNode(Node, msg, strlen(msg));
#else
        if (PortHandle = OS_GetCurrentPortHandle()) {
            sprintf(msg, "Got %d chars\r\n", pAMPSppRecv->BytesRead);
            AT_SendDataToHandle(PortHandle, msg, strlen(msg));
        }
#endif
#endif
        break;

#ifdef CONNECT_DISCONNECT_EXAMPLE
    case SIG_AMP_SPP_LISTEN_CON:
        pSppListenCon = (tSIG_AMPSppListenCon*)OS_GetSignalDataPtr(pSignal);
        SppDisconnect(pSppListenCon->PortName, 1);
        break;

    case SIG_AMP_SPP_CONNECT_CON:
        pSppConnectCon = (tSIG_AMPSppConnectCon*)OS_GetSignalDataPtr(pSignal);
        SppDisconnect(pSppConnectCon->PortName, 0);
        break;
#endif

#ifdef APPLE_CP_SLEEP
    case SIG_AMP_SPP_LISTEN_CON:
    case SIG_AMP_SPP_CONNECT_CON:
        OS_TimerRegisterRequest(1, APP_TMRID_APPLE_CP_SLEEP_DELAY, APP_APPLE_CP_SLEEP_DELAY, SIG_APP_APPLE_SLEEP_TIMEOUT);
        break;

    case SIG_APP_APPLE_SLEEP_TIMEOUT:
      AppleCPSleep();
      break;
#endif

#ifdef APPLE_CP_PWR_CONTROL
    case SIG_AMP_SPP_LISTEN_CON:
    case SIG_AMP_SPP_CONNECT_CON:
        OS_TimerRegisterRequest(1, APP_TMRID_APPLE_CP_PWR_DELAY, APP_APPLE_CP_PWR_DELAY, SIG_APP_APPLE_PWR_TIMEOUT);
        break;

    case SIG_APP_APPLE_PWR_TIMEOUT:
      //TM_printlf("apple cp pwr off");
      //TM_printflush();
      TM_ResetPio(APPLE_CP_PWR_PIN);  // turn off the pwr transistor for the Apple CP
      break;
#endif


#ifdef RECONNECT_EXAMPLE
    case SIG_AMP_SPP_LISTEN_CON:
    case SIG_AMP_SPP_CONNECT_CON:
        pSppListenCon  = (tSIG_AMPSppListenCon*)OS_GetSignalDataPtr(pSignal);
        pSppConnectCon = (tSIG_AMPSppConnectCon*)OS_GetSignalDataPtr(pSignal);
        if (pSppListenCon->Status == HCI_SUCCESS)
        {
            OS_CancelTmrRequest(APP_TMRID_CONNECT_INTERVAL);
            OS_TimerRegisterRequest(1, APP_TMRID_CONNECT_INTERVAL, 30000, SIG_APP_CONNECT_INTERVAL_TIMEOUT);
        }
        break;
#endif

    case SIG_AMP_SPP_DISCONNECT_IND:
    case SIG_AMP_SPP_DISCONNECT_CON:
        pSppDisconnectCon = (tSIG_AMPSppDisconnectCon*)OS_GetSignalDataPtr(pSignal);
        NOP;
        break;

    case SIG_AMP_GAP_LL_CONN_DOWN:
        break;

#ifdef SDK_MESSAGE_TEST
    case SIG_APP_MESSAGE_TEST:
        SDK_MessageTest();
        break;
#endif

#ifdef CONNECT_DISCONNECT_EXAMPLE
    case SIG_APP_DISCONNECT_INTERVAL_TIMEOUT:
        SppDisconnect(PortName, PortRole);
        break;
#endif

    case SIG_APP_WAKEUP:
#ifdef WAKEUP_EXAMPLE
        {
            tSIG_Wakeup * pWakeup;
            pWakeup = (tSIG_Wakeup*)OS_GetSignalDataPtr(pSignal);
            TM_printlf("GPIO States: Old 0x%04X, New 0x%04X",
                       pWakeup->OldPioState, pWakeup->NewPioState);
            TM_printflush();
        }
#endif
        break;

    case SIG_AMP_IAP_AUTH_COMPLETE:
        // Launch application when apple authorization is complete
        iAP_AutoLaunchApp();
        break;

    default:
        // Unhandled signal
        break;
    }

    // Release the signal
    OS_ReleaseSignal(pSignal);
}


/*******************************************************************************
* Function Name  : AppleCPSleep
* Description    : Froces the Apple CP to sleep
********************************************************************************/
#ifdef APPLE_CP_SLEEP
static void AppleCPSleep(void)
{
  //Write Authentication Control:PROC_CONTROL = 4
  SendToAuthenticationControl(FORCE_SLEEP); // force Apple CP to sleep now
  //TM_printlf("apple cp sleep");
  //TM_printflush();
}
#endif

/*******************************************************************************
* Function Name  : APP_InitTask
* Description    : Initialize App layer
********************************************************************************/
void APP_InitTask(void)
{
    // Initialize your variables here
}




/*******************************************************************************
* Function Name  : APP_InitModule
* Description    :
*       1. create APP module tasks
*       2. task independent functions or variables initialization
********************************************************************************/
void APP_InitModule(void)
{
    OS_RegisterDispatchFunc(OS_APP, APP_DispatchSignal);

    memset(&AppVar, 0, sizeof(AppVar));
    AppVar.TaskId = OS_CreateTask(
       "App",
       APP_InitTask,
       APP_ReceiveSignal,
       OS_DEFAULT_PRIO);
}

/*******************************************************************************
* Example Functions
*
* Various examples of useful functions to interact with Bluetooth features
*
********************************************************************************/



/*******************************************************************************
*
*  This function adds a device to a custom user device list
*     phone's BD Address
*     10 digit phone number
*     iOS device flag
*
********************************************************************************/
#ifdef CUSTOM_DEVICE_LIST_DEMO
static
uint8 CustAddDevice(tApp_DeviceRecord *pNewRecord)
{
    tApp_DeviceTable  *pDeviceTable;
    tApp_DeviceRecord *pFlashRecord;
    uint8 NumRecords;
    uint8 i;

    pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);
    if(pDeviceTable == NULL)
    {
        // table not initialized
        TM_printlf("InitTable");
        pDeviceTable = TM_GetFlashWritePtr(VAR_ID_DeviceTable,sizeof(tApp_DeviceTable));
        pDeviceTable->RecordsInUse = 0;
        TM_FlashWriteDone(VAR_ID_DeviceTable);
        pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);
    }

    NumRecords = pDeviceTable->RecordsInUse;
    if(NumRecords >= MAX_DEVICE_RECORDS) {
        return 0;  // table is full, could not write
    }

    // now check for a match by phone number field
    for(i=0; i<MAX_DEVICE_RECORDS; i++) {
      pFlashRecord = &pDeviceTable->DeviceRecord[i];
      if (memcmp(pFlashRecord->PhoneNumber, pNewRecord->PhoneNumber,PHONE_NUMBER_LEN)==0) {
        return 0;  // already exists in the table
      }
    }

    pDeviceTable = TM_GetFlashWritePtr(VAR_ID_DeviceTable,sizeof(tApp_DeviceTable));

    // set pointer to the empty record in flash, assume a sorted table
    pFlashRecord = &pDeviceTable->DeviceRecord[NumRecords];

    // save the phone's address
    memcpy(pFlashRecord->DeviceAddress.Byte,pNewRecord->DeviceAddress.Byte,BD_ADDR_SIZE);

    // save the phone number
    memcpy(pFlashRecord->PhoneNumber,pNewRecord->PhoneNumber,PHONE_NUMBER_LEN);

    // save iOS flag
    pFlashRecord->DeviceType = pNewRecord->DeviceType;

    // increment the number of records in use
    pDeviceTable->RecordsInUse = ++NumRecords;

    // now save to flash memory
    TM_FlashWriteDone(VAR_ID_DeviceTable);
    //TM_print("Address: ");
    //for(i=0;i<6;i++)
    //{
    //  TM_print("%2.2x",pFlashRecord->DeviceAddress.Byte[5-i]);
    //}
    //TM_printlf(NULL);
    TM_printlf("Device Added: %d",NumRecords);
    //TM_print("PhoneNum: ");
    //for(i=0;i<10;i++)
    //{
    //  TM_print("%x",pFlashRecord->PhoneNumber[i]);
    //}
    //TM_printlf(NULL);

    return NumRecords;  // return the number of devices, or 0 for a full table
}
#endif
/*******************************************************************************
*
*  This function removes a device from the custom device list.
*  The device with matching phone number is searched and removed.
*
********************************************************************************/
//static
bool CustRemoveDeviceByPhoneNumber(uint8 *pPhoneNum)
{
    tApp_DeviceTable  *pDeviceTable;
    tApp_DeviceRecord *pFlashRecord,*pSortingRecord;
    uint8 NumRecords;
    uint8 i,x;
    uint8  match = 0xff;  // start with 0xff

    pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);

    NumRecords = pDeviceTable->RecordsInUse;

    for(i=0;i<NumRecords;i++)
    {
        pFlashRecord = &pDeviceTable->DeviceRecord[i];
        if(memcmp(pFlashRecord->PhoneNumber,pPhoneNum,PHONE_NUMBER_LEN)==0)
        {
          match = i;
          break;
        }
    }

    if(match == 0xff)
    {
        return false;  // record not found
    }

    pDeviceTable = TM_GetFlashWritePtr(VAR_ID_DeviceTable,sizeof(tApp_DeviceTable));

    // sort the table
    for(x=match;x<NumRecords-1;x++)
    {
      // set pointer to the empty record in flash
      pFlashRecord = &pDeviceTable->DeviceRecord[x];
      pSortingRecord = &pDeviceTable->DeviceRecord[x+1];

      // save the phone's address
      memcpy(pFlashRecord->DeviceAddress.Byte,pSortingRecord->DeviceAddress.Byte,BD_ADDR_SIZE);

      // save the phone number
      memcpy(pFlashRecord->PhoneNumber,pSortingRecord->PhoneNumber,PHONE_NUMBER_LEN);

      // save iOS flag
      pFlashRecord->DeviceType = pSortingRecord->DeviceType;
    }

    // erase the last record
    pSortingRecord = &pDeviceTable->DeviceRecord[NumRecords-1];
    memset(pSortingRecord,0xff,sizeof(tApp_DeviceRecord));

    // decrement the number of records in use
    if(NumRecords>0)
    {
      pDeviceTable->RecordsInUse = --NumRecords;
    }

    // now save to flash memory
    TM_FlashWriteDone(VAR_ID_DeviceTable);

    TM_printlf("Device Removed: %d",NumRecords);
    return true;  // device was found and deleted from table
}

/*******************************************************************************
*
*  Use this function to remove a device from the pairing table in the module
*
*  tBdAddr RemoveBdAddr:  This is the address to remove using the
*                         AMP_FLASHFLAG_DELETE_BDADDR option
*
********************************************************************************/
void RemoveDevice(tBdAddr RemoveBdAddr)
{
  tOS_Signal                   *pSignal = NULL;
  tSIG_AMPDeleteDeviceDatabase *pDeleteDevDB = NULL;

  // Delete Device from DB
  pSignal= OS_AllocSignal(SIG_AMP_DELETE_DEVICE_DATABASE, sizeof(tSIG_AMPDeleteDeviceDatabase));
  pDeleteDevDB= (tSIG_AMPDeleteDeviceDatabase*)OS_GetSignalDataPtr(pSignal);
  pDeleteDevDB->Transac = AT_GAP_RESULT_DELETEDB_BDADDR;

  memcpy(pDeleteDevDB->BdAddr.Byte, RemoveBdAddr.Byte, BD_ADDR_SIZE);

  pDeleteDevDB->Flag = AMP_FLASHFLAG_DELETE_BDADDR; // or AMP_FLASHFLAG_DELETE_ALL

  // send signal
  OS_Send(OS_AMP, pSignal);
}

/*******************************************************************************
*
*  Use these two functions to list the paired devices
*    call ShowPairedDevices() only
*
********************************************************************************/
void ShowPairedDevices(void)
{
    int i, j, id, devcount=0;
    tDeviceDBInformation *pInfo, *save;
    tDeviceDBInformation *pDBInfoList[MAX_NUM_OF_DEVICES];
    uint32 NxtSeq;

    // Get list
    devcount = GetDeviceList(pDBInfoList, &NxtSeq);

    // Bubble sort recs
    for (i=0; i<devcount; i++) {
        for (j=i+1; j<devcount; j++) {
            if ((pDBInfoList[i] == NULL) ||
                (pDBInfoList[i]->Sequence > pDBInfoList[j]->Sequence))
            {
                save = pDBInfoList[i];
                pDBInfoList[i] = pDBInfoList[j];
                pDBInfoList[j] = save;
            }
        }
    }


    for (i=0; i<devcount; i++)
    {
        // the database location for TM_FLASH_DEVICE_DB is TMFM_1ST_DYNAMIC_ID+4
        id = (TMFM_1ST_DYNAMIC_ID+4) + i;
        pInfo = pDBInfoList[i];
        if (pInfo)
        {
            // bd_addr
            for (j=0; j<6; j++) {
                TM_print("%02x", pInfo->BdAddr.Byte[5-j]);
            }
            TM_printlf("");
        }
    }
    TM_printflush();
}

/*******************************************************************************
*
*  Used in the above function
*
********************************************************************************/
static uint16 GetDeviceList(tDeviceDBInformation **ppInfo, uint32 *NxtSeq)
{
    uint16 i, devcount=0;
    uint32 HiSeq=0;
    tDeviceDBInformation *pRecInfo;

    for (i=0; i<MAX_NUM_OF_DEVICES; i++)
    {
        pRecInfo = TM_GetFlashReadPtr((TMFM_1ST_DYNAMIC_ID+4) + i);
        if (pRecInfo) {
            HiSeq  = MAX(HiSeq, pRecInfo->Sequence);
            *ppInfo = pRecInfo;
            devcount++;
        }
        else
            *ppInfo = NULL;
        ppInfo++;
    }

    *NxtSeq = HiSeq+1;

    return devcount;
}

/*******************************************************************************
*
*  Use this function to disconnect an existing IAP (iPhone) link
*    NodeId: this is the dynamic node id used in multipoint mode
*            Determines which device to disconnect
*
********************************************************************************/
void IapDisconnect(uint8 NodeId)
{
    tOS_Signal                   *pSignal;
    tIapDisconnectReq            *pDisconReq;

    pSignal= BP_AllocSignal(SIG_SEND_IAP_DISCONNECT_REQ, sizeof(tIapDisconnectReq));
    pDisconReq = (tIapDisconnectReq*)OS_GetSignalDataPtr(pSignal);
    pDisconReq->Node = NodeId;
    OS_Send(OS_AT,pSignal);
}

/*******************************************************************************
*
*  Use this function to make a remote connection to a given IOS (iPhone) device
*    pAddr: this is the remote address to connect with
*
********************************************************************************/
#ifdef RECONNECT_EXAMPLE
static void IapConnect(tBdAddr *pAddr)
{
    tOS_Signal           *pSignal;
    tIapConnectReq       *pConnectReq;

    TM_print("Attempting to connect to ");
    for (int i=0; i<6; i++)
        TM_print("%02x", pAddr->Byte[5-i]);
    TM_printlf("");
    TM_printflush();

    pSignal= BP_AllocSignal(SIG_SEND_IAP_RECONNECT_REQ, sizeof(tIapConnectReq));
    pConnectReq = (tIapConnectReq*)OS_GetSignalDataPtr(pSignal);
    memcpy(pConnectReq->RemoteAddr.Byte, pAddr->Byte, BD_ADDR_SIZE);
    OS_Send(OS_AT,pSignal);
}
#endif

//--------------------------------------------------------------------------
// Show Device Records for connection
//--------------------------------------------------------------------------
#ifdef RECONNECT_EXAMPLE
void ShowDeviceRecords(void)
{
    tApp_DeviceTable  *pDeviceTable;
    tApp_DeviceRecord *pFlashRecord;
    uint8 i, j;

    // Get Device Table
    pDeviceTable = TM_GetFlashReadPtr(VAR_ID_DeviceTable);

    // Check
    if (pDeviceTable==NULL) {
        TM_printlf("No Device Table Found");
    }

    // Show Table
    else {
        TM_printlf("RecordsInUse = %d", pDeviceTable->RecordsInUse);
        TM_printlf(" # ID BD_ADDR");
        for (i=0; i<MAX_DEVICE_RECORDS; i++) {
            pFlashRecord = &pDeviceTable->DeviceRecord[i];
            TM_print("%2d %2d ", i, pFlashRecord->DeviceType);
            for (j=0; j<6; j++) {
                TM_print("%02x", pFlashRecord->DeviceAddress.Byte[5-j]);
            }
            TM_printlf("");
        }
    }

    TM_printflush();
}
#endif

/*******************************************************************************
* Function Name  : SDK_MessageTest
* Description    : Send a test message to other device
                   (period: xx seconds   APP_MESSAGE_TEST_INTERVAL is timer)
*******************************************************************************/
#ifdef SDK_MESSAGE_TEST
void SDK_MessageTest(void)
{
    char msg[64];
    uint16 PortHandle;
    uint16 MaxNodes = 10;
    static uint8 NodeID = 0;

    // Point to Point
    if (MP_MODE == eAT_PointToPoint)
    {
        PortHandle = OS_GetCurrentPortHandle();
        if (PortHandle) {
            sprintf(msg, "SDK message sent to handle %03x\r\n", PortHandle);
            AT_SendDataToHandle(PortHandle, msg, strlen(msg));
            TM_print(msg);
            TM_printflush();
        }
    }

    // Multi-Point
    else
    {
        for (int i=0; i<MaxNodes; i++)
        {
            if (++NodeID >= MaxNodes) {
                NodeID = 0;
            }
            if (OS_IsNodeConnected(NodeID)) {
                sprintf(msg, "SDK message sent to node %d, handle %03x\r\n", NodeID, OS_GetHandleFromNode(NodeID));
                AT_SendDataToNode(NodeID, msg, strlen(msg));
                TM_print(msg);
                TM_printflush();
                break;
            }
        }
    }

    // restart another timer now
    OS_TimerRegisterRequest(0, APP_TMRID_MESSAGE_TEST, APP_MESSAGE_TEST_INTERVAL, SIG_APP_MESSAGE_TEST);
}
#endif

//
//=============================================================================
#endif // APP_EXAMPLE
#endif // SDK
