/*
 * Copyright (C) 2016 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <android/sensor.h>

struct SensorConfig {
    int listIndex;
    int type;
    int32_t rate;
    int64_t reportLatency;
    bool receivedEvent;
};


ASensorManager *mSensorManager;
ASensorList mSensorList;
int mNumSensors;
bool mContinuousMode;
SensorConfig mSensorConfigList[16];
int mNumSensorConfigs;

void showHelp()
{
    printf("Usage: sensortest [-h] [-l] [-e <type> <rate_usecs>] [-b <type> <rate_usecs> <batch_usecs>] [-c]\n");
}

void printSensorList()
{
    int prevMinType = -1;
    int currMinType;
    int currMinIndex = 0;

    printf("[Type] - Name\n");

    for (int i = 0; i < mNumSensors; i++) {
        currMinType = INT_MAX;

        for (int j = 0; j < mNumSensors; j++) {
            if ((ASensor_getType(mSensorList[j]) > prevMinType) &&
                (ASensor_getType(mSensorList[j]) < currMinType)) {
                currMinType = ASensor_getType(mSensorList[j]);
                currMinIndex = j;
            }
        }

        printf("[%d] = \"%s\"\n", currMinType, ASensor_getName(mSensorList[currMinIndex]));

        prevMinType = currMinType;
    }
}

int findSensorTypeInSensorList(int type)
{
    for (int i = 0; i < mNumSensors; i++) {
        if (ASensor_getType(mSensorList[i]) == type) {
            return i;
        }
    }

    return -1;
}

int findSensorTypeInConfigList(int type)
{
    for (int i = 0; i < mNumSensorConfigs; i++) {
        if (mSensorConfigList[i].type == type) {
            return i;
        }
    }

    return -1;
}

bool parseArguments(int argc, char **argv)
{
    int currArgumentIndex = 1;
    int sensorIndex;
    int existingSensorConfigIndex;

    mNumSensorConfigs = 0;

    while (currArgumentIndex < argc) {
        if (!strcmp(argv[currArgumentIndex], "-h")) {
            return false;
        } else if (!strcmp(argv[currArgumentIndex], "-l")) {
            printSensorList();
            currArgumentIndex++;
        } else if (!strcmp(argv[currArgumentIndex], "-e")) {
            if (currArgumentIndex + 2 >= argc) {
                printf ("Not enough arguments for enable option\n");
                return false;
            }

            if ((sensorIndex = findSensorTypeInSensorList(atoi(argv[currArgumentIndex+1]))) < 0) {
                printf ("No sensor found with type \"%d\"\n", atoi(argv[currArgumentIndex+1]));
                return false;
            }

            existingSensorConfigIndex = findSensorTypeInConfigList(atoi(argv[currArgumentIndex+1]));

            if (existingSensorConfigIndex >= 0) {
                printf("Replacing previous config for sensor type %d\n", atoi(argv[currArgumentIndex+1]));
                mSensorConfigList[existingSensorConfigIndex] = (SensorConfig) {
                    .listIndex = sensorIndex,
                    .type = atoi(argv[currArgumentIndex+1]),
                    .rate = atoi(argv[currArgumentIndex+2]),
                    .reportLatency = 0,
                    .receivedEvent = false
                };
            } else {
                mSensorConfigList[(mNumSensorConfigs)++] = (SensorConfig) {
                    .listIndex = sensorIndex,
                    .type = atoi(argv[currArgumentIndex+1]),
                    .rate = atoi(argv[currArgumentIndex+2]),
                    .reportLatency = 0,
                    .receivedEvent = false
                };
            }

            currArgumentIndex += 3;
        } else if (!strcmp(argv[currArgumentIndex], "-b")) {
            if (currArgumentIndex + 3 >= argc) {
                printf ("Not enough arguments for batch option\n");
                return false;
            }

            if ((sensorIndex = findSensorTypeInSensorList(atoi(argv[currArgumentIndex+1]))) < 0) {
                printf ("No sensor found with type \"%d\"\n", atoi(argv[currArgumentIndex+1]));
                return false;
            }

            existingSensorConfigIndex = findSensorTypeInConfigList(atoi(argv[currArgumentIndex+1]));

            if (existingSensorConfigIndex >= 0) {
                printf("Replacing previous config for sensor type %d\n", atoi(argv[currArgumentIndex+1]));
                mSensorConfigList[existingSensorConfigIndex] = (SensorConfig) {
                    .listIndex = sensorIndex,
                    .type = atoi(argv[currArgumentIndex+1]),
                    .rate = atoi(argv[currArgumentIndex+2]),
                    .reportLatency = atoi(argv[currArgumentIndex+3]),
                    .receivedEvent = false
                };
            } else {
                mSensorConfigList[(mNumSensorConfigs)++] = (SensorConfig) {
                    .listIndex = sensorIndex,
                    .type = atoi(argv[currArgumentIndex+1]),
                    .rate = atoi(argv[currArgumentIndex+2]),
                    .reportLatency = atoi(argv[currArgumentIndex+3]),
                    .receivedEvent = false
                };
            }

            currArgumentIndex += 4;
        } else if (!strcmp(argv[currArgumentIndex], "-c")) {
            mContinuousMode = true;
            currArgumentIndex++;
        } else {
            printf("Invalid argument \"%s\"\n", argv[currArgumentIndex]);
            return false;
        }
    }

    return true;
}

bool hasReceivedAllEvents()
{
    for (int i = 0; i < mNumSensorConfigs; i++) {
        if (!mSensorConfigList[i].receivedEvent) {
            return false;
        }
    }

    return true;
};

int main(int argc, char **argv) {
    int numSensorEvents;
    ASensorEvent sensorEvents[16];
    int configListIndex;

    mSensorManager = ASensorManager_getInstanceForPackage("");
    mNumSensors = ASensorManager_getSensorList(mSensorManager, &mSensorList);

    if ((argc == 1) || !parseArguments(argc, argv)) {
        showHelp();
        return -1;
    }

    if (mNumSensorConfigs <= 0)
        return 0;

    ALooper *mLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    ASensorEventQueue *sensorEventQueue = ASensorManager_createEventQueue(mSensorManager, mLooper, 0, NULL, NULL);

    for (int i = 0; i < mNumSensorConfigs; i++) {
        if (ASensorEventQueue_registerSensor(sensorEventQueue, mSensorList[mSensorConfigList[i].listIndex],
                                             mSensorConfigList[i].rate, mSensorConfigList[i].reportLatency) < 0) {
            printf("Unable to register sensor %d with rate %d and report latency %" PRId64 "\n",
                   mSensorConfigList[i].listIndex, mSensorConfigList[i].rate,
                   mSensorConfigList[i].reportLatency);
        }

    }

    while (mContinuousMode || !hasReceivedAllEvents()) {
        if ((numSensorEvents = ASensorEventQueue_getEvents(sensorEventQueue, sensorEvents, 16)) < 0) {
            printf("An error occurred while polling for events\n");
            break;
        } else if (numSensorEvents > 0) {
            for (int i = 0; i < numSensorEvents; i++) {
                if ((configListIndex = findSensorTypeInConfigList(sensorEvents[i].type)) < 0) {
                    printf("Received unexpected event for type %d\n", sensorEvents[i].type);
                    break;
                }

                if (mContinuousMode || !mSensorConfigList[configListIndex].receivedEvent) {
                    printf("[%d] = %f, %f, %f @ %" PRId64 "\n", sensorEvents[i].type,
                           sensorEvents[i].data[0], sensorEvents[i].data[1],
                           sensorEvents[i].data[2], sensorEvents[i].timestamp);

                    mSensorConfigList[configListIndex].receivedEvent = true;

                    if (!mContinuousMode) {
                        ASensorEventQueue_disableSensor(sensorEventQueue, mSensorList[mSensorConfigList[configListIndex].listIndex]);
                    }
                }
            }
        }

        fflush(stdout);
    }

    ASensorManager_destroyEventQueue(mSensorManager, sensorEventQueue);

    return 0;
}