import vector
import association
import WFClasses
import stringplus
import io
import random

type Iterator = {
    reset->Void
    hasNext->Boolean
    get->T
    next->T}

type Vector = {
    size->Number
    isEmpty->Boolean
    clear->Void
    contains(_:T)->Boolean
    addFirst(_:T)->Void
    addLast(_:T)->Void
    firstElement->T
    lastElement->T
    removeFirst->T
    removeLast->T
    removeValue(_:T)->T
    indexOf(_:T)->Number
    at(_:Number)->T
    setValue(_:T)at(_:Number)->T
    add(_:T)at(_:Number)->Void
    removeFromIndex(_:Number)->T
    iterator->Iterator
    asString->String
    forEachDo(_:Block)->Void
    map(_:Block)->Vector
    ensureCapacity(_:Number)->Void
    capacity->Number
    copyInto(_)->Void
    indexOf(_:T)startingFrom(_:Number)->Number
    setSize(_:Number)->Void
    trimToSize->Void}

type Association = {
     ==(_:Association)->Boolean
     hashcode->Number
     getValue->V
     getKey->K
     setValue(_:V)->V
     asString->String}

type StringPair = {
    getFirst->String
    getSecond->String
    hashcode->Number
    ==(_:StringPair)->Boolean
    asString->String}

type WordStream = {
     addLexItems(_:String)->Void
     nextToken->String
     hasMoreTokens->Boolean
     resetTo(_:Number)->Void
     currentIndex->Number}

type FreqList = {
    add(_:String)->Void
    get(_:Number)->String
    asString->String}

type TextGenerator ={
    enter(_:String, _:String, _:String)->Void
    getNextWord(_:String, _:String)->String
    asString->String}

//==================================================================

class TextGeneratorClass.new{

    // List of Associations of letter pairs and frequency lists
    var letPairList:Vector is readable := vector.withSize(50)

    var gen := random.generator //Random number generator

    // @param first, second: strings to be first and second parts of key
    // @param third word to be inserted in freq list associated with
    // 
    // @post: update frequency of third word following the first two by
    // adding 1. If not yet in table, add with frequency 1.
    method enter(first:String, second:String, third:String)->Void is public{
        var freq:FreqList := WFClasses.newFreqList
        var assoc:Association := 
            association.withKey(WFClasses.newPair(first, second))value(freq)
        var itemNum := letPairList.indexOf(assoc)
        if(itemNum == -1) then{
            freq := WFClasses.newFreqList
            assoc.setValue(freq)
            letPairList.addLast(assoc)}
        else{
            var element:Association := 
                letPairList.at(itemNum)
            freq := element.getValue}
        freq.add(third)
    }
    
    // @param first, second: last two words generated in output text
    // @return new word chosen from those in association list
    // associated with .
    method getNextWord(first:String, second:String)->String is public{
        var assoc:Association := 
            association.withKey(WFClasses.newPair(first, second))
                value(WFClasses.newFreqList)
            var nextString:String
            var itemNum:Number := letPairList.indexOf(assoc)
            if(itemNum == -1) then {return ""}
            else{
                var element:Association := 
                    letPairList.at(itemNum)
                var freq:FreqList := element.getValue
                nextString := freq.get(gen.next(100)/100)
                return nextString}
    }
    
    // @return string representing table in readable form
    method asString->String is public{
        "The table size is {letPairList.size}\n{letPairList}"}
}

//=================================================================

// Main section of program: User opens a text file, program builds
// TextGenerator table from input and then generates words. The first
// two words are the first two of the inpur. Subsequent words are 
// chosen according to the frequency list associated with the two 
// previous words.

var word1:String
var word2:String
var table:TextGenerator := TextGeneratorClass.new
var ws:WordStream := WFClasses.newWordStream

// User chooses file by entering name in terminal
// Opens file and reads content in to a string
var ostream := io.output
var istream := io.input
ostream.write("Enter source file name (including .txt)\n")
var fname := ""
var chr := istream.next
while{chr != "\n"} do {
    fname := fname ++ chr
    chr := istream.next
}
var source := io.open(fname, "r")
var line:String := ""
while{!source.eof} do {line := source.read}

ws.addLexItems(line)
word1 := ws.nextToken
word2 := ws.nextToken

var origFirst:String := word1
var origSecond:String := word2
while{ws.hasMoreTokens} do{
    var word3:String := ws.nextToken
    table.enter(word1, word2, word3)
    word1 := word2
    word2 := word3}

word1 := origFirst
word2 := origSecond
var newText:String := "{origFirst} {origSecond}"
for (2..199) do {wordNo->
    var word3 := table.getNextWord(word1, word2)
    if(word3 == "") then {
        word2 := origFirst
        word3 := origSecond
        newText := newText ++ " {word2}"}
    newText := newText ++ " {word3}"
    if((wordNo % 20) == 0) then{
        newText := newText ++ "\n"}
    word1 := word2
    word2 := word3}

print("Table contains:\n" ++ table)
print("Generated data is:\n" ++ newText)