/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#if defined(MOZILLA_INTERNAL_API)
#  error This code is NOT for internal Gecko use!
#endif  // defined(MOZILLA_INTERNAL_API)

#include "HandlerRelation.h"
#include "mozilla/Assertions.h"

#include "AccessibleRelation_i.c"

namespace mozilla {
namespace a11y {

HandlerRelation::HandlerRelation(AccessibleHandler* aHandler,
                                 IARelationData& aData)
    : mHandler(aHandler), mData(aData), mTargets(nullptr) {
  // This instance now owns any pointers, so ensure no one else can
  // manipulate them.
  aData.mType = nullptr;
}

HandlerRelation::~HandlerRelation() {
  if (mData.mType) {
    ::SysFreeString(mData.mType);
  }

  if (mTargets) {
    for (long index = 0; index < mData.mNTargets; ++index) {
      mTargets[index]->Release();
    }
    ::CoTaskMemFree(mTargets);
    mTargets = nullptr;
  }
}

HRESULT
HandlerRelation::GetTargets() {
  if (mTargets) {
    // Already cached.
    return S_OK;
  }

  // Marshaling all of the IAccessibleRelation objects across processes is
  // slow, and the client probably only wants targets for a few of them.
  // Therefore, we just use IAccessible2_2::relationTargetsOfType, passing
  // the type we have cached. This is a bit inefficient because Gecko has
  // to look up the relation twice, but it's significantly faster than
  // marshaling the relation objects regardless.
  return mHandler->get_relationTargetsOfType(mData.mType, 0, &mTargets,
                                             &mData.mNTargets);
}

IMPL_IUNKNOWN1(HandlerRelation, IAccessibleRelation)

/*** IAccessibleRelation ***/

HRESULT
HandlerRelation::get_relationType(BSTR* aType) {
  if (!aType) {
    return E_INVALIDARG;
  }
  if (!mData.mType) {
    return E_FAIL;
  }
  *aType = CopyBSTR(mData.mType);
  return S_OK;
}

HRESULT
HandlerRelation::get_localizedRelationType(BSTR* aLocalizedType) {
  // This is not implemented as per ia2AccessibleRelation.
  return E_NOTIMPL;
}

HRESULT
HandlerRelation::get_nTargets(long* aNTargets) {
  if (!aNTargets) {
    return E_INVALIDARG;
  }
  if (mData.mNTargets == -1) {
    return E_FAIL;
  }
  *aNTargets = mData.mNTargets;
  return S_OK;
}

HRESULT
HandlerRelation::get_target(long aIndex, IUnknown** aTarget) {
  if (!aTarget) {
    return E_INVALIDARG;
  }

  HRESULT hr = GetTargets();
  if (FAILED(hr)) {
    return hr;
  }
  if (aIndex >= mData.mNTargets) {
    return E_INVALIDARG;
  }

  *aTarget = mTargets[aIndex];
  (*aTarget)->AddRef();
  return S_OK;
}

HRESULT
HandlerRelation::get_targets(long aMaxTargets, IUnknown** aTargets,
                             long* aNTargets) {
  if (aMaxTargets == 0 || !aTargets || !aNTargets) {
    return E_INVALIDARG;
  }

  HRESULT hr = GetTargets();
  if (FAILED(hr)) {
    return hr;
  }

  if (mData.mNTargets > aMaxTargets) {
    // Don't give back more targets than were requested.
    *aNTargets = aMaxTargets;
  } else {
    *aNTargets = mData.mNTargets;
  }

  for (long index = 0; index < *aNTargets; ++index) {
    aTargets[index] = mTargets[index];
    aTargets[index]->AddRef();
  }
  return S_OK;
}

}  // namespace a11y
}  // namespace mozilla