|  | import { LogEntry } from "@/game/interface"
import { parseTwoDigitYear } from "moment"
export enum POV {
  First,
  Second,
  Third
}
export type SoloLine<T> = (user: T) => LogEntry
export type SoloLineArgs<T, V> = (user: T, args: V) => LogEntry
export type PairLine<T> = (user: T, target: T) => LogEntry
export type PairLineArgs<T, V> = (user: T, target: T, args: V) => LogEntry
export type GroupLine<T> = (user: T, targets: Array<T>) => 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;
  possessive: boolean;
  objective: boolean;
  perspective: POV;
}
const emptyConfig: WordOptions = {
  allCaps: false,
  capital: false,
  count: false,
  nounKind: NounKind.Specific,
  verbKind: VerbKind.Root,
  plural: false,
  proper: false,
  vowel: VowelSound.Default,
  possessive: false,
  objective: false,
  perspective: POV.Third
}
export type TextLike = { toString: () => string }
// updates as needed
export class LiveText<T> {
  constructor (private contents: T, private run: (thing: T) => TextLike) {}
  toString (): string {
    return this.run(this.contents).toString()
  }
}
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
  }
  get possessive (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.possessive = true
    return this.configure(opts) as this
  }
  get objective (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.objective = true
    return this.configure(opts) as this
  }
  get pov1 (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.perspective = POV.First
    return this.configure(opts) as this
  }
  get pov2 (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.perspective = POV.Second
    return this.configure(opts) as this
  }
  get pov3 (): this {
    const opts: WordOptions = Object.assign({}, this.opt)
    opts.perspective = POV.Third
    return this.configure(opts) as this
  }
}
export class OptionalWord extends Word {
  constructor (public word: Word, private chance = 0.5, private suffix = " ") {
    super(word.opt)
  }
  configure (opts: WordOptions): Word {
    return new OptionalWord(this.word.configure(opts), this.chance, this.suffix)
  }
  toString (): string {
    if (Math.random() < this.chance) {
      return this.word + this.suffix
    } else {
      return ""
    }
  }
}
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.choices.length > 1)
    this.history.last = choice
    return this.choices[choice].configure(this.opt).toString()
  }
}
export class Noun extends Word {
  constructor (
    protected singularNoun: string,
    protected pluralNoun: string | null = null,
    protected possessiveNoun: string | null = null,
    protected options: WordOptions = emptyConfig
  ) {
    super(options)
  }
  configure (opts: WordOptions): Word {
    return new Noun(
      this.singularNoun,
      this.pluralNoun,
      this.possessiveNoun,
      opts
    )
  }
  toString (): string {
    let result: string
    // TODO: plural possessive nouns?
    if (this.options.possessive) {
      if (this.possessiveNoun === null) {
        result = this.singularNoun + "'s"
      } else {
        result = this.possessiveNoun
      }
    } else 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
  }
  conjugate (verb: Word): Word {
    if (this.opt.plural) {
      return verb.root
    } else {
      return verb.singular
    }
  }
}
export class ImproperNoun extends Noun {
  constructor (singularNoun: string, pluralNoun: string = singularNoun) {
    super(singularNoun, pluralNoun, null, {
      plural: false,
      allCaps: false,
      capital: false,
      proper: false,
      nounKind: NounKind.Specific,
      verbKind: VerbKind.Root,
      vowel: VowelSound.Default,
      count: true,
      possessive: false,
      objective: false,
      perspective: POV.Third
    })
  }
}
export class ProperNoun extends Noun {
  constructor (singularNoun: string) {
    super(singularNoun, null, null, {
      plural: false,
      allCaps: false,
      capital: false,
      proper: true,
      nounKind: NounKind.Specific,
      verbKind: VerbKind.Root,
      vowel: VowelSound.Default,
      count: true,
      possessive: false,
      objective: false,
      perspective: POV.Third
    })
  }
}
export class Adjective extends Word {
  constructor (private adjective: string, opt: WordOptions = emptyConfig) {
    super(opt)
  }
  configure (opts: WordOptions): Word {
    return new Adjective(this.adjective, opts)
  }
  toString (): string {
    let word = this.adjective
    if (this.opt.allCaps) {
      word = word.toUpperCase()
    } else if (this.opt.capital) {
      word = word.slice(0, 1).toUpperCase() + word.slice(1)
    }
    return word
  }
}
export class Adverb extends Word {
  constructor (private adverb: string, opt: WordOptions = emptyConfig) {
    super(opt)
  }
  configure (opt: WordOptions): Word {
    return new Adverb(this.adverb, opt)
  }
  toString (): string {
    return this.adverb
  }
}
/**
 * root: break
 * singular: breaks
 * present: breaking
 * past: broken
 */
