root/src/add-ons/kernel/drivers/input/hid_shared/HIDCollection.cpp
/*
 * Copyright 2009-2011, Michael Lotz, mmlr@mlotz.ch.
 * Distributed under the terms of the MIT License.
 */

#ifndef USERLAND_HID
#include "Driver.h"
#else
#include "UserlandHID.h"
#endif

#include "HIDCollection.h"
#include "HIDReport.h"
#include "HIDReportItem.h"

#include <new>
#include <stdlib.h>
#include <string.h>


HIDCollection::HIDCollection(HIDCollection *parent, uint8 type,
        local_item_state &localState)
        :       fParent(parent),
                fType(type),
                fStringID(localState.string_index),
                fPhysicalID(localState.designator_index)
{
        usage_value usageValue;
        if (localState.usage_stack != NULL && localState.usage_stack_used > 0)
                usageValue.u.extended = localState.usage_stack[0].u.extended;
        else if (localState.usage_minimum_set)
                usageValue.u.extended = localState.usage_minimum.u.extended;
        else if (localState.usage_maximum_set)
                usageValue.u.extended = localState.usage_maximum.u.extended;
        else if (type == COLLECTION_LOGICAL) {
                // this is just a logical grouping collection
                usageValue.u.extended = 0;
        } else {
                TRACE_ALWAYS("none of the possible usages for the collection are "
                        "set\n");
        }

        fUsage = usageValue.u.extended;
}


HIDCollection::~HIDCollection()
{
        for (int32 i = 0; i < fChildren.Count(); i++)
                delete fChildren[i];
}


uint16
HIDCollection::UsagePage()
{
        usage_value value;
        value.u.extended = fUsage;
        return value.u.s.usage_page;
}


uint16
HIDCollection::UsageID()
{
        usage_value value;
        value.u.extended = fUsage;
        return value.u.s.usage_id;
}


status_t
HIDCollection::AddChild(HIDCollection *child)
{
        if (fChildren.PushBack(child) == B_NO_MEMORY) {
                TRACE_ALWAYS("no memory when trying to resize collection child list\n");
        }

        return B_OK;
}


HIDCollection *
HIDCollection::ChildAt(uint32 index)
{
        int32 count = fChildren.Count();
        if (count < 0 || index >= (uint32)count)
                return NULL;

        return fChildren[index];
}


uint32
HIDCollection::CountChildrenFlat(uint8 type)
{
        uint32 count = 0;
        if (type == COLLECTION_ALL || fType == type)
                count++;

        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child == NULL)
                        continue;

                count += child->CountChildrenFlat(type);
        }

        return count;
}


HIDCollection *
HIDCollection::ChildAtFlat(uint8 type, uint32 index)
{
        return _ChildAtFlat(type, index);
}


void
HIDCollection::AddItem(HIDReportItem *item)
{
        if (fItems.PushBack(item) == B_NO_MEMORY) {
                TRACE_ALWAYS("no memory when trying to resize collection items\n");
        }

}


HIDReportItem *
HIDCollection::ItemAt(uint32 index)
{
        int32 count = fItems.Count();
        if (count < 0 || index >= (uint32)count)
                return NULL;

        return fItems[index];
}


uint32
HIDCollection::CountItemsFlat()
{
        uint32 count = fItems.Count();

        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child != NULL)
                        count += child->CountItemsFlat();
        }

        return count;
}


HIDReportItem *
HIDCollection::ItemAtFlat(uint32 index)
{
        return _ItemAtFlat(index);
}


void
HIDCollection::PrintToStream(uint32 indentLevel)
{
        char indent[indentLevel + 1];
        memset(indent, '\t', indentLevel);
        indent[indentLevel] = 0;

        const char *typeName = "unknown";
        switch (fType) {
                case COLLECTION_PHYSICAL:
                        typeName = "physical";
                        break;
                case COLLECTION_APPLICATION:
                        typeName = "application";
                        break;
                case COLLECTION_LOGICAL:
                        typeName = "logical";
                        break;
                case COLLECTION_REPORT:
                        typeName = "report";
                        break;
                case COLLECTION_NAMED_ARRAY:
                        typeName = "named array";
                        break;
                case COLLECTION_USAGE_SWITCH:
                        typeName = "usage switch";
                        break;
                case COLLECTION_USAGE_MODIFIER:
                        typeName = "usage modifier";
                        break;
        }

        TRACE_ALWAYS("%sHIDCollection %p\n", indent, this);
        TRACE_ALWAYS("%s\ttype: %u %s\n", indent, fType, typeName);
        TRACE_ALWAYS("%s\tusage: 0x%08" B_PRIx32 "\n", indent, fUsage);
        TRACE_ALWAYS("%s\tstring id: %u\n", indent, fStringID);
        TRACE_ALWAYS("%s\tphysical id: %u\n", indent, fPhysicalID);

        TRACE_ALWAYS("%s\titem count: %" B_PRIu32 "\n", indent, fItems.Count());
        for (int32 i = 0; i < fItems.Count(); i++) {
                HIDReportItem *item = fItems[i];
                if (item != NULL)
                        item->PrintToStream(indentLevel + 1);
        }

        TRACE_ALWAYS("%s\tchild count: %" B_PRIu32 "\n", indent, fChildren.Count());
        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child != NULL)
                        child->PrintToStream(indentLevel + 1);
        }
}


HIDCollection *
HIDCollection::_ChildAtFlat(uint8 type, uint32 &index)
{
        if (type == COLLECTION_ALL || fType == type) {
                if (index == 0)
                        return this;

                index--;
        }

        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child == NULL)
                        continue;

                HIDCollection *result = child->_ChildAtFlat(type, index);
                if (result != NULL)
                        return result;
        }

        return NULL;
}


HIDReportItem *
HIDCollection::_ItemAtFlat(uint32 &index)
{
        int32 count = fItems.Count();
        if (count > 0 && index < (uint32)count)
                return fItems[index];

        index -= fItems.Count();

        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child == NULL)
                        continue;

                HIDReportItem *result = child->_ItemAtFlat(index);
                if (result != NULL)
                        return result;
        }

        return NULL;
}


void
HIDCollection::BuildReportList(uint8 reportType,
        HIDReport **reportList, uint32 &reportCount)
{

        for (int32 i = 0; i < fItems.Count(); i++) {
                HIDReportItem *item = fItems[i];
                if (item == NULL)
                        continue;

                HIDReport *report = item->Report();
                if (reportType != HID_REPORT_TYPE_ANY && report->Type() != reportType)
                        continue;

                bool found = false;
                for (uint32 j = 0; j < reportCount; j++) {
                        if (reportList[j] == report) {
                                found = true;
                                break;
                        }
                }

                if (found)
                        continue;

                reportList[reportCount++] = report;
        }

        for (int32 i = 0; i < fChildren.Count(); i++) {
                HIDCollection *child = fChildren[i];
                if (child == NULL)
                        continue;

                child->BuildReportList(reportType, reportList, reportCount);
        }
}