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

#ifndef ANDROID_SENSORHAL_EXT_DYNAMIC_SENSOR_MANAGER_H
#define ANDROID_SENSORHAL_EXT_DYNAMIC_SENSOR_MANAGER_H

#include "SensorEventCallback.h"
#include "RingBuffer.h"
#include <hardware/sensors.h>
#include <utils/RefBase.h>

#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

namespace android {
namespace SensorHalExt {

class BaseDynamicSensorDaemon;

class DynamicSensorManager : public SensorEventCallback {
public:
    // handleBase is reserved for the dynamic sensor meta sensor.
    // handleMax must be greater than handleBase + 1.
    // This class has two operation mode depending on callback: 1) extension, 2) stand-alone.
    // In extension mode, callback must not be nullptr. Sensor event generated will be submitted to
    // buffer of primary sensor HAL implementation. In stand-alone mode, callback must be nullptr.
    // Generated sensor events will be added into internal buffer waiting for poll() function to
    // pick up.
    //
    static DynamicSensorManager* createInstance(
            int handleBase, int handleCount, SensorEventCallback *callback);
    virtual ~DynamicSensorManager();

    // calls to add or remove sensor, called from sensor daemon
    bool registerSensor(sp<BaseSensorObject> sensor);
    void unregisterSensor(sp<BaseSensorObject> sensor);

    // Determine if a sensor handle is in the range defined in constructor.
    // It does not test if sensor handle is valid.
    bool owns(int handle) const;

    // handles sensor hal requests.
    int activate(int handle, bool enable);
    int batch(int handle, nsecs_t sample_period, nsecs_t batch_period);
    int setDelay(int handle, nsecs_t sample_period);
    int flush(int handle);
    int poll(sensors_event_t * data, int count);

    // SensorEventCallback
    virtual int submitEvent(sp<BaseSensorObject>, const sensors_event_t &e) override;

    // get meta sensor struct
    const sensor_t& getDynamicMetaSensor() const;
protected:
    DynamicSensorManager(int handleBase, int handleMax, SensorEventCallback* callback);
private:
    // a helper class used for generate connection and disconnection report
    class ConnectionReport {
    public:
        ConnectionReport() {}
        ConnectionReport(int handle, sp<BaseSensorObject> sensor);
        ~ConnectionReport();
        const sensors_event_t& generateConnectionEvent(int metaHandle);
        static void fillDisconnectionEvent(sensors_event_t* event, int metaHandle, int handle);
    private:
        sensor_t mSensor;
        std::string mName;
        std::string mVendor;
        std::string mPermission;
        std::string mStringType;
        sensors_event_t mEvent;
        uint8_t mUuid[16];
        bool mGenerated;
        DISALLOW_EVIL_CONSTRUCTORS(ConnectionReport);
    };

    // returns next available handle to use upon a new sensor connection, or -1 if we run out.
    int getNextAvailableHandle();

    // TF:  int foo(sp<BaseSensorObject> obj);
    template <typename TF>
    int operateSensor(int handle, TF f) const {
        std::lock_guard<std::mutex> lk(mLock);
        const auto i = mMap.find(handle);
        if (i == mMap.end()) {
            return BAD_VALUE;
        }
        sp<BaseSensorObject> s = i->second.promote();
        if (s == nullptr) {
            // sensor object is already gone
            return BAD_VALUE;
        }
        return f(s);
    }

    // available sensor handle space
    const std::pair<int, int> mHandleRange;
    sensor_t mMetaSensor;

    // immutable pointer to event callback, used in extention mode.
    SensorEventCallback * const mCallback;

    // RingBuffer used in standalone mode
    static constexpr size_t kFifoSize = 4096; //4K events
    mutable std::mutex mFifoLock;
    RingBuffer mFifo;

    // mapping between handle and SensorObjects
    mutable std::mutex mLock;
    int mNextHandle;
    std::unordered_map<int, wp<BaseSensorObject>> mMap;
    std::unordered_map<void *, int> mReverseMap;
    mutable std::unordered_map<int, ConnectionReport> mPendingReport;

    // daemons
    std::vector<sp<BaseDynamicSensorDaemon>> mDaemonVector;
};

} // namespace SensorHalExt
} // namespace android

#endif // ANDROID_SENSORHAL_EXT_DYNAMIC_SENSOR_MANAGER_H