/*
 * Copyright (c) 2020-2021 Huawei Device 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.
 */

#include "imgdecode/cache_manager.h"
#include "gfx_utils/graphic_log.h"
#include "hal_tick.h"
#include "securec.h"

namespace OHOS {
const uint8_t* CacheEntry::GetImgData() const
{
    return dsc_.imgInfo.data;
}

RetCode CacheEntry::ReadLine(const Point& start, int16_t len, uint8_t* buf)
{
    RetCode ret;
    if (dsc_.decoder != nullptr) {
        ret = dsc_.decoder->ReadLine(dsc_, start, len, buf);
    } else {
        ret = RetCode::FAIL;
    }

    return ret;
}

void CacheEntry::Clear()
{
    if (dsc_.decoder != nullptr) {
        dsc_.decoder->Close(dsc_);
    }

    dsc_.decoder = nullptr;
    ClearSrc();
    dsc_.imgInfo.data = nullptr;
    dsc_.fd = -1;
    dsc_.srcType = IMG_SRC_UNKNOWN;
    life_ = 0;
}

void CacheEntry::ClearSrc()
{
    if (dsc_.srcType == IMG_SRC_FILE) {
        UIFree(const_cast<char*>(dsc_.path));
    }
    dsc_.path = nullptr;
}

RetCode CacheEntry::SetSrc(const char* path)
{
    ClearSrc();
    if (dsc_.srcType == IMG_SRC_FILE) {
        size_t strLen = strlen(path);
        if (strLen > MAX_SRC_LENGTH) {
            return RetCode::FAIL;
        }
        char* newStr = static_cast<char*>(UIMalloc(static_cast<uint32_t>(strLen) + 1));
        if (newStr == nullptr) {
            return RetCode::FAIL;
        }
        if (memcpy_s(newStr, strLen + 1, path, strLen) != EOK) {
            UIFree(reinterpret_cast<void*>(newStr));
            newStr = nullptr;
            return RetCode::FAIL;
        }
        newStr[strLen] = '\0';
        dsc_.path = newStr;
    } else {
        dsc_.path = path;
    }
    return RetCode::OK;
}

CacheManager& CacheManager::GetInstance()
{
    static CacheManager instance;
    return instance;
}

RetCode CacheManager::Init(uint16_t size)
{
    if ((size == 0) || (size > DEFAULT_MAX_CACHE_ENTRY_NUM)) {
        return RetCode::FAIL;
    }
    Reset();
    if (entryArr_ != nullptr) {
        UIFree(reinterpret_cast<void*>(entryArr_));
    }

    uint32_t tmpCacheSize = size * sizeof(CacheEntry);
    entryArr_ = static_cast<CacheEntry*>(UIMalloc(tmpCacheSize));
    if (entryArr_ == nullptr) {
        size_ = 0;
        return RetCode::FAIL;
    }

    if (memset_s(entryArr_, tmpCacheSize, 0, tmpCacheSize) != EOK) {
        UIFree(reinterpret_cast<void*>(entryArr_));
        entryArr_ = nullptr;
        return RetCode::FAIL;
    }

    size_ = size;
    return RetCode::OK;
}

RetCode CacheManager::Open(const char* path, const Style& style, CacheEntry& entry)
{
    if ((path == nullptr) || (GetSize() <= 0)) {
        return RetCode::FAIL;
    }

    AgingAll();
    uint16_t indexHitted = 0;
    RetCode ret = GetIndex(path, indexHitted);
    if (ret == RetCode::OK) {
        ReadToCache(entryArr_[indexHitted]);
        entry = entryArr_[indexHitted];
        return RetCode::OK;
    }

    SelectEntryToReplace(indexHitted);
    if ((entryArr_[indexHitted].dsc_.path != nullptr) && (entryArr_[indexHitted].dsc_.decoder != nullptr)) {
        entryArr_[indexHitted].dsc_.decoder->Close(entryArr_[indexHitted].dsc_);
    }

    uint32_t startTime = HALTick::GetInstance().GetTime();
    entryArr_[indexHitted].life_ = 0;

    ret = TryDecode(path, style, entryArr_[indexHitted]);
    if (ret != RetCode::OK) {
        return ret;
    }
    ReadToCache(entryArr_[indexHitted]);
    entryArr_[indexHitted].life_ = HALTick::GetInstance().GetElapseTime(startTime);
    entry = entryArr_[indexHitted];
    return RetCode::OK;
}

RetCode CacheManager::Close(const char* path)
{
    if (path == nullptr) {
        return RetCode::FAIL;
    }

    for (uint16_t index = 0; index < GetSize(); index++) {
        if (entryArr_[index].dsc_.srcType == IMG_SRC_FILE) {
            if (entryArr_[index].dsc_.path == nullptr) {
                continue;
            }
            if (strcmp(entryArr_[index].dsc_.path, path) == 0) {
                Clear(entryArr_[index]);
                break;
            }
        } else {
            if (entryArr_[index].dsc_.path == path) {
                Clear(entryArr_[index]);
                break;
            }
        }
    }

    return RetCode::OK;
}

bool CacheManager::GetImageHeader(const char* path, ImageHeader& header)
{
    CacheEntry entry;
    Style useless;
    RetCode ret = Open(path, useless, entry);
    if (ret != RetCode::OK) {
        GRAPHIC_LOGW("CacheManager::GetImageHeader Image get info found unknown src type\n");
        return false;
    }

    header = entry.GetImgHeader();
    return true;
}

RetCode CacheManager::Reset()
{
    if (entryArr_ == nullptr) {
        return RetCode::OK;
    }

    for (uint16_t index = 0; index < GetSize(); index++) {
        if (entryArr_[index].dsc_.path != nullptr) {
            Clear(entryArr_[index]);
        }
    }

    return RetCode::OK;
}

RetCode CacheManager::ReadToCache(CacheEntry& entry)
{
    if (entry.dsc_.decoder == nullptr) {
        return RetCode::FAIL;
    }
    return entry.dsc_.decoder->ReadToCache(entry.dsc_);
}

void CacheManager::Clear(CacheEntry& entry)
{
    entry.Clear();
}

void CacheManager::AgingAll(int32_t time)
{
    for (uint16_t index = 0; index < GetSize(); index++) {
        if (entryArr_[index].life_ > INT32_MIN + AGING_INTERVAL) {
            entryArr_[index].life_ -= time;
        }
    }
}

RetCode CacheManager::GetIndex(const char* path, uint16_t& hittedIndex)
{
    for (uint16_t index = 0; index < GetSize(); index++) {
        if (entryArr_[index].dsc_.srcType == IMG_SRC_FILE) {
            if ((entryArr_[index].dsc_.path != nullptr) && !strcmp(path, entryArr_[index].dsc_.path)) {
                entryArr_[index].life_ += entryArr_[index].dsc_.timeToOpen * LIFE_GAIN_INTERVAL;
                if (entryArr_[index].life_ > LIFE_LIMIT) {
                    entryArr_[index].life_ = LIFE_LIMIT;
                }
                hittedIndex = index;
                return RetCode::OK;
            }
        } else {
            ImageInfo* imgDsc = reinterpret_cast<ImageInfo*>(const_cast<char*>(path));
            if ((entryArr_[index].dsc_.path == path) && (entryArr_[index].dsc_.imgInfo.data == imgDsc->data)) {
                entryArr_[index].life_ += entryArr_[index].dsc_.timeToOpen * LIFE_GAIN_INTERVAL;
                if (entryArr_[index].life_ > LIFE_LIMIT) {
                    entryArr_[index].life_ = LIFE_LIMIT;
                }
                hittedIndex = index;
                return RetCode::OK;
            }
        }
    }

    return RetCode::FAIL;
}

RetCode CacheManager::SelectEntryToReplace(uint16_t& selectedIndex)
{
    selectedIndex = 0;
    for (uint16_t index = 0; index < GetSize(); index++) {
        if (entryArr_[index].life_ < entryArr_[selectedIndex].life_) {
            selectedIndex = index;
        }
    }

    return RetCode::OK;
}

RetCode CacheManager::TryDecode(const char* path, const Style& style, CacheEntry& entry)
{
    FileImgDecoder* decoder = &(FileImgDecoder::GetInstance());
    if (decoder == nullptr) {
        Clear(entry);
        return RetCode::FAIL;
    }

    entry.dsc_.srcType = IMG_SRC_FILE;
    RetCode ret = entry.SetSrc(path);
    if (ret != RetCode::OK) {
        Clear(entry);
        return ret;
    }
    entry.dsc_.decoder = decoder;

    ret = entry.dsc_.decoder->GetHeader(entry.dsc_);
    if (ret != RetCode::OK) {
        Clear(entry);
        return ret;
    }

    ret = entry.dsc_.decoder->Open(entry.dsc_);
    if (ret != RetCode::OK) {
        Clear(entry);
        return ret;
    }

    return ret;
}
} // namespace OHOS