/*
 * Copyright (C) 2015-2021 Corporation.
 *
 * 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.
 */

#define LOG_TAG V4l2DeviceFactory

#include "V4l2DeviceFactory.h"

#include <fcntl.h>

#include "iutils/CameraLog.h"

namespace icamera {

std::map<int, V4l2DeviceFactory*> V4l2DeviceFactory::sInstances;
Mutex V4l2DeviceFactory::sLock;

V4l2DeviceFactory::V4l2DeviceFactory(int cameraId) : mCameraId(cameraId) {
    LOG1("<id%d> @%s", mCameraId, __func__);
}

V4l2DeviceFactory::~V4l2DeviceFactory() {
    LOG1("<id%d> @%s", mCameraId, __func__);
}

/**
 * Create a static instance of V4l2DeviceFactory for cameraId.
 * It should be called before any device is used.
 */
void V4l2DeviceFactory::createDeviceFactory(int cameraId) {
    AutoMutex lock(sLock);
    getInstance(cameraId);
}

/**
 * Release the static instance of V4l2DeviceFactory for cameraId.
 * All device related to the instance of of V4l2DeviceFactory will be release here as well
 * After calling this function, all device could not be used anymore.
 */
void V4l2DeviceFactory::releaseDeviceFactory(int cameraId) {
    AutoMutex lock(sLock);
    V4l2DeviceFactory* factory = getInstance(cameraId);
    sInstances.erase(cameraId);
    factory->releaseSubDevices(cameraId);
    delete factory;
}

/**
 * Get an opened sub device
 *
 * The caller is supposed to get an opened sub device.
 * If openSubDev failed, it just return non-opened instance,
 * and using this instance to call its funtion will cause 'device not open' error,
 *
 * Return a not nullptr sub device pointer
 */
V4L2Subdevice* V4l2DeviceFactory::getSubDev(int cameraId, const std::string& devName) {
    AutoMutex lock(sLock);
    V4l2DeviceFactory* factory = getInstance(cameraId);
    // If an existing sub device found, then just return it.

    if (factory->mDevices.find(devName) != factory->mDevices.end()) {
        return factory->mDevices[devName];
    }
    // Create a new sub device for devName, since it's not created before.
    V4L2Subdevice* subdev = new V4L2Subdevice(devName);

    // Make sure the caller always got an opened device.
    subdev->Open(O_RDWR);
    // Add the new allocated sub device into device map.
    factory->mDevices[devName] = subdev;
    return subdev;
}

/**
 * Release all sub devices in device map
 *
 * It's a private function with no lock in it, must be called with lock protection.
 *
 * It MUST be called after all sub devices are not used anymore
 */
void V4l2DeviceFactory::releaseSubDevices(int /*cameraId*/) {
    for (auto it = mDevices.begin(); it != mDevices.end(); it++) {
        V4L2Subdevice* subdev = it->second;
        if (subdev) {
            subdev->Close();
            delete subdev;
        }
    }
    mDevices.clear();
}

/**
 * Private function with no lock in it, must be called with lock protection
 */
V4l2DeviceFactory* V4l2DeviceFactory::getInstance(int cameraId) {
    if (sInstances.find(cameraId) != sInstances.end()) {
        return sInstances[cameraId];
    }

    sInstances[cameraId] = new V4l2DeviceFactory(cameraId);
    return sInstances[cameraId];
}

}  // namespace icamera
