/*
 * Copyright (C) 2019-2021 Intel 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 ParserBase

#include "ParserBase.h"

#include <expat.h>
#include <string.h>

#include <memory>
#include <string>
#include <vector>

#include "iutils/CameraLog.h"
#include "iutils/Errors.h"
#include "iutils/Utils.h"

namespace icamera {

const char* ParserBase::skipWhiteSpace(const char* src) {
    while (*src == '\n' || *src == '\t' || *src == ' ' || *src == '\v' || *src == '\r' ||
           *src == '\f') {
        src++;
    }
    return src;
}

int ParserBase::parseXmlParameterToChar(const char* str, unsigned char* table) {
    CheckAndLogError(str == nullptr, -1, "@%s, str is nullptr", __func__);

    int index = 0;
    char *savePtr, *tablePtr;
    int sz = strlen(str);
    char src[sz + 1];
    MEMCPY_S(src, sz, str, sz);
    src[sz] = '\0';

    tablePtr = strtok_r(src, ",", &savePtr);
    while (tablePtr) {
        table[index] = atoi(tablePtr);
        if (savePtr != nullptr) savePtr = const_cast<char*>(skipWhiteSpace(savePtr));
        index++;
        tablePtr = strtok_r(nullptr, ",", &savePtr);
    }
    return 0;
}

/* template function need the function ptr */
std::string ParserBase::convertCharToString(const char* str) {
    return str;
}

void ParserBase::startElement(void* userData, const char* name, const char** atts) {
    ParserBaseCallBack* profiles = reinterpret_cast<ParserBaseCallBack*>(userData);
    profiles->startParseElement(userData, name, atts);
}

void ParserBase::endElement(void* userData, const char* name) {
    ParserBaseCallBack* profiles = reinterpret_cast<ParserBaseCallBack*>(userData);
    profiles->endParseElement(userData, name);
}

int ParserBase::parseXmlFile(const std::string& xmlFile) {
    int ret = UNKNOWN_ERROR;
    int done;
    FILE* fp = nullptr;
    int bufSize = 4 * 1024;  // parse 4k data every time

    CheckAndLogError(xmlFile.empty(), UNKNOWN_ERROR, "xmlFile is empty");

    LOG2("@%s, parsing profile: %s", __func__, xmlFile.c_str());

    fp = ::fopen(xmlFile.c_str(), "r");
    CheckAndLogError(nullptr == fp, UNKNOWN_ERROR,
                     "@%s, line:%d, Can not open profile file %s in read mode, fp is nullptr",
                     __func__, __LINE__, xmlFile.c_str());

    std::unique_ptr<char[]> pBuf(new char[bufSize]);
    XML_Parser parser = ::XML_ParserCreate(nullptr);
    if (nullptr == parser) {
        LOGE("@%s, line:%d, parser is nullptr", __func__, __LINE__);
        goto exit;
    }

    ::XML_SetUserData(parser, this);
    ::XML_SetElementHandler(parser, startElement, endElement);

    do {
        int len = static_cast<int>(::fread(pBuf.get(), 1, bufSize, fp));
        if (!len) {
            if (ferror(fp)) {
                clearerr(fp);
                goto exit;
            }
        }
        done = len < bufSize;
        if (XML_Parse(parser, (const char*)pBuf.get(), len, done) == XML_STATUS_ERROR) {
            LOGE("@%s, line:%d, XML_Parse error", __func__, __LINE__);
            goto exit;
        }
    } while (!done);
    ret = OK;

exit:
    if (parser) ::XML_ParserFree(parser);
    if (fp) ::fclose(fp);

    return ret;
}

void ParserBase::getAvaliableXmlFile(const std::vector<const char*>& avaliableXmlFiles,
                                     std::string& xmlFile) {
    struct stat st;
    for (auto xml : avaliableXmlFiles) {
        int ret = stat(xml, &st);
        if (ret == 0) {
            xmlFile = xml;
            return;
        }
    }
}

int ParserBase::getDataFromXmlFile(std::string fileName) {
    LOG2("@%s", __func__);
    CheckAndLogError(fileName.size() == 0, UNKNOWN_ERROR, "file name is null");

    std::string curFolderFileName = std::string("./") + fileName;
    std::string sysFolderFileName = PlatformData::getCameraCfgPath() + fileName;
    const std::vector<const char*> profiles = {curFolderFileName.c_str(),
                                               sysFolderFileName.c_str()};

    std::string chosenXmlFile;
    getAvaliableXmlFile(profiles, chosenXmlFile);
    if (chosenXmlFile.empty()) {
        LOGI("%s is not found", fileName.c_str());
        return UNKNOWN_ERROR;
    }

    return parseXmlFile(chosenXmlFile);
}

}  // namespace icamera
