/*
 * 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.
 */
#include "HidDefs.h"
#include "HidLog.h"
#include "HidTree.h"
#include <memory>

namespace HidUtil {

// HidTreeNode
HidTreeNode::HidTreeNode() : mNodeType(TYPE_UNINITIALIZED), mData(0), mFullUsage(0) {
}

HidTreeNode::HidTreeNode(std::shared_ptr<HidTreeNode> parent,
                         uint32_t data, uint32_t fullUsage, int nodeType)
        : mNodeType(nodeType), mData(data),
        mFullUsage(fullUsage), mParent(parent) {
}

HidTreeNode::HidTreeNode(std::shared_ptr<HidTreeNode> parent,
                         uint32_t data, uint32_t fullUsage)
        : mNodeType(TYPE_NORMAL), mData(data),
        mFullUsage(fullUsage), mParent(parent) {
}

void HidTreeNode::outputRecursive(std::ostream &os, int level) const {
    insertIndentation(os, level);
    os << "Node data: " << mData
       << ", usage " << std::hex << mFullUsage << std::dec << LOG_ENDL;

    for (auto &child : mChildren) {
        child->outputRecursive(os, level + 1);
    }
}

std::shared_ptr<HidTreeNode> HidTreeNode::deepCopy(
        std::shared_ptr<HidTreeNode> parent) const {
    std::shared_ptr<HidTreeNode> copy(new HidTreeNode(parent, mData, mFullUsage, mNodeType));
    for (auto &i : mChildren) {
        copy->mChildren.push_back(i->deepCopy(copy));
    }
    return copy;
}

void HidTreeNode::insertIndentation(std::ostream &os, int level) const {
    constexpr char indentCharacter = '\t';
    std::fill_n(std::ostreambuf_iterator<char>(os), level, indentCharacter);
}

std::shared_ptr<HidTreeNode> HidTreeNode::addChild(std::shared_ptr<HidTreeNode> child) {
    mChildren.push_back(child);
    return child;
}

std::shared_ptr<HidTreeNode> HidTreeNode::getParent() const {
    return mParent.lock();
}

bool HidTreeNode::isReportCollection() const {
    return mNodeType == TYPE_NORMAL && mChildren.size() == 1
            && mChildren.front()->mNodeType == TYPE_REPORT;
}

unsigned int HidTreeNode::getFullUsage() const {
    return mFullUsage;
}

std::vector<std::shared_ptr<HidTreeNode>>& HidTreeNode::getChildren() {
    return mChildren;
}

const std::vector<std::shared_ptr<HidTreeNode>>& HidTreeNode::getChildren() const {
    return mChildren;
}

bool HidTreeNode::isUsageCollection() const {
    using namespace HidDef::CollectionType;
    return mNodeType == TYPE_NORMAL && (mData == PHYSICAL || mData == APPLICATION);
}

int HidTreeNode::getNodeType() const {
    return mNodeType;
}

std::ostream& operator<<(std::ostream& os, const HidTreeNode& n) {
    n.outputRecursive(os, 0);
    return os;
}

// HidReportNode
HidReportNode::HidReportNode(std::shared_ptr<HidTreeNode> parent, const HidReport &report)
    : HidTreeNode(parent, 0 /*data*/, 0 /*fullUsage*/, TYPE_REPORT), mReport(report) {
}

void HidReportNode::outputRecursive(std::ostream &os, int level) const {
    insertIndentation(os, level);
    os << mReport << LOG_ENDL;
}

std::shared_ptr<HidTreeNode> HidReportNode::deepCopy(
        std::shared_ptr<HidTreeNode> parent) const {
    std::shared_ptr<HidTreeNode> copy(new HidReportNode(parent, mReport));
    return copy;
}

const HidReport& HidReportNode::getReport() const {
    return mReport;
}

void HidReportNode::collapse(unsigned int newUsage) {
    mReport.setCollapsed(newUsage);
}

} //namespace HidUtil