// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_WIN_WINRT_FOUNDATION_HELPERS_H_
#define BASE_WIN_WINRT_FOUNDATION_HELPERS_H_

#include <windows.foundation.h>
#include <wrl/client.h>

#include <vector>

#include "base/optional.h"
#include "base/win/hstring_compare.h"

// This file provides helpers for WinRT types.

namespace base {
namespace win {
namespace internal {

// Template tricks needed to dispatch to the correct implementation.
//
// For all types which are neither InterfaceGroups nor RuntimeClasses, the
// following three typedefs are synonyms for a single C++ type.  But for
// InterfaceGroups and RuntimeClasses, they are different types:
//   LogicalT: The C++ Type for the InterfaceGroup or RuntimeClass, when
//             used as a template parameter.  Eg "RCFoo*"
//   AbiT:     The C++ type for the default interface used to represent the
//             InterfaceGroup or RuntimeClass when passed as a method parameter.
//             Eg "IFoo*"
//   ComplexT: An instantiation of the Internal "AggregateType" template that
//             combines LogicalT with AbiT. Eg "AggregateType<RCFoo*,IFoo*>".
//             ComplexT is tightly coupled to the interface being implemented,
//             hence defined in headers which include this file.
//             For instance base/win/async_operation.h or
//             base/win/collection_helpers.h
//
// windows.foundation.collections.h defines the following template and
// semantics in Windows::Foundation::Internal:
//
// template <class LogicalType, class AbiType>
// struct AggregateType;
//
//   LogicalType - the Windows Runtime type (eg, runtime class, interface group,
//                 etc) being provided as an argument to an _impl template, when
//                 that type cannot be represented at the ABI.
//   AbiType     - the type used for marshalling, ie "at the ABI", for the
//                 logical type.
template <typename TComplex>
using AbiType =
    typename ABI::Windows::Foundation::Internal::GetAbiType<TComplex>::type;

template <typename TComplex>
using LogicalType =
    typename ABI::Windows::Foundation::Internal::GetLogicalType<TComplex>::type;

// Compile time switch to decide what container to use for |TComplex|.
// Depends on whether the underlying Abi type is a pointer to IUnknown or not.
// It queries the internals of Windows::Foundation to obtain this information.
template <typename TComplex>
using StorageType = std::conditional_t<
    std::is_convertible<AbiType<TComplex>, IUnknown*>::value,
    Microsoft::WRL::ComPtr<std::remove_pointer_t<AbiType<TComplex>>>,
    AbiType<TComplex>>;

// Similar to StorageType, but returns a base::Optional in case underlying Abi
// type is not a pointer to IUnknown.
template <typename TComplex>
using OptionalStorageType = std::conditional_t<
    std::is_convertible<AbiType<TComplex>, IUnknown*>::value,
    Microsoft::WRL::ComPtr<std::remove_pointer_t<AbiType<TComplex>>>,
    base::Optional<AbiType<TComplex>>>;

template <typename T>
HRESULT CopyTo(const T& value, T* ptr) {
  *ptr = value;
  return S_OK;
}

template <typename T>
HRESULT CopyTo(const Microsoft::WRL::ComPtr<T>& value, T** ptr) {
  return value.CopyTo(ptr);
}

template <typename T>
HRESULT CopyTo(const base::Optional<T>& value, T* ptr) {
  *ptr = *value;
  return S_OK;
}

template <typename T>
HRESULT CopyN(typename std::vector<T>::const_iterator first,
              unsigned count,
              T* result) {
  std::copy_n(first, count, result);
  return S_OK;
}

template <typename T>
HRESULT CopyN(
    typename std::vector<Microsoft::WRL::ComPtr<T>>::const_iterator first,
    unsigned count,
    T** result) {
  for (unsigned i = 0; i < count; ++i)
    CopyTo(*first++, result++);
  return S_OK;
}

inline bool IsEqual(const HSTRING& lhs, const HSTRING& rhs) {
  INT32 result;
  HRESULT hr = HStringCompare(lhs, rhs, &result);
  DCHECK(SUCCEEDED(hr));
  return result == 0;
}

template <typename T>
bool IsEqual(const T& lhs, const T& rhs) {
  return lhs == rhs;
}

template <typename T>
bool IsEqual(const Microsoft::WRL::ComPtr<T>& com_ptr, const T* ptr) {
  return com_ptr.Get() == ptr;
}

struct Less {
  bool operator()(const HSTRING& lhs, const HSTRING& rhs) const {
    INT32 result;
    HRESULT hr = HStringCompare(lhs, rhs, &result);
    DCHECK(SUCCEEDED(hr));
    return result < 0;
  }

  template <typename T>
  bool operator()(const Microsoft::WRL::ComPtr<T>& com_ptr,
                  const T* ptr) const {
    return com_ptr.Get() < ptr;
  }

  template <typename T>
  constexpr bool operator()(const T& lhs, const T& rhs) const {
    return lhs < rhs;
  }
};

}  // namespace internal
}  // namespace win
}  // namespace base

#endif  // BASE_WIN_WINRT_FOUNDATION_HELPERS_H_
