public static void addSpellsToClassForLevels(
      PlayerCharacter pc, Domain d, PCClass aClass, int minLevel, int maxLevel) {
    if (aClass == null) {
      return;
    }

    for (int aLevel = minLevel; aLevel <= maxLevel; aLevel++) {
      Collection<Spell> domainSpells = pc.getSpellsIn(d.get(ObjectKey.DOMAIN_SPELLLIST), aLevel);
      for (Spell spell : domainSpells) {
        List<CharacterSpell> slist =
            pc.getCharacterSpells(aClass, spell, Globals.getDefaultSpellBook(), aLevel);
        boolean flag = true;

        for (CharacterSpell cs1 : slist) {
          flag = !(cs1.getOwner().equals(d));

          if (!flag) {
            break;
          }
        }

        if (flag) {
          CharacterSpell cs = new CharacterSpell(d, spell);
          cs.addInfo(aLevel, 1, Globals.getDefaultSpellBook());
          pc.addCharacterSpell(aClass, cs);
        }
      }
    }
  }
  /**
   * Sets the locked flag on a PC
   *
   * @param pc
   */
  public static void applyDomain(PlayerCharacter pc, Domain d) {
    ClassSource source = pc.getDomainSource(d);
    PCClass aClass = pc.getClassKeyed(source.getPcclass().getKeyName());
    if (aClass != null) {
      int maxLevel;

      for (maxLevel = 0; maxLevel < 10; maxLevel++) {
        if (pc.getSpellSupport(aClass).getCastForLevel(maxLevel, pc) == 0) {
          break;
        }
      }

      if (maxLevel > 0) {
        addSpellsToClassForLevels(pc, d, aClass, 0, maxLevel - 1);
      }

      if ((maxLevel > 1) && (aClass.getSafe(IntegerKey.KNOWN_SPELLS_FROM_SPECIALTY) == 0)) {
        DomainSpellList domainSpellList = d.get(ObjectKey.DOMAIN_SPELLLIST);
        final List<Spell> aList =
            pc.getAllSpellsInLists(Collections.singletonList(domainSpellList));

        for (Spell gcs : aList) {
          if (SpellLevel.getFirstLvlForKey(gcs, domainSpellList, pc) < maxLevel) {
            pc.setDomainSpellCount(aClass, 1);
            break;
          }
        }
      }
    }

    Collection<CDOMReference<Spell>> mods = d.getSafeListMods(Spell.SPELLS);
    for (CDOMReference<Spell> ref : mods) {
      Collection<Spell> spells = ref.getContainedObjects();
      Collection<AssociatedPrereqObject> assoc = d.getListAssociations(Spell.SPELLS, ref);
      for (AssociatedPrereqObject apo : assoc) {
        if (!PrereqHandler.passesAll(apo.getPrerequisiteList(), pc, d)) {
          continue;
        }
        for (Spell s : spells) {
          String book = apo.getAssociation(AssociationKey.SPELLBOOK);
          List<CharacterSpell> aList = pc.getCharacterSpells(aClass, s, book, -1);

          if (aList.isEmpty()) {
            Formula times = apo.getAssociation(AssociationKey.TIMES_PER_UNIT);
            CharacterSpell cs = new CharacterSpell(d, s);
            int resolvedTimes = times.resolve(pc, d.getQualifiedKey()).intValue();
            cs.addInfo(1, resolvedTimes, book);
            pc.addCharacterSpell(aClass, cs);
          }
        }
      }
    }
  }
  /**
   * Remove any spells granted by the domain to the class.
   *
   * @param pc The character.
   * @param domain The domain.
   * @param aClass The class which would have the spells allocated.
   */
  public static void removeSpellsFromClassForLevels(
      PlayerCharacter pc, Domain domain, PCClass aClass) {
    if (aClass == null) {
      return;
    }

    Collection<? extends CharacterSpell> characterSpells = pc.getCharacterSpells(aClass);
    for (CharacterSpell characterSpell : characterSpells) {
      if (characterSpell.getOwner() == domain) {
        pc.removeCharacterSpell(aClass, characterSpell);
      }
    }
  }
  private void buildKnownPreparedSpellsForCDOMObject(CDOMObject pObject) {
    Collection<? extends CharacterSpell> sp = charDisplay.getCharacterSpells(pObject);
    List<CharacterSpell> cSpells = new ArrayList<>(sp);

    // Add in the spells granted by objects
    pc.addBonusKnownSpellsToList(pObject, cSpells);
    PCClass pcClass = (PCClass) (pObject instanceof PCClass ? pObject : null);

    for (CharacterSpell charSpell : cSpells) {
      for (SpellInfo spellInfo : charSpell.getInfoList()) {
        // Create SpellNodeImpl for each spell
        String book = spellInfo.getBook();
        boolean isKnown = Globals.getDefaultSpellBook().equals(book);
        SpellFacadeImplem spellImplem =
            new SpellFacadeImplem(pc, charSpell.getSpell(), charSpell, spellInfo);
        SpellNodeImpl node;
        if (pcClass != null) {
          node =
              new SpellNodeImpl(
                  spellImplem,
                  pcClass,
                  String.valueOf(spellInfo.getActualLevel()),
                  getRootNode(book));
        } else {
          node =
              new SpellNodeImpl(
                  spellImplem, String.valueOf(spellInfo.getActualLevel()), getRootNode(book));
        }
        if (spellInfo.getTimes() > 1) {
          node.addCount(spellInfo.getTimes() - 1);
        }
        boolean isSpellBook =
            charDisplay.getSpellBookByName(book).getType() == SpellBook.TYPE_SPELL_BOOK;
        // Add to list
        if (isKnown) {
          allKnownSpellNodes.addElement(node);
          knownSpellNodes.addElement(node);
        } else if (isSpellBook) {
          bookSpellNodes.addElement(node);
        } else if (pObject instanceof Race) {
          allKnownSpellNodes.addElement(node);
        } else {
          preparedSpellNodes.addElement(node);
        }
      }
    }
  }
  /**
   * Add a spell to the named book for the character. The request will be validated and any errors
   * shown to the user by the UIDelegate.
   *
   * @param spell The spell to be added.
   * @param bookName The book to add the spell to.
   * @param metamagicFeats List of the metamagic feats that should be applied to this spell.
   * @return The new SpellNode, or null if the selection was invalid.
   */
  private SpellNode addSpellToCharacter(
      SpellNode spell, String bookName, List<Ability> metamagicFeats) {
    if (!(spell.getSpell() instanceof SpellFacadeImplem)) {
      return null;
    }
    if (spell.getSpellcastingClass() == null) {
      return null;
    }
    CharacterSpell charSpell = ((SpellFacadeImplem) spell.getSpell()).getCharSpell();
    if (charSpell == null) {
      return null;
    }
    int level = Integer.parseInt(spell.getSpellLevel());
    for (Ability ability : metamagicFeats) {
      level += ability.getSafe(IntegerKey.ADD_SPELL_LEVEL);
    }

    String errorMsg =
        pc.addSpell(
            charSpell,
            metamagicFeats,
            spell.getSpellcastingClass().getKeyName(),
            bookName,
            level,
            level);
    if (!StringUtils.isEmpty(errorMsg)) {
      delegate.showErrorMessage(Constants.APPLICATION_NAME, errorMsg);
      return null;
    }

    SpellInfo spellInfo = charSpell.getSpellInfoFor(bookName, level, metamagicFeats);
    boolean isKnown = Globals.getDefaultSpellBook().equals(bookName);
    SpellFacadeImplem spellImplem =
        new SpellFacadeImplem(pc, charSpell.getSpell(), charSpell, spellInfo);
    SpellNodeImpl node =
        new SpellNodeImpl(
            spellImplem,
            spell.getSpellcastingClass(),
            String.valueOf(spellInfo.getActualLevel()),
            getRootNode(bookName));
    return node;
  }