/*
 *****************************************************************************
 *                                                                           *
 *                 IMPINJ CONFIDENTIAL AND PROPRIETARY                       *
 *                                                                           *
 * This source code is the sole property of Impinj, Inc.  Reproduction or    *
 * utilization of this source code in whole or in part is forbidden without  *
 * the prior written consent of Impinj, Inc.                                 *
 *                                                                           *
 * (c) Copyright Impinj, Inc. 2009. All rights reserved.                     *
 *                                                                           *
 *****************************************************************************
 */

/*
 *****************************************************************************
 *
 * $Id: inventory_live.c 67085 2011-03-24 22:15:12Z dshaheen $
 *
 * Description:
 *     This is a simple sample application for performing the following functions:
 *     - Start up the RFID Reader Library
 *     - Enumerate the attached radios
 *     - Open each Radio in turn and
 *       - Retrieve the MAC version
 *       - Retrieve the response mode setting so it can be restored at the end
 *       - Set the response mode packets to normal
 *       - Perform an inventory
 *       - Restore the response mode setting to original value
 *       - Close the radio
 *     - Shut down the library
 *
 *
 *****************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rfid_library.h"
#include "rfid_packets.h"
#include "print_packet.h"
#include "byte_swap.h"
#include "sample_utility.h"


INT32S RFID_CALLBACK PacketCallbackFunction(
    RFID_RADIO_HANDLE   handle,
    INT32U              bufferLength,
    const INT8U*        pBuffer,
    void *              context
    );

INT32S cycleLimit = 0;

/**
    Simple help triggered by running with a -h or
    invalid program arguments.
**/

void print_help(char *prog_name )
{
    printf("\nUsage: %s [-h] [-fi] [-tf] [-ab] -f=n \n\n", prog_name );

    printf("where:\n" );
    printf("  -h        Prints this help screen\n" );
    printf("  -fi       Enables FastID\n" );
    printf("  -tf       Enables TagFocus\n");
    printf("  -ab       Enables additional TagFocus inventory criteria (A->B inventory),\n");
    printf("              but does not enable the TagFocus extension itself.\n");
    printf("  -f=n      Sets the flag word passed to the inventory API to the value n.\n");
    printf("              where bit 0 enables/disables previously defined select criteria,\n");
    printf("              and bit 1 enables/disables previously defined post match filtering criteria.\n");
    printf("  -c=n      Sets the 'cycleLimit': stop continuous inventory after 'cycleLimit' cycles.\n");

    printf("Example: %s -fi \n", prog_name );
}

