/*
 * 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.
 */
#define LOG_TAG "directchannel"
#include "directchannel.h"

#include <cutils/ashmem.h>
#include <hardware/sensors.h>
#include <utils/Log.h>

#include <sys/mman.h>

namespace android {

bool DirectChannelBase::isValid() {
    return mBuffer != nullptr;
}

int DirectChannelBase::getError() {
    return mError;
}

void DirectChannelBase::write(const sensors_event_t * ev) {
    if (isValid()) {
        mBuffer->write(ev, 1);
    }
}

AshmemDirectChannel::AshmemDirectChannel(const struct sensors_direct_mem_t *mem) : mAshmemFd(0) {
    mAshmemFd = mem->handle->data[0];

    if (!::ashmem_valid(mAshmemFd)) {
        mError = BAD_VALUE;
        return;
    }

    if ((size_t)::ashmem_get_size_region(mAshmemFd) != mem->size) {
        mError = BAD_VALUE;
        return;
    }

    mSize = mem->size;

    mBase = ::mmap(NULL, mem->size, PROT_WRITE, MAP_SHARED, mAshmemFd, 0);
    if (mBase == nullptr) {
        mError = NO_MEMORY;
        return;
    }

    mBuffer = std::unique_ptr<LockfreeBuffer>(new LockfreeBuffer(mBase, mSize));
    if (!mBuffer) {
        mError = NO_MEMORY;
    }
}

AshmemDirectChannel::~AshmemDirectChannel() {
    if (mBase) {
        mBuffer = nullptr;
        ::munmap(mBase, mSize);
        mBase = nullptr;
    }
    ::close(mAshmemFd);
}

ANDROID_SINGLETON_STATIC_INSTANCE(GrallocHalWrapper);

GrallocHalWrapper::GrallocHalWrapper()
        : mError(NO_INIT), mVersion(-1),
          mGrallocModule(nullptr), mAllocDevice(nullptr), mGralloc1Device(nullptr),
          mPfnRetain(nullptr), mPfnRelease(nullptr), mPfnLock(nullptr), mPfnUnlock(nullptr) {
    const hw_module_t *module;
    status_t err = ::hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    ALOGE_IF(err, "couldn't load %s module (%s)", GRALLOC_HARDWARE_MODULE_ID, strerror(-err));

    if (module == nullptr) {
        mError = (err < 0) ? err : NO_INIT;
    }

    switch ((module->module_api_version >> 8) & 0xFF) {
        case 0:
            err = ::gralloc_open(module, &mAllocDevice);
            if (err != NO_ERROR) {
                ALOGE("cannot open alloc device (%s)", strerror(-err));
                break;
            }

            if (mAllocDevice == nullptr) {
                ALOGE("gralloc_open returns no error, but result is nullptr");
                err = INVALID_OPERATION;
                break;
            }

            // successfully initialized gralloc
            mGrallocModule = (gralloc_module_t *)module;
            mVersion = 0;
            break;
        case 1:
            err = ::gralloc1_open(module, &mGralloc1Device);
            if (err != NO_ERROR) {
                ALOGE("cannot open gralloc1 device (%s)", strerror(-err));
                break;
            }

            if (mGralloc1Device == nullptr || mGralloc1Device->getFunction == nullptr) {
                ALOGE("gralloc1_open returns no error, but result is nullptr");
                err = INVALID_OPERATION;
                break;
            }

            mPfnRetain = (GRALLOC1_PFN_RETAIN)(mGralloc1Device->getFunction(mGralloc1Device,
                                                      GRALLOC1_FUNCTION_RETAIN));
            mPfnRelease = (GRALLOC1_PFN_RELEASE)(mGralloc1Device->getFunction(mGralloc1Device,
                                                       GRALLOC1_FUNCTION_RELEASE));
            mPfnLock = (GRALLOC1_PFN_LOCK)(mGralloc1Device->getFunction(mGralloc1Device,
                                                    GRALLOC1_FUNCTION_LOCK));
            mPfnUnlock = (GRALLOC1_PFN_UNLOCK)(mGralloc1Device->getFunction(mGralloc1Device,
                                                      GRALLOC1_FUNCTION_UNLOCK));
            if (mPfnRetain == nullptr || mPfnRelease == nullptr
                    || mPfnLock == nullptr || mPfnUnlock == nullptr) {
                ALOGE("Function pointer for retain, release, lock and unlock are %p, %p, %p, %p",
                      mPfnRetain, mPfnRelease, mPfnLock, mPfnUnlock);
                err = BAD_VALUE;
                break;
            }

            // successfully initialized gralloc1
            mGrallocModule = (gralloc_module_t *)module;
            mVersion = 1;
            break;
        default:
            ALOGE("Unknown version, not supported");
            break;
    }
    mError = err;
}

GrallocHalWrapper::~GrallocHalWrapper() {
    if (mAllocDevice != nullptr) {
        ::gralloc_close(mAllocDevice);
    }
}

int GrallocHalWrapper::registerBuffer(const native_handle_t *handle) {
    switch (mVersion) {
        case 0:
            return mGrallocModule->registerBuffer(mGrallocModule, handle);
        case 1:
            return mapGralloc1Error(mPfnRetain(mGralloc1Device, handle));
        default:
            return NO_INIT;
    }
}

int GrallocHalWrapper::unregisterBuffer(const native_handle_t *handle) {
    switch (mVersion) {
        case 0:
            return mGrallocModule->unregisterBuffer(mGrallocModule, handle);
        case 1:
            return mapGralloc1Error(mPfnRelease(mGralloc1Device, handle));
        default:
            return NO_INIT;
    }
}

int GrallocHalWrapper::lock(const native_handle_t *handle,
                           int usage, int l, int t, int w, int h, void **vaddr) {
    switch (mVersion) {
        case 0:
            return mGrallocModule->lock(mGrallocModule, handle, usage, l, t, w, h, vaddr);
        case 1: {
            const gralloc1_rect_t rect = {
                .left = l,
                .top = t,
                .width = w,
                .height = h
            };
            return mapGralloc1Error(mPfnLock(mGralloc1Device, handle,
                                             GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
                                             GRALLOC1_CONSUMER_USAGE_NONE,
                                             &rect, vaddr, -1));
        }
        default:
            return NO_INIT;
    }
}

int GrallocHalWrapper::unlock(const native_handle_t *handle) {
    switch (mVersion) {
        case 0:
            return mGrallocModule->unlock(mGrallocModule, handle);
        case 1: {
            int32_t dummy;
            return mapGralloc1Error(mPfnUnlock(mGralloc1Device, handle, &dummy));
        }
        default:
            return NO_INIT;
    }
}

int GrallocHalWrapper::mapGralloc1Error(int grallocError) {
    switch (grallocError) {
        case GRALLOC1_ERROR_NONE:
            return NO_ERROR;
        case GRALLOC1_ERROR_BAD_DESCRIPTOR:
        case GRALLOC1_ERROR_BAD_HANDLE:
        case GRALLOC1_ERROR_BAD_VALUE:
            return BAD_VALUE;
        case GRALLOC1_ERROR_NOT_SHARED:
        case GRALLOC1_ERROR_NO_RESOURCES:
            return NO_MEMORY;
        case GRALLOC1_ERROR_UNDEFINED:
        case GRALLOC1_ERROR_UNSUPPORTED:
            return INVALID_OPERATION;
        default:
            return UNKNOWN_ERROR;
    }
}

GrallocDirectChannel::GrallocDirectChannel(const struct sensors_direct_mem_t *mem)
        : mNativeHandle(nullptr) {
    if (mem->handle == nullptr) {
        ALOGE("mem->handle == nullptr");
        mError = BAD_VALUE;
        return;
    }

    mNativeHandle = ::native_handle_clone(mem->handle);
    if (mNativeHandle == nullptr) {
        ALOGE("clone mem->handle failed...");
        mError = NO_MEMORY;
        return;
    }

    mError = GrallocHalWrapper::getInstance().registerBuffer(mNativeHandle);
    if (mError != NO_ERROR) {
        ALOGE("registerBuffer failed");
        return;
    }

    mError = GrallocHalWrapper::getInstance().lock(mNativeHandle,
            GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0, mem->size, 1, &mBase);
    if (mError != NO_ERROR) {
        ALOGE("lock buffer failed");
        return;
    }

    if (mBase == nullptr) {
        ALOGE("lock buffer => nullptr");
        mError = NO_MEMORY;
        return;
    }

    mSize = mem->size;
    mBuffer = std::make_unique<LockfreeBuffer>(mBase, mSize);
    if (!mBuffer) {
        mError = NO_MEMORY;
        return;
    }

    mError = NO_ERROR;
}

GrallocDirectChannel::~GrallocDirectChannel() {
    if (mNativeHandle != nullptr) {
        if (mBase) {
            mBuffer = nullptr;
            GrallocHalWrapper::getInstance().unlock(mNativeHandle);
            mBase = nullptr;
        }
        GrallocHalWrapper::getInstance().unregisterBuffer(mNativeHandle);
        ::native_handle_close(mNativeHandle);
        ::native_handle_delete(mNativeHandle);
        mNativeHandle = nullptr;
    }
}

} // namespace android