/* 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/. */

/*
 * internal interface representing CSS style rules that contain other
 * rules, such as @media rules
 */

#include "mozilla/css/GroupRule.h"

#include "mozilla/dom/CSSRuleList.h"
#include "nsPrintfCString.h"

using namespace mozilla::dom;

namespace mozilla::css {

GroupRule::GroupRule(StyleSheet* aSheet, Rule* aParentRule,
                     uint32_t aLineNumber, uint32_t aColumnNumber)
    : Rule(aSheet, aParentRule, aLineNumber, aColumnNumber) {}

GroupRule::~GroupRule() {
  MOZ_ASSERT(!mSheet, "SetStyleSheet should have been called");
  if (mRuleList) {
    mRuleList->DropReferences();
  }
}

NS_IMPL_ADDREF_INHERITED(GroupRule, Rule)
NS_IMPL_RELEASE_INHERITED(GroupRule, Rule)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule)
NS_INTERFACE_MAP_END_INHERITING(Rule)

bool GroupRule::IsCCLeaf() const {
  if (!Rule::IsCCLeaf()) {
    return false;
  }
  return !mRuleList;
}

ServoCSSRuleList* GroupRule::CssRules() {
  if (!mRuleList) {
    // Lazily create the rule list since most style rules won't have child
    // rules.
    mRuleList =
        new ServoCSSRuleList(GetOrCreateRawRules(), GetStyleSheet(), this);
  }
  return mRuleList;
}

NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GroupRule, Rule)
  if (tmp->mRuleList) {
    // If tmp has a style sheet (which can happen if it gets unlinked
    // earlier than its owning style sheet), then we need to null out the
    // style sheet pointer on descendants now, before we clear mRuleList.
    tmp->mRuleList->DropReferences();
    tmp->mRuleList = nullptr;
  }
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GroupRule, Rule)
  ImplCycleCollectionTraverse(cb, tmp->mRuleList, "mRuleList");
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

#ifdef DEBUG
void GroupRule::List(FILE* out, int32_t aIndent) const {
  // TODO list something reasonable?
}
#endif

/* virtual */
void GroupRule::DropSheetReference() {
  if (mRuleList) {
    mRuleList->DropSheetReference();
  }
  Rule::DropSheetReference();
}

uint32_t GroupRule::InsertRule(const nsACString& aRule, uint32_t aIndex,
                               ErrorResult& aRv) {
  if (IsReadOnly()) {
    return 0;
  }

  StyleSheet* sheet = GetStyleSheet();
  if (NS_WARN_IF(!sheet)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return 0;
  }

  uint32_t count = StyleRuleCount();
  if (aIndex > count) {
    aRv.ThrowIndexSizeError(nsPrintfCString(
        "Can't insert rule at index %u because rule list length is %u", aIndex,
        count));
    return 0;
  }

  NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");

  nsresult rv = sheet->InsertRuleIntoGroup(aRule, this, aIndex);
  if (NS_FAILED(rv)) {
    aRv.Throw(rv);
    return 0;
  }
  return aIndex;
}

void GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv) {
  if (IsReadOnly()) {
    return;
  }

  StyleSheet* sheet = GetStyleSheet();
  if (NS_WARN_IF(!sheet)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return;
  }

  uint32_t count = StyleRuleCount();
  if (aIndex >= count) {
    aRv.ThrowIndexSizeError(nsPrintfCString(
        "Index %u is too large for list of length %u", aIndex, count));
    return;
  }

  NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");

  nsresult rv = sheet->DeleteRuleFromGroup(this, aIndex);
  if (NS_FAILED(rv)) {
    aRv.Throw(rv);
  }
}

size_t GroupRule::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
  // TODO how to implement?
  return 0;
}

}  // namespace mozilla::css