int main(int argc, char** argv)
{
    RFID_STATUS                             status;
    RFID_VERSION                            libraryVersion;
    RFID_RADIO_ENUM*                        pEnum;
    INT32U                                  index;
    RFID_RADIO_HANDLE                       handle;
    RFID_18K6C_INVENTORY_PARMS              inventoryParms;
    int                                     indent_level = 0;
    RFID_RESPONSE_MODE                      originalResponseMode;
    int                                     i;
    BOOL32                                  tagFocusEnabled = 0;
    BOOL32                                  abInventoryEnabled = 0;
    BOOL32                                  fastIdEnabled = 0;
    INT32U                                  inventoryFlags = 0;
    char *                                  programName = argv[0];
    RFID_18K6C_TAG_GROUP                    originalTagGroup;
    RFID_18K6C_TAG_GROUP                    tagGroup;
    RFID_18K6C_SINGULATION_ALGORITHM        originalAlgorithm;
    RFID_18K6C_SINGULATION_DYNAMICQ_PARMS   originalAlgorithmParameters;
    RFID_18K6C_SINGULATION_DYNAMICQ_PARMS   algorithmParameters;
    RFID_IMPINJ_EXTENSIONS                  originalExtensions;
    RFID_IMPINJ_EXTENSIONS                  extensions;


    /* parse optional command line arguments */

    for ( i = 1 ; i < argc ; ++ i )
    {
        if ( 0 == strcmp( argv[ i ], "-h" ) )
        {
            print_help(programName);
            return -1;
        }
        else if ( 0 == strcmp( argv[ i ], "-tf" ) )
        {
            printf( "TagFocus enabled\n");
            tagFocusEnabled = 1;

            /* tag focus also requires these settings to be applied */
            abInventoryEnabled    = 1;
        }
        else if ( 0 == strcmp( argv[ i ], "-fi" ) )
        {
            printf( "FastID enabled\n");
            fastIdEnabled = 1;
        }
        else if ( 0 == strcmp( argv[ i ], "-ab" ) )
        {
            printf( "A->B Inventory enabled\n");
            abInventoryEnabled = 1;
        }
        else if ( 0 == strncmp( argv[ i ], "-f=", 3) )
        {
            char *token;
            token = strtok(argv[i], "=");
            token = strtok(NULL,"=");
            inventoryFlags = atoi(token);
            printf( "flags=%04X\n", inventoryFlags);
        }
        else if ( 0 == strncmp( argv[ i ], "-c=", 3) )
        {
            char *token;
            token = strtok(argv[i], "=");
            token = strtok(NULL,"=");
            cycleLimit = atoi(token);
            printf( "cycleLimit=%d\n", cycleLimit);
        }
        else
        {
            printf("\nUnknown argument found: %s\n\n", argv[ i ] );
            print_help(programName);
            return -1;
        }
    }

    /* First open the library */
    status = RFID_Startup(&libraryVersion, 0);
    if (RFID_STATUS_OK != status)
    {
        printf("RFID_Startup() failed.  RC = %d\n", status);
        printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        goto EXIT;
    }

    /* Allocate a buffer for radio enumeration */
    pEnum = (RFID_RADIO_ENUM *) malloc(sizeof(RFID_RADIO_ENUM));
    if (NULL == pEnum)
    {
        printf("Failed to allocate memory\n");
        goto CLEANUP_1;
    }

    /* Initialize the enumeration buffer */
    pEnum->length      = sizeof(RFID_RADIO_ENUM);
    pEnum->totalLength = sizeof(RFID_RADIO_ENUM);

    /* Keep attempting to enumerate while the library says that the buffer is */
    /* too small.  This accounts for a situation where a radio may be plugged */
    /* in between the call to enumerate and the return with the previously    */
    /* needed buffer size.                                                    */
    while (RFID_ERROR_BUFFER_TOO_SMALL ==
        (status = RFID_RetrieveAttachedRadiosList(pEnum, 0)))
    {
        /* Allocate a new, larger buffer */
        RFID_RADIO_ENUM* pNewEnum =
            (RFID_RADIO_ENUM *) realloc(pEnum, pEnum->totalLength);
        if (NULL == pNewEnum)
        {
            printf("Failed to reallocate memory\n");
            goto CLEANUP_2;
        }
        pEnum = pNewEnum;
    }

    /* Verify that the enumeration was successful */
    if (RFID_STATUS_OK != status)
    {
        printf("RFID_RetrieveAttachedRadiosList failed:  RC = %d\n", status);
        printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        goto CLEANUP_2;
    }

    if (!pEnum->countRadios)
    {
        fprintf(stderr, "ERROR: No radios attached to the system\n");
        goto CLEANUP_2;
    }

    printf("RFID_RADIO_ENUM.length      = %u\n", pEnum->length);
    printf("RFID_RADIO_ENUM.totalLength = %u\n", pEnum->totalLength);
    printf("RFID_RADIO_ENUM.countRadios = %u\n", pEnum->countRadios);

    /* Set up the inventory parameters */
    inventoryParms.length                = sizeof(RFID_18K6C_INVENTORY_PARMS);
    inventoryParms.common.tagStopCount   = 0;
    inventoryParms.common.pCallback      = PacketCallbackFunction;
    inventoryParms.common.pCallbackCode  = NULL;
    inventoryParms.common.context        = &indent_level;

    /* For each radio in the list, open it, try to get its MAC version,    */
    /* try to perform an inventory, then close it                          */
    for (index = 0; index < pEnum->countRadios; ++index)
    {
        RFID_VERSION macVersion;

        printf("Radio %u:\n", index + 1);
        printf("\tStructure length: %u\n", pEnum->ppRadioInfo[index]->length);
        printf("\tCookie: 0x%.8x\n", pEnum->ppRadioInfo[index]->cookie);
        printf("\tDriver Version:\n");
        printf("\t\tMajor: %u\n",
            pEnum->ppRadioInfo[index]->driverVersion.major);
        printf("\t\tMinor: %u\n",
            pEnum->ppRadioInfo[index]->driverVersion.minor);
        printf("\t\tMaintenance: %u\n",
            pEnum->ppRadioInfo[index]->driverVersion.maintenance);
        printf("\t\tRelease: %u\n",
            pEnum->ppRadioInfo[index]->driverVersion.release);
        printf("\tUnique ID Length: %u\n", pEnum->ppRadioInfo[index]->idLength);
        PrintRadioUniqueId("Unique ID: ",
            pEnum->ppRadioInfo[index]->idLength,
            pEnum->ppRadioInfo[index]->pUniqueId);

        /* Open the radio */
        if (RFID_STATUS_OK !=
            (status =
            RFID_RadioOpen(pEnum->ppRadioInfo[index]->cookie, &handle, 0)))
        {
            printf("RFID_RadioOpen failed:  RC = %d\n", status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            continue;
        }

        /* Attempt to get the MAC version for the radio */
        if (RFID_STATUS_OK !=
            (status = RFID_MacGetVersion(handle, &macVersion)))
        {
            printf("RFID_MacGetVersion failed: RC = %d\n", status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }
        else
        {
            printf("\tMAC Version:\n");
            printf("\t\tMajor: %u\n", macVersion.major);
            printf("\t\tMinor: %u\n", macVersion.minor);
            printf("\t\tMaintenance: %u\n", macVersion.maintenance);
            printf("\t\tRelease: %u\n", macVersion.release);
        }

        /* Retrieve current response mode so it can be restored */
        status = RFID_RadioGetResponseDataMode(
                    handle,
                    (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                    &originalResponseMode);
        if (RFID_STATUS_OK != status)
        {
            printf("RFID_RadioGetResponseDataMode failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }

        /* Attempt to configure the MAC for the data response mode (packets) we want to see */
        status = RFID_RadioSetResponseDataMode(
                     handle,
                     (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                     RFID_RESPONSE_MODE_NORMAL);

        if (RFID_STATUS_OK != status)
        {
            printf("RFID_RadioSetResponseDataMode failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }

        /* Retrieve current QueryTagGroup settings.  These may be modified depending on
         * based on multiple input conditions, so get it once above the  conditional
         * logic, so it can be restored when we are done.
        */
        status =  RFID_18K6CGetQueryTagGroup(handle,
                    &originalTagGroup);
        if (RFID_STATUS_OK != status)
        {
            printf("RFID_18K6CGetQueryTagGroup failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }
        tagGroup  = originalTagGroup;

        if (tagFocusEnabled || fastIdEnabled)
        {
            /* ImpinjExtensions will be modified for both tag focus and fast ID
               so, get the original settings to later be restored
            */
            status = RFID_RadioGetImpinjExtensions(handle,
                        &originalExtensions);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_RadioGetImpinjExtensions failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

            /* update the extensions based on the enable inputs */
            extensions                  = originalExtensions;
            extensions.tagFocus         = tagFocusEnabled;
            extensions.fastId           = fastIdEnabled;

            status = RFID_RadioSetImpinjExtensions(handle,
                        &extensions);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_RadioSetImpinjExtensions failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }
        }

        /* if we are applying selects, then enable the selectAsserted condition */
        if (inventoryFlags & RFID_FLAG_PERFORM_SELECT)
        {
            tagGroup.selected = RFID_18K6C_SELECT_ASSERTED;
        }
        else /* otherwise, make sure select all is chosen */
        {
            tagGroup.selected = RFID_18K6C_SELECT_ALL;
        }

        if (abInventoryEnabled)
        {
            /*  Gather up inventory related parameters we are going to modify
                so they can be restored when we are done.
            */

            status = RFID_18K6CGetCurrentSingulationAlgorithm(handle,
                        &originalAlgorithm);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_18K6CGetCurrentSingulationAlgorithm failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

            originalAlgorithmParameters.length = sizeof(RFID_18K6C_SINGULATION_DYNAMICQ_PARMS);
            status = RFID_18K6CGetSingulationAlgorithmParameters(handle,
                        RFID_18K6C_SINGULATION_ALGORITHM_DYNAMICQ,
                        &originalAlgorithmParameters);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_18K6CGetSingulationAlgorithmParameters failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

            /* now modify the settings to enable A->B inventory */
            algorithmParameters = originalAlgorithmParameters;
            algorithmParameters.toggleTarget = 0;

            tagGroup.session                 = RFID_18K6C_INVENTORY_SESSION_S1;
            tagGroup.target                  = RFID_18K6C_INVENTORY_SESSION_TARGET_A;

            /* now set the modified settings in the radio */

            status = RFID_18K6CSetCurrentSingulationAlgorithm(handle,
                        RFID_18K6C_SINGULATION_ALGORITHM_DYNAMICQ);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_18K6CSetCurrentSingulationAlgorithm failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

            status = RFID_18K6CSetSingulationAlgorithmParameters(handle,
                        RFID_18K6C_SINGULATION_ALGORITHM_DYNAMICQ,
                        &algorithmParameters);
            if (RFID_STATUS_OK != status)
            {
                printf("RFID_18K6CSetSingulationAlgorithmParameters failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }


        } /* end if abInventoryEnabled */

        /* set the QueryTagGroup conditions in the radio */
        status =  RFID_18K6CSetQueryTagGroup(handle,
                    &tagGroup);
        if (RFID_STATUS_OK != status)
        {
            printf("RFID_18K6CSetQueryTagGroup failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }

		/* Attempt to change to continuous/non-continuous mode */
		status =  RFID_RadioSetOperationMode(handle, cycleLimit ?
					RFID_RADIO_OPERATION_MODE_CONTINUOUS : RFID_RADIO_OPERATION_MODE_NONCONTINUOUS);
		if (RFID_STATUS_OK != status)
		{
			printf("RFID_RadioSetOperationMode failed: RC = %d\n",status);
			printf("    Status:\t\t%s\n", RFIDStatusToString(status));
		}

        /* Attempt to perform an inventory on the radio */
        printf("Attempt to perform an inventory on the radio: begin\n");
        if (RFID_STATUS_OK !=
            (status = RFID_18K6CTagInventory(handle, &inventoryParms, inventoryFlags)))
        {
            printf("RFID_18K6CTagInventory failed: RC = %d\n", status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }
        printf("Attempt to perform an inventory on the radio: end\n");

        /* Restore the MAC data response mode (packets) we want to see */
        status = RFID_RadioSetResponseDataMode(
                     handle,
                     (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                     originalResponseMode);
        if (RFID_STATUS_OK != status)
        {
            printf("Restore RFID_RadioSetResponseDataMode failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }


        if (tagFocusEnabled || fastIdEnabled)
        {
            /* restore Impinj Extensions if either feature was enabled */
            status = RFID_RadioSetImpinjExtensions(handle,
                        &originalExtensions);
            if (RFID_STATUS_OK != status)
            {
                printf("Restore RFID_RadioSetImpinjExtensions failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }
        }

        if (abInventoryEnabled)
        {
            /* Restore the orginal settings which were modified */

            status = RFID_18K6CSetCurrentSingulationAlgorithm(handle,
                        originalAlgorithm);
            if (RFID_STATUS_OK != status)
            {
                printf("Restore RFID_18K6CSetCurrentSingulationAlgorithm failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

            status = RFID_18K6CSetSingulationAlgorithmParameters(handle,
                        RFID_18K6C_SINGULATION_ALGORITHM_DYNAMICQ,
                        &originalAlgorithmParameters);
            if (RFID_STATUS_OK != status)
            {
                printf("Restore RFID_18K6CSetSingulationAlgorithmParameters failed: RC = %d\n",status);
                printf("    Status:\t\t%s\n", RFIDStatusToString(status));
            }

        }

        /* restore the query controls to the original settings, whether they were modified or not */
        status =  RFID_18K6CSetQueryTagGroup(handle,
                    &originalTagGroup);
        if (RFID_STATUS_OK != status)
        {
            printf("Restore RFID_18K6CSetQueryTagGroup failed: RC = %d\n",status);
            printf("    Status:\t\t%s\n", RFIDStatusToString(status));
        }


        RFID_MacClearError(handle);

        /* Close the radio */
        RFID_RadioClose(handle);
    }

CLEANUP_2:
    /* Free the dynamically alloated memory */
    free(pEnum);

CLEANUP_1:
    /* Shut down the library */
    RFID_Shutdown();

EXIT:
    return 0;
}

INT32S PacketCallbackFunction(
    RFID_RADIO_HANDLE   handle,
    INT32U              bufferLength,
    const INT8U*        pBuffer,
    void *              context
    )
{
    int *indent = (int *)context;
    INT32S rc;

    RFID_UNREFERENCED_LOCAL(handle);

    rc = PacketTrace(bufferLength, pBuffer, indent, &cycleLimit);
//	printf("PacketCallbackFunction, cycleLimit=%d, rc=%d\n", cycleLimit, rc);
    if(rc) {
    	printf("PacketCallbackFunction, rc=%d\n", rc);
    }
    return(rc);
}