export class Verb extends Word {
  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)
  }
  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
  }
}
export class Preposition extends Word {
  constructor (private word: string, public opt: WordOptions = emptyConfig) {
    super(opt)
  }
  configure (opts: WordOptions): Word {
    return new Preposition(this.word, opts)
  }
  toString (): string {
    if (this.opt.capital) {
      return this.word.slice(0, 1).toUpperCase() + this.word.slice(1)
    } else {
      return this.word
    }
  }
}
// this one is obnoxious
export class ToBe extends Word {
  constructor (protected opts: WordOptions = emptyConfig) {
    super(opts)
  }
  configure (opts: WordOptions): Word {
    return new ToBe(opts)
  }
  toString (): string {
    let choice
    if (this.opts.plural) {
      choice = "are"
    }
    switch (this.opts.perspective) {
      case POV.First:
        choice = "am"
        break
      case POV.Second:
        choice = "are"
        break
      case POV.Third:
        choice = "is"
        break
    }
    if (this.opt.allCaps) {
      choice = choice.toUpperCase()
    } else if (this.opt.capital) {
      choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
    }
    return choice
  }
}
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)
  }
  conjugate (verb: Word): Word {
    if (this.isPlural) {
      return verb.root
    } else {
      return verb.singular
    }
  }
  private caps (input: string): string {
    if (this.capitalize) {
      return input.slice(0, 1).toUpperCase() + input.slice(1)
    } else {
      return input
    }
  }
}
export type OnomatopoeiaPart = [string, number, number]
export class Onomatopoeia extends Word {
  constructor (protected parts: Array<OnomatopoeiaPart>, public opts: WordOptions = emptyConfig) {
    super(opts)
  }
  configure (opts: WordOptions): Word {
    return new Onomatopoeia(this.parts, opts)
  }
  toString (): string {
    const built = this.parts.reduce((result, next) => {
      const [piece, min, max] = next
      const count = Math.floor(Math.random() * (max - min)) + min
      for (let i = 0; i < count; i++) {
        result += piece
      }
      return result
    }, "")
    return built
  }
}
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"
})
export const SecondPersonPronouns = new Pronoun(
  {
    subjective: "you",
    objective: "you",
    possessive: "your",
    reflexive: "yourself"
  },
  false,
  true
)
export const FirstPersonPronouns = new Pronoun(
  {
    subjective: "I",
    objective: "me",
    possessive: "my",
    reflexive: "myself"
  },
  false,
  true
)
export class PronounAsNoun extends Noun {
  constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) {
    super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt)
    this.options.nounKind = NounKind.All
    this.options.plural = true
  }
  configure (opts: WordOptions): Word {
    return new PronounAsNoun(this.pronouns, opts)
  }
  toString (): string {
    if (this.options.objective) {
      return new Noun(
        this.pronouns.objective,
        this.pronouns.objective,
        this.pronouns.possessive,
        this.options
      ).toString()
    } else {
      return super.toString()
    }
  }
  conjugate (verb: Word): Word {
    if (this.pronouns === FirstPersonPronouns) {
      return super.conjugate(verb.pov1)
    } else if (this.pronouns === SecondPersonPronouns) {
      return super.conjugate(verb.pov2)
    } else {
      return super.conjugate(verb.pov3)
    }
  }
}
 |