package lojban

object SentenceRecognizer {
  val vowels = "aeiou"
  val consonants = "bcdfgjklmnprstvxyz"

  def main(args: Array[String]): Unit = {
    // http://www.lojban.org/tiki/Simple%20phrases
    // Does not include h q w
    // Capital letters not needed
    // I don't understand: i mi na jimpe
    }
  
  /** Test whether the character is a vowel ("aeiou") */
  def isV(ch: Char) = "aeiou" contains ch
  
  /** Test whether the string consists of a single vowel */
  def isV(s: String): Boolean = s.length == 1 && isV(s(0))
  
  /** Test whether the character is a consonant
   *  ('h', 'q', and 'w' are not used) */
  def isC(ch: Char) = "bcdfgjklmnprstvxyz" contains ch
  
  /** Test whether the string consists of a single consonant */
  def isC(s: String): Boolean = s.length == 1 && isC(s(0))

  /** Sentence is a Statement or a Predclaim */
   def isSentence(s: String) = {
    isStatement(s) || isPredclaim(s)
  }

  /** Predclaim is a Predname BA Preds or a DA Preds */
  def isPredclaim(s: String): Boolean = {
    val words = getWords(s)
    (isDA(words(0)) && isPreds(reassemble(words.tail))) ||
      (splitOff(words, isPredname) match {
        case Some((predname, more)) =>
          isBA(more(0)) && isPreds(reassemble(more tail))
        case None => false
      })
  }

  /** Preds is a Predstring or a Preds A Predstring */
  def isPreds(s: String): Boolean = {
    val words = getWords(s)
    val initialPredstring = words.takeWhile(isPRED _)
    if (initialPredstring isEmpty) false
    else {
      val moreWords = words drop initialPredstring.length
      (moreWords isEmpty) || 
        (isA(moreWords(0)) && isPreds(reassemble(moreWords.tail)))
    }
  }

  /** Predname is a LA Predstring or a NAME */
  def isPredname(s: String) = {
    val words = getWords(s)
    (words.length == 1 && isNAME(words(0))) ||
      (isLA(words(0)) && isPredstring(reassemble(words.tail)))
  }
  
  /** Predstring is a PRED or a Predstring PRED */
  def isPredstring(s: String) =
    (s.trim.split(" ")) forall (x => isPRED(x))
  
   /** Statement is a Predname Verbpred Predname or a Predname Verbpred */
  def isStatement(s: String) = {
    val words = getWords(s)
    splitOff(words, isPredname) match {
        case Some((predname, more)) =>
          splitOff(more, isVerbpred) match {
            case Some((verbpred, moreyet)) =>
              (moreyet.isEmpty || isPredname(reassemble(moreyet)))
            case None => false
          }
        case None => false
    }
  }

  /** Verbpred is a MOD Predstring */
  def isVerbpred(s: String) = {
    val words = getWords(s)
    words.length > 1 && isMOD(words(0)) && isPredstring(reassemble(words.tail))
  }
  
  /** A is "a" or "e" or "i" or "o" or "u" */
  def isA(s: String) = {
    s.length == 1 && isV(s(0))
  }
  
  /** MOD is "ga" or "ge" or "gi" or "go" or "gu" */
  def isMOD(s: String) = {
    List("ga", "ge", "gi", "go", "gu") contains s
  }
  
  /** BA is "ba" or "be" or "bi" or "bo" or "bu" */
  def isBA(s: String) = {
    List("ba", "be", "bi", "bo", "bu") contains s 
  }
  
  /** DA is "da" or "de" or "di" or "do" or "du" */
  def isDA(s: String) = {
    List("da", "de", "di", "do", "du") contains s
  }
  
  /** LA is "la" or "le" or "li" or "lo" or "lu" */
  def isLA(s: String) = {
    List("la", "le", "li", "lo", "lu") contains s
  }
  
  /** NAME is any name (must end in a consonant) */
  def isNAME(s: String) = {
    s.length > 0 && isC(s.last)
  }
  
  /** PRED is any predicate -- must have the form CCVCV or CVCCV */
  def isPRED(s: String) = {
    val cv = s map ((x: Char) => if (isC(x)) 'C' else if (isV(x)) 'V' else '*')
    (cv == "CCVCV" || cv == "CVCCV") && s(0) != s(1) && s(2) != s(3)
  }
  
  /** Turns a string of words into a list of words */
  private def getWords(s: String): List[String] = {
    s.trim.split(" ").toList
  }
  
  /** Turns a list of words into a string of words */
  private def reassemble(words: List[String]): String = {
    words.mkString(" ")
  }
  
  /** Tries to find the longest match for the nonterminal
   *  (must be at least one word) at the beginning of the
   *  list of words, and if found, returns a tuple of two
   *  lists: The words satisfying the nonterminal, and the
   *  rest of the words.
   */
  private def splitOff(words: List[String],
                       nonterminal: String => Boolean):
                       Option[(List[String], List[String])] = {
    for (n <- words.length to 1 by -1) {
      if (nonterminal(reassemble(words take n))) {
        return Some((words take n, words drop n))
      }
    }
    None
  }
}