/*
 * 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.
 */
#define LOG_TAG "HidlSupport"

#include <hidl/HidlSupport.h>

#include <unordered_map>

#include <android-base/logging.h>
#include <android-base/parseint.h>

namespace android {
namespace hardware {

namespace details {
bool debuggable() {
#ifdef LIBHIDL_TARGET_DEBUGGABLE
    return true;
#else
    return false;
#endif
}
}  // namespace details

hidl_handle::hidl_handle() {
    mHandle = nullptr;
    mOwnsHandle = false;
}

hidl_handle::~hidl_handle() {
    freeHandle();
}

hidl_handle::hidl_handle(const native_handle_t *handle) {
    mHandle = handle;
    mOwnsHandle = false;
}

// copy constructor.
hidl_handle::hidl_handle(const hidl_handle &other) {
    mOwnsHandle = false;
    *this = other;
}

// move constructor.
hidl_handle::hidl_handle(hidl_handle &&other) {
    mOwnsHandle = false;
    *this = std::move(other);
}

// assignment operators
hidl_handle &hidl_handle::operator=(const hidl_handle &other) {
    if (this == &other) {
        return *this;
    }
    freeHandle();
    if (other.mHandle != nullptr) {
        mHandle = native_handle_clone(other.mHandle);
        if (mHandle == nullptr) {
            PLOG(FATAL) << "Failed to clone native_handle in hidl_handle";
        }
        mOwnsHandle = true;
    } else {
        mHandle = nullptr;
        mOwnsHandle = false;
    }
    return *this;
}

hidl_handle &hidl_handle::operator=(const native_handle_t *native_handle) {
    freeHandle();
    mHandle = native_handle;
    mOwnsHandle = false;
    return *this;
}

hidl_handle &hidl_handle::operator=(hidl_handle &&other) {
    if (this != &other) {
        freeHandle();
        mHandle = other.mHandle;
        mOwnsHandle = other.mOwnsHandle;
        other.mHandle = nullptr;
        other.mOwnsHandle = false;
    }
    return *this;
}

void hidl_handle::setTo(native_handle_t* handle, bool shouldOwn) {
    freeHandle();
    mHandle = handle;
    mOwnsHandle = shouldOwn;
}

const native_handle_t* hidl_handle::operator->() const {
    return mHandle;
}

// implicit conversion to const native_handle_t*
hidl_handle::operator const native_handle_t *() const {
    return mHandle;
}

// explicit conversion
const native_handle_t *hidl_handle::getNativeHandle() const {
    return mHandle;
}

void hidl_handle::freeHandle() {
    if (mOwnsHandle && mHandle != nullptr) {
        // This can only be true if:
        // 1. Somebody called setTo() with shouldOwn=true, so we know the handle
        //    wasn't const to begin with.
        // 2. Copy/assignment from another hidl_handle, in which case we have
        //    cloned the handle.
        // 3. Move constructor from another hidl_handle, in which case the original
        //    hidl_handle must have been non-const as well.
        native_handle_t *handle = const_cast<native_handle_t*>(
                static_cast<const native_handle_t*>(mHandle));
        native_handle_close(handle);
        native_handle_delete(handle);
        mHandle = nullptr;
    }
}

static const char *const kEmptyString = "";

hidl_string::hidl_string()
    : mBuffer(kEmptyString),
      mSize(0),
      mOwnsBuffer(false) {
}

hidl_string::~hidl_string() {
    clear();
}

hidl_string::hidl_string(const char *s) : hidl_string() {
    if (s == nullptr) {
        return;
    }

    copyFrom(s, strlen(s));
}

hidl_string::hidl_string(const char *s, size_t length) : hidl_string() {
    copyFrom(s, length);
}

hidl_string::hidl_string(const hidl_string &other): hidl_string() {
    copyFrom(other.c_str(), other.size());
}

hidl_string::hidl_string(const std::string &s) : hidl_string() {
    copyFrom(s.c_str(), s.size());
}

hidl_string::hidl_string(hidl_string &&other): hidl_string() {
    moveFrom(std::forward<hidl_string>(other));
}

hidl_string &hidl_string::operator=(hidl_string &&other) {
    if (this != &other) {
        clear();
        moveFrom(std::forward<hidl_string>(other));
    }
    return *this;
}

hidl_string &hidl_string::operator=(const hidl_string &other) {
    if (this != &other) {
        clear();
        copyFrom(other.c_str(), other.size());
    }

    return *this;
}

hidl_string &hidl_string::operator=(const char *s) {
    clear();

    if (s == nullptr) {
        return *this;
    }

    copyFrom(s, strlen(s));
    return *this;
}

hidl_string &hidl_string::operator=(const std::string &s) {
    clear();
    copyFrom(s.c_str(), s.size());
    return *this;
}

hidl_string::operator std::string() const {
    return std::string(mBuffer, mSize);
}

std::ostream& operator<<(std::ostream& os, const hidl_string& str) {
    os << str.c_str();
    return os;
}

void hidl_string::copyFrom(const char *data, size_t size) {
    // assume my resources are freed.

    if (size >= UINT32_MAX) {
        LOG(FATAL) << "string size can't exceed 2^32 bytes: " << size;
    }
    char *buf = (char *)malloc(size + 1);
    memcpy(buf, data, size);
    buf[size] = '\0';
    mBuffer = buf;

    mSize = static_cast<uint32_t>(size);
    mOwnsBuffer = true;
}

void hidl_string::moveFrom(hidl_string &&other) {
    // assume my resources are freed.

    mBuffer = std::move(other.mBuffer);
    mSize = other.mSize;
    mOwnsBuffer = other.mOwnsBuffer;

    other.mOwnsBuffer = false;
    other.clear();
}

void hidl_string::clear() {
    if (mOwnsBuffer && (mBuffer != kEmptyString)) {
        free(const_cast<char *>(static_cast<const char *>(mBuffer)));
    }

    mBuffer = kEmptyString;
    mSize = 0;
    mOwnsBuffer = false;
}

void hidl_string::setToExternal(const char *data, size_t size) {
    if (size > UINT32_MAX) {
        LOG(FATAL) << "string size can't exceed 2^32 bytes: " << size;
    }
    clear();

    mBuffer = data;
    mSize = static_cast<uint32_t>(size);
    mOwnsBuffer = false;
}

const char *hidl_string::c_str() const {
    return mBuffer;
}

size_t hidl_string::size() const {
    return mSize;
}

bool hidl_string::empty() const {
    return mSize == 0;
}

sp<HidlMemory> HidlMemory::getInstance(const hidl_memory& mem) {
    sp<HidlMemory> instance = new HidlMemory();
    instance->hidl_memory::operator=(mem);
    return instance;
}

sp<HidlMemory> HidlMemory::getInstance(hidl_memory&& mem) {
    sp<HidlMemory> instance = new HidlMemory();
    instance->hidl_memory::operator=(std::move(mem));
    return instance;
}

sp<HidlMemory> HidlMemory::getInstance(const hidl_string& name, int fd, uint64_t size) {
    native_handle_t* handle = native_handle_create(1, 0);
    if (!handle) {
        close(fd);
        LOG(ERROR) << "native_handle_create fails";
        return new HidlMemory();
    }
    handle->data[0] = fd;

    hidl_handle hidlHandle;
    hidlHandle.setTo(handle, true /* shouldOwn */);

    sp<HidlMemory> instance = new HidlMemory(name, std::move(hidlHandle), size);
    return instance;
}

HidlMemory::HidlMemory() : hidl_memory() {}

HidlMemory::HidlMemory(const hidl_string& name, hidl_handle&& handle, size_t size)
        : hidl_memory(name, std::move(handle), size) {}

// it's required to have at least one out-of-line method to avoid weak vtable
HidlMemory::~HidlMemory() {
    hidl_memory::~hidl_memory();
}

}  // namespace hardware
}  // namespace android