|  | import { Entity, POV } from './entity'
import { LogEntry, LogLines, LogLine } from './interface'
export class POVPair<K extends Entity, V extends Entity> {
  run (user: K, target: V): LogEntry {
    const choice = this.options.find(element => element[0][0] === user.perspective && element[0][1] === target.perspective)
    if (choice === undefined) {
      return new LogLine("Fen didn't write any text for this...")
    } else {
      return choice[1](user, target)
    }
  }
  constructor (private options: Array<[[POV, POV], (user: K, target: V) => LogEntry]>) {
  }
}
export class POVPairArgs<K extends Entity, V extends Entity, U> {
  run (user: K, target: V, args: U): LogEntry {
    const choice = this.options.find(element => element[0][0] === user.perspective && element[0][1] === target.perspective)
    if (choice === undefined) {
      return new LogLine("Fen didn't write any text for this...")
    } else {
      return choice[1](user, target, args)
    }
  }
  constructor (private options: Array<[[POV, POV], (user: K, target: V, args: U) => LogEntry]>) {
  }
}
export class POVSolo<K extends Entity> {
  run (user: K): LogEntry {
    const choice = this.options.find(element => element[0][0] === user.perspective)
    if (choice === undefined) {
      return new LogLine("Fen didn't write any text for this...")
    } else {
      return choice[1](user)
    }
  }
  constructor (private options: Array<[[POV], (user: K) => LogEntry]>) {
  }
}
export class POVSoloArgs<K extends Entity, U> {
  run (user: K, args: U): LogEntry {
    const choice = this.options.find(element => element[0][0] === user.perspective)
    if (choice === undefined) {
      return new LogLine("Fen didn't write any text for this...")
    } else {
      return choice[1](user, args)
    }
  }
  constructor (private options: Array<[[POV], (user: K, args: U) => LogEntry]>) {
  }
}
enum NounKind {
    Specific,
    Nonspecific,
    All
}
enum VowelSound {
    Default,
    Vowel,
    NonVowel
}
enum VerbKind {
  Root,
  Singular,
  Present,
  Past,
  PastParticiple
}
export interface Pluralizable {
  isPlural: boolean;
}
interface WordOptions {
    plural: boolean;
    capital: boolean;
    allCaps: boolean;
    proper: boolean;
    nounKind: NounKind;
    verbKind: VerbKind;
    vowel: VowelSound;
    count: boolean;
}
const emptyConfig: WordOptions = {
  allCaps: false,
  capital: false,
  count: false,
  nounKind: NounKind.Specific,
  verbKind: VerbKind.Root,
  plural: false,
  proper: false,
  vowel: VowelSound.Default
}
export type TextLike = { toString: () => string }
// updates as needed
export class LiveText<T> {
  toString (): string {
    return this.run(this.contents).toString()
  }
  constructor (private contents: T, private run: (thing: T) => TextLike) {
  }
}
export class DynText {
  private parts: Array<TextLike>
  constructor (...parts: TextLike[]) {
    this.parts = parts
  }
  toString (): string {
    return (this.parts.map(part => part.toString())).join('')
  }
}
export abstract class Word {
  constructor (public opt: WordOptions = emptyConfig) {
  }
  abstract configure (opts: WordOptions): Word;
  abstract toString (): string;
  // These functions are pure; they don't mutate the original object.
  // This is necessary to avoid causing chaos.
  get allCaps (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.allCaps = true
    return this.configure(opts) as this
  }
  get capital (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.capital = true
    return this.configure(opts) as this
  }
  get plural (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.plural = true
    return this.configure(opts) as this
  }
  get proper (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.proper = true
    return this.configure(opts) as this
  }
  get improper (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.proper = false
    return this.configure(opts) as this
  }
  get specific (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.nounKind = NounKind.Specific
    return this.configure(opts) as this
  }
  get nonspecific (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.nounKind = NounKind.Nonspecific
    return this.configure(opts) as this
  }
  get all (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.nounKind = NounKind.All
    return this.configure(opts) as this
  }
  get uncountable (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.count = false
    return this.configure(opts) as this
  }
  get root (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.verbKind = VerbKind.Root
    return this.configure(opts) as this
  }
  get singular (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.verbKind = VerbKind.Singular
    return this.configure(opts) as this
  }
  get present (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.verbKind = VerbKind.Present
    return this.configure(opts) as this
  }
  get past (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.verbKind = VerbKind.Past
    return this.configure(opts) as this
  }
  get pastParticiple (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.verbKind = VerbKind.PastParticiple
    return this.configure(opts) as this
  }
}
export class RandomWord extends Word {
  private history: { last: number }
  constructor (public choices: Array<Word>, opt: WordOptions = emptyConfig, history: { last: number } = { last: -1 }) {
    super(opt)
    this.history = history
  }
  configure (opts: WordOptions): Word {
    return new RandomWord(this.choices, opts, this.history)
  }
  toString (): string {
    let choice
    do {
      choice = Math.floor(Math.random() * this.choices.length)
    } while (choice === this.history.last)
    this.history.last = choice
    return this.choices[choice].configure(this.opt).toString()
  }
}
export class Noun extends Word {
  constructor (private singularNoun: string, private pluralNoun: string|null = null, private options: WordOptions = emptyConfig) {
    super(options)
  }
  configure (opts: WordOptions): Word {
    return new Noun(this.singularNoun, this.pluralNoun, opts)
  }
  toString (): string {
    let result: string
    if (this.options.plural) {
      if (this.pluralNoun === null) {
        result = this.singularNoun
      } else {
        result = (this.pluralNoun as string)
      }
    } else {
      result = this.singularNoun
    }
    if (!this.options.proper) {
      if (this.options.nounKind === NounKind.Nonspecific && this.options.count) {
        if (this.options.plural) {
          result = 'some ' + result
        } else {
          if (this.options.vowel === VowelSound.Default) {
            if ('aeiouAEIOU'.indexOf(result.slice(0, 1)) >= 0) {
              result = 'an ' + result
            } else {
              result = 'a ' + result
            }
          } else if (this.options.vowel === VowelSound.Vowel) {
            result = 'an ' + result
          } else if (this.options.vowel === VowelSound.NonVowel) {
            result = 'a ' + result
          }
        }
      } else if (this.options.nounKind === NounKind.Specific) {
        result = 'the ' + result
      }
    }
    if (this.options.allCaps) {
      result = result.toUpperCase()
    } else if (this.options.capital) {
      result = result.slice(0, 1).toUpperCase() + result.slice(1)
    }
    return result
  }
}
export class ImproperNoun extends Noun {
  constructor (singularNoun: string, pluralNoun: string = singularNoun) {
    super(singularNoun, pluralNoun, { plural: false, allCaps: false, capital: false, proper: false, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true })
  }
}
export class ProperNoun extends Noun {
  constructor (singularNoun: string) {
    super(singularNoun, null, { plural: false, allCaps: false, capital: false, proper: true, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true })
  }
}
export class Adjective extends Word {
  constructor (private adjective: string, opt: WordOptions = emptyConfig) {
    super(opt)
  }
  configure (opts: WordOptions): Word {
    return new Adjective(this.adjective, opts)
  }
  // TODO caps et al.
  toString (): string {
    return this.adjective
  }
}
export class Verb extends Word {
  configure (opts: WordOptions): Word {
    return new Verb(
      this._root,
      this._singular,
      this._present,
      this._past,
      this._pastParticiple,
      opts
    )
  }
  toString (): string {
    let choice: string
    switch (this.opt.verbKind) {
      case VerbKind.Root: choice = this._root; break
      case VerbKind.Singular: choice = this._singular; break
      case VerbKind.Present: choice = this._present; break
      case VerbKind.Past: choice = this._past; break
      case VerbKind.PastParticiple: choice = this._pastParticiple; break
    }
    if (this.opt.allCaps) {
      choice = choice.toUpperCase()
    } else if (this.opt.capital) {
      choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
    }
    return choice
  }
  constructor (private _root: string, private _singular: string = _root + "s", private _present: string = _root + "ing", private _past: string = _root + "ed", private _pastParticiple: string = _past, public opt: WordOptions = emptyConfig) {
    super(opt)
  }
}
interface PronounDict {
    subjective: string;
    objective: string;
    possessive: string;
    reflexive: string;
}
export class Pronoun implements Pluralizable {
  constructor (private pronouns: PronounDict, private capitalize: boolean = false, public isPlural: boolean = false) {
  }
  get capital (): Pronoun {
    return new Pronoun(this.pronouns, true)
  }
  get subjective (): string {
    return this.caps(this.pronouns.subjective)
  }
  get objective (): string {
    return this.caps(this.pronouns.objective)
  }
  get possessive (): string {
    return this.caps(this.pronouns.possessive)
  }
  get reflexive (): string {
    return this.caps(this.pronouns.reflexive)
  }
  private caps (input: string): string {
    if (this.capitalize) {
      return input.slice(0, 1).toUpperCase() + input.slice(1)
    } else {
      return input
    }
  }
}
export const MalePronouns = new Pronoun({
  subjective: 'he',
  objective: 'him',
  possessive: 'his',
  reflexive: 'himself'
})
export const FemalePronouns = new Pronoun({
  subjective: 'she',
  objective: 'her',
  possessive: 'her',
  reflexive: 'herself'
})
export const TheyPronouns = new Pronoun({
  subjective: 'they',
  objective: 'them',
  possessive: 'their',
  reflexive: 'themself'
}, false, true)
export const TheyPluralPronouns = new Pronoun({
  subjective: 'they',
  objective: 'them',
  possessive: 'their',
  reflexive: 'themselves'
}, false, true)
export const ObjectPronouns = new Pronoun({
  subjective: 'it',
  objective: 'it',
  possessive: 'its',
  reflexive: 'itself'
})
 |