/*
 * 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.
 */
#ifndef HIDUTIL_HIDPARSER_H_
#define HIDUTIL_HIDPARSER_H_

#include "HidItem.h"
#include "HidTree.h"
#include "HidGlobal.h"
#include "HidLocal.h"
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <array>
#include <ostream>

namespace HidUtil {
class HidParser {
public:
    enum {
        REPORT_TYPE_FEATURE = 0,
        REPORT_TYPE_INPUT = 1,
        REPORT_TYPE_OUTPUT = 2
    };

    struct ReportItem;
    struct ReportPacket;

    // report (including input output and feature) grouped by full usage
    struct ReportDigest {
        unsigned int fullUsage;
        std::vector<ReportPacket> packets;
    };

    typedef std::vector<ReportDigest> DigestVector;

    // parse HID descriptor
    bool parse(const std::vector<HidItem> &token);
    bool parse(const unsigned char *begin, size_t size);

    // filter the tree to eliminate single child report leaf node causes by usage array type
    // reports
    void filterTree();

    // generate a list of report digest for all interested usage. It will automatically
    // call filterTree().
    DigestVector generateDigest(const std::unordered_set<unsigned int> &interestedUsage);

    // get parsed tree (filtered or not filtered)
    const std::shared_ptr<HidTreeNode> getTree() const { return mTree; }

    // get all parsed report in a parsed form.
    const std::vector<HidReport>& getReport() const { return mReport; }

private:
    typedef std::array<std::vector<HidReport>, 3> ReportSet;
    typedef std::unordered_map<unsigned int /* reportId */, ReportSet> ReportSetGroup;

    // helper subroutines
    void reset();
    bool processMainTag(const HidItem &i);
    static void filterTree(std::shared_ptr<HidTreeNode> &node);
    static void digest(
            DigestVector *digestVector,
            const std::shared_ptr<HidTreeNode> &node,
            const std::unordered_set<unsigned int> &interestedUsage);
    static std::vector<ReportPacket> convertGroupToPacket(const ReportSetGroup &group);

    HidGlobalStack mGlobalStack;
    HidLocal mLocal;
    std::shared_ptr<HidTreeNode> mTree;
    std::shared_ptr<HidTreeNode> mCurrent;
    std::vector<HidReport> mReport;
};

struct HidParser::ReportItem {
    unsigned int usage;
    unsigned int id;
    int type; // feature, input or output

    int64_t minRaw;
    int64_t maxRaw;

    // conversion for float point values
    // real value = (signExtendIfNeeded(raw) + b) * a
    // raw value = mask(real/a - b);
    //
    // conversion for integer values
    // real value = signExtendedIfNeeded(raw) + b;
    // raw value = mask(real - b);
    double a; // scaling
    int64_t b; // offset
    unsigned int unit;

    size_t bitOffset;
    size_t bitSize; // bit length per unit
    size_t count;

    // helper function
    bool isSigned() const {
        return minRaw < 0;
    }

    bool isByteAligned() const {
        return (bitOffset & 7) == 0 && (bitSize & 7) == 0;
    }

    // convert raw values to unsigned format
    uint32_t mask(int64_t input) const {
        return static_cast<uint32_t>(input & rawMask());
    }

    bool decode(uint32_t input, double *output) const {
        if (output == nullptr) {
            return false;
        }
        int64_t s = signExtendIfNeeded(input);
        if (s < minRaw || s > maxRaw) {
            return false;
        }
        *output = (s + b) * a;
        return true;
    }

    bool encode(double input, uint32_t *output) const {
        if (output == nullptr) {
            return false;
        }
        input = input / a - b;
        if (input < minRaw || input > maxRaw) {
            return false;
        }
        *output = static_cast<uint32_t>(static_cast<int64_t>(input) & rawMask());
        return true;
    }

    int64_t rawMask() const {
        constexpr int64_t one = 1;
        return (one << bitSize) - 1;
    }

    int64_t signExtendIfNeeded(int64_t value) const {
        return value | ((isSigned() && isNegative(value)) ? ~rawMask() : 0);
    }

    bool isNegative(int64_t value) const {
        constexpr int64_t one = 1;
        return ((one << (bitSize - 1)) & value) != 0;
    }
};

// a collection of report item that forms a packet
// this is the input output unit with HID hardware
struct HidParser::ReportPacket {
    std::vector<ReportItem> reports;
    size_t bitSize;
    int type; // REPORT_TYPE_FEATURE/INPUT/OUTPUT
    unsigned int id;

    size_t getByteSize() const { return (bitSize + 7) / 8; };
};

std::ostream& operator<<(std::ostream &os, const HidParser::DigestVector &digest2);
} // namespace HidUtil

#endif // HIDUTIL_HIDPARSER_H_