DataTypes.kt

/*
 * Copyright (C) 2021 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.
 */

package androidx.health.services.client.data

/** Helper class to facilitate working with [DataTypes] [DataType]. */
public object DataTypes {
    private val AGGREGATE_TYPE_TO_RAW_TYPE =
        DataTypeBiMap(
            DataType.AGGREGATE_DISTANCE to DataType.DISTANCE,
            DataType.AGGREGATE_CALORIES_EXPENDED to DataType.TOTAL_CALORIES,
            DataType.AGGREGATE_STEP_COUNT to DataType.STEPS,
            DataType.AGGREGATE_ELEVATION to DataType.ELEVATION,
            DataType.AGGREGATE_FLOORS to DataType.FLOORS,
            DataType.AGGREGATE_SWIMMING_STROKE_COUNT to DataType.SWIMMING_STROKES
        )

    private val MAX_TYPE_TO_RAW_TYPE =
        DataTypeBiMap(
            DataType.MAX_HEART_RATE_BPM to DataType.HEART_RATE_BPM,
            DataType.MAX_PACE to DataType.PACE,
            DataType.MAX_SPEED to DataType.SPEED
        )

    private val AVERAGE_TYPE_TO_RAW_TYPE =
        DataTypeBiMap(
            DataType.AVERAGE_HEART_RATE_BPM to DataType.HEART_RATE_BPM,
            DataType.AVERAGE_PACE to DataType.PACE,
            DataType.AVERAGE_SPEED to DataType.SPEED
        )

    /** Check if a [DataType] represents aggregate value of a collection of non-aggregate data. */
    @JvmStatic
    public fun isAggregateDataType(dataType: DataType): Boolean =
        AGGREGATE_TYPE_TO_RAW_TYPE.map.containsKey(dataType)

    /** Check if a [DataType] represents the maximum value of a collection of non-aggregate data. */
    @JvmStatic
    public fun isStatisticalMaxDataType(dataType: DataType): Boolean =
        MAX_TYPE_TO_RAW_TYPE.map.containsKey(dataType)

    /** Check if a [DataType] represents the average value of a collection of non-aggregate data. */
    @JvmStatic
    public fun isStatisticalAverageDataType(dataType: DataType): Boolean =
        AVERAGE_TYPE_TO_RAW_TYPE.map.containsKey(dataType)

    /**
     * Check if a [DataType] represents raw data value, i.e., neither aggregate value nor
     * statistical value.
     */
    @JvmStatic
    public fun isRawType(dataType: DataType): Boolean =
        !isAggregateDataType(dataType) &&
            !isStatisticalMaxDataType(dataType) &&
            !isStatisticalAverageDataType(dataType)

    /** Get the aggregate [DataType] from a raw [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getAggregateTypeFromRawType(rawType: DataType): DataType? =
        AGGREGATE_TYPE_TO_RAW_TYPE.inverse[rawType]

    /** Get the raw [DataType] from an aggregate [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getRawTypeFromAggregateType(aggregateType: DataType): DataType? =
        AGGREGATE_TYPE_TO_RAW_TYPE.map[aggregateType]

    /** Get the max [DataType] from a raw [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getMaxTypeFromRawType(rawType: DataType): DataType? =
        MAX_TYPE_TO_RAW_TYPE.inverse[rawType]

    /** Get the raw [DataType] from a max [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getRawTypeFromMaxType(maxType: DataType): DataType? =
        MAX_TYPE_TO_RAW_TYPE.map[maxType]

    /** Get the average [DataType] from a raw [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getAverageTypeFromRawType(rawType: DataType): DataType? =
        AVERAGE_TYPE_TO_RAW_TYPE.inverse[rawType]

    /** Get the raw [DataType] from an average [DataType], or null if it doesn't exist. */
    @JvmStatic
    public fun getRawTypeFromAverageType(averageType: DataType): DataType? =
        AVERAGE_TYPE_TO_RAW_TYPE.map[averageType]

    /** Get the aggregate, average, and max [DataType] from a raw [DataType] if they exist. */
    @JvmStatic
    public fun getAggregatedDataTypesFromRawType(rawType: DataType): Set<DataType> {
        val allDataTypes = HashSet<DataType>()

        getAggregateTypeFromRawType(rawType)?.let { allDataTypes.add(it) }
        getMaxTypeFromRawType(rawType)?.let { allDataTypes.add(it) }
        getAverageTypeFromRawType(rawType)?.let { allDataTypes.add(it) }

        return allDataTypes
    }

    private class DataTypeBiMap(vararg pairs: Pair<DataType, DataType>) {
        val map = mapOf(*pairs)
        val inverse = pairs.map { it.second to it.first }.toMap()
    }
}