import cat_factory // can access top-level methods in cat_factory.grace
import vector

type Cat = {
    getName->String
    color->String
    changeNameTo(_:String)->String
    speak->Void
    asString->String}

type Cheetah = Cat & {
    speed->Number
    race(_:Cat)->Cat}

type Iterator = {
    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}

// searches for cat named name in list and returns matching cat
// if it exists
method find (name:String) in (list:Vector)->Dynamic{
    for(1..list.size) do {i->
        if(list.at(i).getName == name) then{
            return list.at(i)}
    }
}

// a vector that accepts objects of type Cat
var myCats:Vector := vector.withSize 10

def myFirstCat = cat_factory.catOfColor "calico" named "Ivan"
def mySecondCat = cat_factory.catOfColor "black"
def myThirdCat = cat_factory.cat

myThirdCat.changeNameTo "Missy"

myCats.addFirst(myFirstCat)

myCats.addLast(myThirdCat)

myCats.add(mySecondCat)at(2)

myCats.at(2).changeNameTo "Macavity"

myCats.addLast(cat_factory.cheetah)

myCats.addLast(cat_factory.cheetahNamed "Alex")

myCats.addLast(cat_factory.cheetahNamed "Sebastian" withSpeed 74.5)

// makes each cat in the vector use speak method
myCats.forEachDo{thisCat->thisCat.speak}

print "\n"

def champion = cat_factory.cheetahNamed "Champion" withSpeed 73

// prints result of races between each cat in the vector and Champion
myCats.forEachDo{thisCat->
    var s:String :=  "Champion vs {thisCat.getName}: "
    s := s ++ "{champion.race(thisCat).getName} won"
    print(s)}

print "\n"

// if Kitty is a cheetah, will change name to Edith
// if Kitty is a cat but not a cheetah, will change name to Ellen
// if there's no cat named Kitty in the vector, will print message
match(find "Kitty" in(myCats))
    case{found:Cheetah-> found.changeNameTo "Edith"}
    case{found:Cat-> found.changeNameTo "Ellen"}
    case{other-> print "You don't own a cat named Kitty"}

// prints out each cat in the vector
var catIter:Iterator := myCats.iterator
print "My cats are: "
while{catIter.hasNext} do {print(catIter.next)}