/**
   * Syllabify a LinkedList of phones. This is an implementation of the syllabification rules by
   * Jürgen Trouvain.
   *
   * @param phoneList phoneList
   * @return a LinkedList of phone strings with inserted "-" strings at syllable boundaries.
   */
  public LinkedList<String> syllabify(LinkedList<String> phoneList) {
    // Regel(1a)
    // Jede Grenze einer morphologischen Wurzel stellt eine
    // Silbengrenze dar.
    // Regel(1b)
    // Jede Grenze eines Präfixes stellt eine Silbengrenze dar.
    // Dort, wo ein Fugensuffix bzw ein Suffix beginnt,
    // gibt es keine morphologische Silbengrenze.
    // Bsp: Lebens-gefaehrte und nicht Leben-s-gefaehrte
    // Bsp: Mei-nung und nicht Mein-ung

    // Note: We don't have morpheme boundaries! (=> so ignore rule 1)

    // 2.: finde nicht-morphologische Silbengrenzen
    // teile Woerter, die eine morphologisch bedingte Silbengrenze haben
    // in ihre Silbenteile, um dort spaeter nach weiteren,
    // nicht-morphologisch bedingten, Silbengrenzen zu suchen

    // Only one such component as long as we don't have morpheme boundaries

    if (phoneList == null) {
      throw new IllegalArgumentException("Cannot syllabify null string");
    }
    ListIterator<String> it = phoneList.listIterator(0);
    if (!it.hasNext()) {
      return phoneList;
    }
    Allophone previous = getAllophone(it.next());
    boolean previousIsVowel = (previous != null && previous.sonority() >= 4);
    while (it.hasNext()) {
      Allophone next = getAllophone(it.next());
      boolean nextIsVowel = (next != null && next.sonority() >= 4);

      // Regel(5)
      // Wenn zwischen zwei Vokalen keine weiteren Phone sind,
      // dann setze die Silbengrenze vor den zweiten Vokal.
      if (previousIsVowel && nextIsVowel && !next.name().equals("6")) {
        // Insert a syllable boundary between the two.
        it.previous(); // one step back
        it.add("-"); // insert syllable boundary
        it.next(); // and forward again
      }
      previousIsVowel = nextIsVowel;
    }

    // Regel(4)
    // Suche das "Tal" (kleinster Level < 4) zwischen zwei benachbarten
    // Vokalen, sofern die Vokale nicht durch eine morphologisch bedingte
    // Grenze getrennt werden.
    it = phoneList.listIterator(0);
    int minSonority = 7; // one higher than possible maximum.
    int minIndex = -1; // position of the sonority minimum
    int syllableStart = -1;
    while (it.hasNext()) {
      String s = it.next();
      if (s.equals("-")) {
        // Forget about all valleys:
        minSonority = 7;
        minIndex = -1;
        syllableStart = it.previousIndex();
      } else {
        Allophone ph = getAllophone(s);
        if (ph != null && ph.sonority() < minSonority) {
          minSonority = ph.sonority();
          minIndex = it.previousIndex();
        } else if (ph != null && ph.sonority() >= 4) {
          // Found a vowel. Now, if there is a (non-initial) sonority
          // valley before this vowel, insert a valley marker:
          if (minIndex > syllableStart + 1) {
            int steps = 0;
            while (it.nextIndex() > minIndex) {
              steps++;
              it.previous();
            }
            it.add(".");
            while (steps > 0) {
              it.next();
              steps--;
            }
          }
          minSonority = 7;
          minIndex = -1;
        }
      }
    }

    // Regel(6a)
    // Steht zwischen einem ungespannten Vokal (Level 5) und dem
    // darauffolgenden Vokal (Level 4, 5 oder 6) nur *ein* Konsonant des
    // Levels 2 oder 3, so ersetze die Talmarkierung durch eine
    // ambisilbische Silbengrenze (Symbol "_").
    // halbformal:
    // ([v5]).([k2,3])([v4,5,6])
    // --> ([v5])_([k2,3])([v4,5,6])

    // Regel(6b)
    // Steht zwischen einem ungespannten Vokal (Level 5) und dem
    // darauffolgenden Vokal (Level 4, 5 oder 6) mehr als ein Konsonant
    // (Levels 1,2 oder 3), und folgt gleichzeitig dem 5er-Vokal eine
    // Talmarkierung, so versetze die "Talmarkierung" ein Phonem weiter und
    // ersetze sie durch eine normale Silbengrenze.
    // halbformal:
    // ([v5]).([k1,2,3])([k1,2,3]+)([v4,5,6])
    // --> ([v5]).([k1,2,3])-([k1,2,3]+)([v4,5,6])

    // Regel(6c)
    // In allen anderen Faellen ersetze die "Talmarkierung" mit einer
    // normalen Silbengrenze.
    it = phoneList.listIterator(0);
    while (it.hasNext()) {
      String s = it.next();
      if (s.equals(".")) {
        it.previous(); // skip . backwards
        Allophone ph = getAllophone(it.previous());
        it.next();
        it.next(); // skip ph and . forwards
        if (ph != null && ph.sonority() == 5) {
          // The phone just after the marker:
          ph = getAllophone(it.next());
          if (ph != null && ph.sonority() <= 3) {
            // Now the big question: another consonant or not?
            ph = getAllophone(it.next());
            if (ph != null && ph.sonority() <= 3) {
              // (6b) remove ., go one further, insert -
              // two ph back, and the .:
              it.previous();
              it.previous();
              it.previous();
              it.remove(); // remove the .
              it.next(); // skip one ph
              it.add("-");
            } else {
              // (6a) replace . with _
              // two ph back, and the .:
              it.previous();
              it.previous();
              it.previous();

              // only use minuses, because underscores denote also pauses
              // it.set("_"); // replace . with _
              it.set("-"); // replace . with -
            }
          } else {
            // unlikely case: no consonant after a 5
            it.previous();
            it.previous();
            it.set("-");
          }
        } else {
          // (6c) simply replace . with -
          it.set("-");
        }
      }
    }

    // Regel(7)
    // Folgt einem Phonem /N/, vor dem unmittelbar eine ambisilbische
    // Silbengrenze steht, ein Vollvokal (Level 5 oder 6), so verschiebe
    // die Silbengrenze um ein Phonem (naemlich hinter das /N/) und
    // ersetze es durch eine normale Silbengrenze.
    // halbformal:
    // _N([v5,6])
    // --> N-([v5,6])
    it = phoneList.listIterator(0);
    while (it.hasNext()) {
      String s = it.next();
      // only use minuses, because underscores denote also pauses
      // if (s.equals("_")) {
      if (s.equals("-")) {
        Allophone ph = getAllophone(it.next());
        if (ph != null && ph.name().equals("N")) {
          ph = getAllophone(it.next());
          if (ph != null && ph.sonority() >= 5) {
            // (7) remove _, put a - after the N
            // skip vowel, N, and _ backwards:
            it.previous();
            it.previous();
            it.previous();
            it.remove(); // remove _
            it.next(); // skip N forwards
            it.add("-"); // insert -
          } // else, just leave it
        }
      }
    }
    correctStressSymbol(phoneList);
    return phoneList;
  }
  /**
   * For those syllables containing a "1" character, remove that "1" character and add a stress
   * marker ' at the beginning of the syllable.
   *
   * @param phoneList phoneList
   */
  protected void correctStressSymbol(LinkedList<String> phoneList) {
    boolean stressFound = false;
    ListIterator<String> it = phoneList.listIterator(0);
    while (it.hasNext()) {
      String s = it.next();
      if (s.endsWith("1")) {
        if (this.removeTrailingOneFromPhones) {
          it.set(s.substring(0, s.length() - 1)); // delete "1"
        }
        if (!stressFound) {
          // Only add a stress marker for first occurrence of "1":
          // Search backwards for syllable boundary or beginning of word:
          int steps = 0;
          while (it.hasPrevious()) {
            steps++;
            String t = it.previous();
            if (t.equals("-") || t.equals("_")) { // syllable boundary
              it.next();
              steps--;
              break;
            }
          }
          it.add("'");
          while (steps > 0) {
            it.next();
            steps--;
          }
          stressFound = true;
        }
      }
    }
    // No stressed vowel in word?
    if (!stressFound) {
      // Stress first non-schwa syllable
      it = phoneList.listIterator(0);
      while (it.hasNext()) {
        String s = it.next();

        // HB there's a problem here, not sure why, but s can be "-" in some circumstances and we
        // get
        // java.lang.IllegalArgumentException: Allophone `-' could not be found in AllophoneSet
        // `sampa' (Locale: sv)
        // in that case
        if (s == "-") {
          System.err.println("Problem with -");
          break;
        }

        Allophone ph = allophoneSet.getAllophone(s);
        if (ph.sonority() >= 5) { // non-schwa vowel
          // Search backwards for syllable boundary or beginning of word:
          int steps = 0;
          while (it.hasPrevious()) {
            steps++;
            String t = it.previous();
            if (t.equals("-") || t.equals("_")) { // syllable boundary
              it.next();
              steps--;
              break;
            }
          }
          it.add("'");
          while (steps > 0) {
            it.next();
            steps--;
          }
          break; // OK, that's it.
        }
      }
    }
  }