already_AddRefed<Promise>
TelephonyCallGroup::Remove(TelephonyCall& aCall, ErrorResult& aRv)
{
  MOZ_ASSERT(!mCalls.IsEmpty());

  RefPtr<Promise> promise = CreatePromise(aRv);
  if (!promise) {
    return nullptr;
  }

  if (mState != TelephonyCallGroupState::Connected) {
    NS_WARNING("Remove call from a non-connected call group. Ignore!");
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }

  uint32_t serviceId = aCall.ServiceId();
  uint32_t callIndex = aCall.CallIndex();

  RefPtr<TelephonyCall> call = GetCall(serviceId, callIndex);
  if (!call) {
    NS_WARNING("Didn't have this call. Ignore!");
    promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
    return promise.forget();
  }

  nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
  aRv = mTelephony->Service()->SeparateCall(serviceId, callIndex, callback);
  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);

  return promise.forget();
}
void
TelephonyCallGroup::Remove(TelephonyCall& aCall, ErrorResult& aRv)
{
  if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) {
    NS_WARNING("Remove call from a non-connected call group. Ignore!");
    return;
  }

  uint32_t serviceId = aCall.ServiceId();
  uint32_t callIndex = aCall.CallIndex();

  nsRefPtr<TelephonyCall> call;

  call = GetCall(serviceId, callIndex);
  if (call) {
    aRv = mTelephony->Provider()->SeparateCall(serviceId, callIndex);
  } else {
    NS_WARNING("Didn't have this call. Ignore!");
  }
}
void
TelephonyCallGroup::Add(TelephonyCall& aCall,
                        ErrorResult& aRv)
{
  if (!CanConference(aCall, nullptr)) {
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
    return;
  }

  aRv = mTelephony->Provider()->ConferenceCall(aCall.ServiceId());
}
bool
TelephonyCallGroup::CanConference(const TelephonyCall& aCall,
                                  TelephonyCall* aSecondCall)
{
  if (!aCall.Mergeable()) {
    return false;
  }

  if (!aSecondCall) {
    MOZ_ASSERT(!mCalls.IsEmpty());

    return (mCallState == nsITelephonyProvider::CALL_STATE_CONNECTED &&
            aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD) ||
           (mCallState == nsITelephonyProvider::CALL_STATE_HELD &&
            aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED);
  }

  MOZ_ASSERT(mCallState == nsITelephonyProvider::CALL_STATE_UNKNOWN);

  if (aCall.ServiceId() != aSecondCall->ServiceId()) {
    return false;
  }

  if (!aSecondCall->Mergeable()) {
    return false;
  }

  return (aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED &&
          aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_HELD) ||
         (aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD &&
          aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED);
}
bool
TelephonyCallGroup::CanConference(const TelephonyCall& aCall,
                                  const TelephonyCall* aSecondCall)
{
  if (!aCall.Mergeable()) {
    return false;
  }

  if (!aSecondCall) {
    MOZ_ASSERT(!mCalls.IsEmpty());
    return (mState == TelephonyCallGroupState::Connected &&
            aCall.State() == TelephonyCallState::Held) ||
           (mState == TelephonyCallGroupState::Held &&
            aCall.State() == TelephonyCallState::Connected);
  }

  MOZ_ASSERT(mState != TelephonyCallGroupState::_empty);

  if (aCall.ServiceId() != aSecondCall->ServiceId()) {
    return false;
  }

  if (!aSecondCall->Mergeable()) {
    return false;
  }

  return (aCall.State() == TelephonyCallState::Connected &&
          aSecondCall->State() == TelephonyCallState::Held) ||
         (aCall.State() == TelephonyCallState::Held &&
          aSecondCall->State() == TelephonyCallState::Connected);
}
already_AddRefed<Promise>
TelephonyCallGroup::Add(TelephonyCall& aCall,
                        TelephonyCall& aSecondCall,
                        ErrorResult& aRv)
{
  RefPtr<Promise> promise = CreatePromise(aRv);
  if (!promise) {
    return nullptr;
  }

  if (!CanConference(aCall, &aSecondCall)) {
    promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
    return promise.forget();
  }

  nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
  aRv = mTelephony->Service()->ConferenceCall(aCall.ServiceId(), callback);
  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);

  return promise.forget();
}