/*
 * Copyright (C) 2014-2017 Intel Corporation.
 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
 *
 * 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 "FlashLight"

#include <sys/stat.h>
#include "LogHelper.h"
#include <FlashLight.h>
#include "PlatformData.h"

namespace android {
namespace camera2 {

FlashLight& FlashLight::getInstance()
{
    static FlashLight flashInstance;
    return flashInstance;
}

FlashLight::FlashLight() : mCallbacks(NULL)
{
    for (int camid = 0; camid< MAX_NUM_CAMERA; camid++) {
        for (int flindex = 0; flindex < MAX_NUM_FLASH_OF_ONE_MODULE; flindex++) {
            mFlashFds[camid][flindex] = -1;
            mCameraOpen[camid] = false;
            mFlashOn[camid][flindex] = false;
        }
    }
}

FlashLight::~FlashLight()
{
    for (int camid = 0; camid< MAX_NUM_CAMERA; camid++) {
        for (int flindex = 0; flindex < MAX_NUM_FLASH_OF_ONE_MODULE; flindex++) {
            if (mFlashFds[camid][flindex] >= 0)
                {
                    setFlashMode(camid, flindex, false);
                    close(mFlashFds[camid][flindex]);
                    mFlashFds[camid][flindex] = -1;
                }
        }
    }
}

int32_t FlashLight::getFlashLightInfo(
        const int cameraId, bool &hasFlash,
        const char* (&flashNode)[MAX_NUM_FLASH_OF_ONE_MODULE]) {

    int32_t retVal = 0;
    hasFlash = false;

    const CameraHWInfo* camHwInfo = PlatformData::getCameraHWInfo();
    CheckError(!camHwInfo, -EINVAL, "@%s,  camera hw info was not uninitialized",
                   __FUNCTION__);

    const struct SensorDriverDescriptor* sensorInfo = camHwInfo->getSensorDrvDes(cameraId);
    CheckError(!sensorInfo, -EINVAL, "@%s,  camera sensor info was not uninitialized",
                   __FUNCTION__);

    int flash_num = sensorInfo->mFlashNum;

    for (int i = 0; i < flash_num; i++) {

        const std::string& node = sensorInfo->mModuleFlashDevName[i];
        flashNode[i] = node.c_str();
    }

    hasFlash = flash_num > 0 ? true : false;
    LOGD("@%s : hasFlash %d, flashNode[0]: %s, flashNode[1]: %s",
         __FUNCTION__, hasFlash, flashNode[0], flashNode[1]);

    return retVal;
}

int32_t FlashLight::setCallbacks(
        const camera_module_callbacks_t* callbacks)
{
    int32_t retVal = 0;
    mCallbacks = callbacks;
    return retVal;
}

int32_t FlashLight::init(const int cameraId)
{
    int32_t retVal = 0;
    bool hasFlash = false;
    const char* flashPath[MAX_NUM_FLASH_OF_ONE_MODULE]
        = {NULL, NULL};

    if (cameraId < 0 || cameraId >= MAX_NUM_CAMERA) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        return -EINVAL;
    }

    getFlashLightInfo(cameraId, hasFlash, flashPath);

    if (!hasFlash) {
        LOGE("%s: No flash available for camera id: %d", __FUNCTION__, cameraId);
        retVal = -ENOSYS;
    } else if (mCameraOpen[cameraId]) {
        LOGE("%s: Camera in use for camera id: %d", __FUNCTION__, cameraId);
        retVal = -EBUSY;
    } else if (mFlashFds[cameraId][0] >= 0) {
        LOGD("%s: Flash is already inited for camera id: %d", __FUNCTION__, cameraId);
    } else {
        for (int i = 0; i < MAX_NUM_FLASH_OF_ONE_MODULE; i++) {
            if (flashPath[i]) {
                if (mFlashFds[cameraId][i] == -1)
                    mFlashFds[cameraId][i] = open(flashPath[i], O_RDWR | O_NONBLOCK);
                if (mFlashFds[cameraId][i] < 0) {
                    LOGE("%s: Unable to open node '%s'", __FUNCTION__, flashPath[i]);
                    retVal = -EBUSY;
                    break;
                }
            }
        }
    }

    LOGD("@%s : retval = %d", __FUNCTION__, retVal);
    return retVal;
}

int32_t FlashLight::deinit(const int cameraId)
{
    int32_t retVal = 0;

    if (cameraId < 0 || cameraId >= MAX_NUM_CAMERA) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        retVal = -EINVAL;
    } else {
        for (int i = 0; i < MAX_NUM_FLASH_OF_ONE_MODULE; i++) {
            if (mFlashFds[cameraId][i] >= 0) {
                setFlashMode(cameraId, i, false);
                close(mFlashFds[cameraId][i]);
                mFlashFds[cameraId][i] = -1;
            }
        }
    }

    return retVal;
}

int32_t FlashLight::setFlashMode(const int cameraId, const bool mode)
{
    int32_t retVal = 0;
    LOGD("@%s : cameraId %d, mode %d", __FUNCTION__, cameraId, mode);

    if (cameraId < 0 || cameraId >= MAX_NUM_CAMERA) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        retVal = -EINVAL;
    } else if (mode == mFlashOn[cameraId][0]) {
        LOGD("%s: flash %d is already in requested state: %d", __FUNCTION__, cameraId, mode);
        retVal = -EALREADY;
    } else if (mFlashFds[cameraId][0] < 0) {
        LOGE("%s: called for uninited flash: %d", __FUNCTION__, cameraId);
        retVal = -EINVAL;
    }  else {
        for (int i = 0; i < MAX_NUM_FLASH_OF_ONE_MODULE; i++) {
            if (mFlashFds[cameraId][i] >= 0) {
                retVal |= setFlashMode(cameraId, i, mode);
            }
        }
    }

    return retVal;
}

int32_t FlashLight::setFlashMode(const int cameraId, int flashIdx, const bool mode)
{
    int32_t retVal = 0;
    LOGD("@%s : cameraId %d, mode %d", __FUNCTION__, cameraId, mode);

    if (cameraId < 0 || cameraId >= MAX_NUM_CAMERA) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        retVal = -ENOSYS;
    } else if (mode == mFlashOn[cameraId][flashIdx]) {
        LOGD("%s: flash %d is already in requested state: %d", __FUNCTION__, cameraId, mode);
        retVal = -EALREADY;
    } else if (mFlashFds[cameraId][flashIdx] < 0) {
        LOGE("%s: called for uninited flash: %d", __FUNCTION__, cameraId);
        retVal = -ENOSYS;
    }  else {
        struct v4l2_control control;
        struct v4l2_queryctrl qctrl;

        memset(&control, 0, sizeof(control));
        memset(&qctrl, 0, sizeof(qctrl));

        qctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY;
        if (ioctl(mFlashFds[cameraId][flashIdx], VIDIOC_QUERYCTRL, &qctrl) < 0) {
            LOGE("@%s : query falsh torch power failed", __FUNCTION__);
            return -ENOSYS;
        }
        LOGD(" qctrl.flags(0x%08x)", qctrl.flags);
        if (qctrl.flags != V4L2_CTRL_FLAG_READ_ONLY) {
            control.id = V4L2_CID_FLASH_TORCH_INTENSITY;
            control.value = qctrl.default_value;
            if (ioctl(mFlashFds[cameraId][flashIdx], VIDIOC_S_CTRL, &control, 0) < 0) {
                LOGE("@%s : set flash intensity failed, may be not support set torch intensity.", __FUNCTION__);
                return -ENOSYS;
            }
        }

        memset(&control, 0, sizeof(control));
        control.id = V4L2_CID_FLASH_LED_MODE;
        control.value = mode ? V4L2_FLASH_LED_MODE_TORCH : V4L2_FLASH_LED_MODE_NONE;
        if (ioctl(mFlashFds[cameraId][flashIdx], VIDIOC_S_CTRL, &control, 0) < 0) {
            LOGE("@%s : set flash mode %d failed", __FUNCTION__, mode);
            return -ENOSYS;
        }
        LOGI("@%s : set flash mode %d sucess", __FUNCTION__, mode);
        mFlashOn[cameraId][flashIdx] = mode;
    }
    return retVal;
}

int32_t FlashLight::reserveFlashForCamera(const int cameraId)
{
    int32_t retVal = 0;

    if (cameraId < 0 || cameraId >= MAX_NUM_CAMERA) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        retVal = -EINVAL;
    } else if (mCameraOpen[cameraId]) {
        LOGD("%s: Flash already reserved for camera id: %d", __FUNCTION__,
                cameraId);
    } else {
        deinit(cameraId);
        mCameraOpen[cameraId] = true;

        bool hasFlash = false;
        const char* flashPath[MAX_NUM_FLASH_OF_ONE_MODULE]
            = {NULL, NULL};

        getFlashLightInfo(cameraId, hasFlash, flashPath);

        if (mCallbacks == NULL ||
                mCallbacks->torch_mode_status_change == NULL) {
            LOGE("%s: Callback is not defined!", __FUNCTION__);
            retVal = -ENOSYS;
        } else if (!hasFlash) {
            LOGD("%s: no flash exists for camera id: %d", __FUNCTION__, cameraId);
        } else {
            char cameraIdStr[1];
            snprintf(cameraIdStr, 1, "%d", cameraId);
            mCallbacks->torch_mode_status_change(mCallbacks,
                    cameraIdStr,
                    TORCH_MODE_STATUS_NOT_AVAILABLE);
        }
    }

    return retVal;
}

int32_t FlashLight::releaseFlashFromCamera(const int cameraId)
{
    int32_t retVal = 0;

    if (cameraId < 0 || cameraId >= MAX_NUM_FLASH_OF_ONE_MODULE) {
        LOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
        retVal = -EINVAL;
    } else if (!mCameraOpen[cameraId]) {
        LOGD("%s: Flash not reserved for camera id: %d", __FUNCTION__, cameraId);
    } else {
        mCameraOpen[cameraId] = false;

        bool hasFlash = false;
        const char* flashPath[MAX_NUM_FLASH_OF_ONE_MODULE]
            = {NULL, NULL};

        getFlashLightInfo(cameraId, hasFlash, flashPath);

        if (mCallbacks == NULL ||
                mCallbacks->torch_mode_status_change == NULL) {
            LOGE("%s: Callback is not defined!", __FUNCTION__);
            retVal = -ENOSYS;
        } else if (!hasFlash) {
            LOGD("%s: no flash exists for camera id: %d", __FUNCTION__, cameraId);
        } else {
            char cameraIdStr[1];
            snprintf(cameraIdStr, 1, "%d", cameraId);
            mCallbacks->torch_mode_status_change(mCallbacks,
                    cameraIdStr,
                    TORCH_MODE_STATUS_AVAILABLE_OFF);
        }
    }

    return retVal;
}

} /* namespace camera2 */
} /* namespace android */