/******************************************************************************
 * File: OpenXrUtilities.hpp
 * Copyright (c) 2021 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
 *
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 *
 ******************************************************************************/

#ifndef QCOM_OPENXR_UTILITIES_H
#define QCOM_OPENXR_UTILITIES_H

#include <list>
#include <mutex>

#include "openxr/openxr_qcom.h"

namespace wikitude::sdk {
    class Matrix4;
    class Vector3;
    class VersionNumber;
}

struct timespec;

namespace wikitude::openxr {
    XrPosef getPoseFromMatrix4(const sdk::Matrix4& matrix_);
    sdk::Matrix4 getMatrix4FromPose(const XrPosef& pose_);

    sdk::Vector3 rotateVector(const sdk::Vector3& v_, const sdk::Matrix4& transformMatrix_);

    sdk::Matrix4 convertPlanarTargetPoseToRootSpace(const sdk::Matrix4& targetToMusketIrWorld_);

    bool getPlatformTimeConversionProcAddr(XrInstance instance_, PFN_xrGetInstanceProcAddr getInstanceFunction_, PFN_xrVoidFunction& outFunction_);

    XrTime getCurrentTime(XrInstance instance_, PFN_xrVoidFunction pfnConvertPlatformTimeToTimeKHR_);

    /** @brief Return the difference between the boottime clock and monotonic clock in nanoseconds.
     *
     * The returned value is always >= 0 and signed integer is used to keep compatibility with relevant code.
     * Remark: This won't work on platforms where timespec isn't available.
     */
    int64_t getBootTimeToMonotonicClockDifferenceNs();

    /** @brief Writes heisenberg build and SDK version information to a given 'XrComponentVersion' struct.
     *
     * Remark: 'buildDate_' is expected to be in default locale (POSIX) and in format '%c' of 'strftime'
     * (e.g. "Mon May 30 15:29:32 2022"). In case this does not apply, the build date is not written to
     * the output struct.
     */
    void getXrComponentVersion(XrComponentVersionQCOM* outComponentVersion_, const char* buildNumber_, const char* buildDate_, const char* buildVersion_, const char* versionNumber_);

    /** @brief convert timespect time to int64 value
     */
    int64_t specToNs(const timespec& spec_);

    /** @brief convert int64 value to timespect time
     */
    timespec nsToSpec(int64_t ns_);

    /**@brief get time in NS based on a certain clock type e.g. BOOT_TIME,...
     */
    int64_t getTimeNS(int type_);

    // Find the first struct of a specific type in an input struct's next chain.
    template<class TStructureType, XrStructureType StructureTypeEnum, class TChainHeadType>
    const TStructureType* findInInputNextChain(TChainHeadType& chainHead_) {
        for (auto nextStruct = static_cast<const XrBaseInStructure*>(chainHead_.next); nextStruct; nextStruct = nextStruct->next) {
            if (nextStruct->type == StructureTypeEnum) {
                return reinterpret_cast<const TStructureType*>(nextStruct);
            }
        }
        return nullptr;
    }

    // Find the first struct of a specific type in an output struct's next chain.
    template<class TStructureType, XrStructureType StructureTypeEnum, class TChainHeadType>
    TStructureType* findInOutputNextChain(TChainHeadType& chainHead_) {
        for (auto nextStruct = static_cast<XrBaseOutStructure*>(chainHead_.next); nextStruct; nextStruct = nextStruct->next) {
            if (nextStruct->type == StructureTypeEnum) {
                return reinterpret_cast<TStructureType*>(nextStruct);
            }
        }
        return nullptr;
    }
}

#endif /* QCOM_OPENXR_UTILITIES_H */
