/*
 * 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.
 */

#include "calibrationfile.h"

#include "file.h"
#include "log.h"

namespace android {

constexpr char kCalibrationFile[] = "/persist/sensorcal.json";

std::shared_ptr<CalibrationFile> CalibrationFile::instance_;

std::shared_ptr<CalibrationFile> CalibrationFile::Instance() {
    if (!CalibrationFile::instance_) {
        auto inst = std::shared_ptr<CalibrationFile>(new CalibrationFile());
        if (inst->Initialize()) {
            CalibrationFile::instance_ = inst;
        }
    }

    return CalibrationFile::instance_;
}

bool CalibrationFile::Initialize() {
    file_ = std::unique_ptr<File>(new File(kCalibrationFile, "rw"));

    status_t err = file_->initCheck();
    if (err != OK) {
        LOGE("Couldn't open calibration file: %d (%s)", err, strerror(-err));
        return false;
    }

    off64_t file_size = file_->seekTo(0, SEEK_END);
    if (file_size > 0) {
        auto file_data = std::vector<char>(file_size);
        file_->seekTo(0, SEEK_SET);
        ssize_t bytes_read = file_->read(file_data.data(), file_size);
        if (bytes_read != file_size) {
            LOGE("Read of configuration file returned %zd, expected %" PRIu64,
                 bytes_read, file_size);
            return false;
        }

        sp<JSONCompound> json = JSONCompound::Parse(file_data.data(), file_size);
        if (json == nullptr || !json->isObject()) {
            // If there's an existing file and we couldn't parse it, or it
            // parsed to something unexpected, then we don't want to wipe out
            // the file - the user needs to decide what to do, e.g. they can
            // manually edit to fix corruption, or delete it, etc.
            LOGE("Couldn't parse sensor calibration file (requires manual "
                 "resolution)");
            return false;
        } else {
            json_root_ = reinterpret_cast<JSONObject*>(json.get());
            LOGD("Parsed JSONObject from file:\n%s",
                 json_root_->toString().c_str());
        }
    }

    // No errors, but there was no existing calibration data so construct a new
    // object
    if (json_root_ == nullptr) {
        json_root_ = new JSONObject();
    }

    return true;
}

const sp<JSONObject> CalibrationFile::GetJSONObject() const {
    return json_root_;
}

bool CalibrationFile::SetSingleAxis(const char *key, int32_t value) {
    json_root_->setInt32(key, value);
    return true;
}

bool CalibrationFile::SetSingleAxis(const char *key, float value) {
    json_root_->setFloat(key, value);
    return true;
}

bool CalibrationFile::SetTripleAxis(const char *key, int32_t x, int32_t y,
        int32_t z) {
    sp<JSONArray> json_array = new JSONArray();
    json_array->addInt32(x);
    json_array->addInt32(y);
    json_array->addInt32(z);
    json_root_->setArray(key, json_array);
    return true;
}

bool CalibrationFile::SetFourAxis(const char *key, int32_t x, int32_t y,
        int32_t z, int32_t w) {
    sp<JSONArray> json_array = new JSONArray();
    json_array->addInt32(x);
    json_array->addInt32(y);
    json_array->addInt32(z);
    json_array->addInt32(w);
    json_root_->setArray(key, json_array);
    return true;
}

bool CalibrationFile::Save() {
    AString json_str = json_root_->toString();
    LOGD("Saving JSONObject to file (%zd bytes):\n%s", json_str.size(),
         json_str.c_str());
    file_->seekTo(0, SEEK_SET);
    ssize_t bytes_written = file_->write(json_str.c_str(), json_str.size());
    if (bytes_written < 0 || static_cast<size_t>(bytes_written) != json_str.size()) {
        LOGE("Write returned %zd, expected %zu", bytes_written, json_str.size());
        return false;
    }
    return true;
}

}  // namespace android