#lang racket/base

; quotation generator
; Generates a sentence using a markov chain on a list of quotations
; Each quotation is classified by date and author

(require
  racket/string
  "../base-generation/random.rkt"
  "../base-generation/markov.rkt")

(provide
  quotation-author quotation-date quotation-quote
  jsexpr->quotation quotation->jsexpr
  ;
  make-quotation-generator
  jsexpr->quotation-generator
  quotation-generate
  quotation-get-sample
)

; quotation structure
(struct quotation
  (author
   date
   quote))

; Deserialisation of a quotation from a json-compatible expression
(define (jsexpr->quotation js)
  (quotation
    (hash-ref js 'author)
    (hash-ref js 'date)
    (hash-ref js 'quote)))

; Serialisation of a quotation to a json-compatible expression
(define (quotation->jsexpr cit)
  (make-immutable-hash
    `((author . ,(quotation-author cit))
      (date . ,(quotation-date cit))
      (quote . ,(quotation-quote cit)))))

; Generator structure
(struct quotation-generator
  (chains
   samples))

; Constructor for generator from samples
(define (make-quotation-generator samples)
  (define gen (quotation-generator (make-markov 1) samples))
  (for-each
    (lambda (q)
      (markov-add-example! (quotation-generator-chains gen) (string-split (quotation-quote q))))
    samples)
  gen)

; Generator from a jsexpr
(define (jsexpr->quotation-generator js)
  (make-quotation-generator
    (map jsexpr->quotation js)))

; Generate a new quotation
; date, author: fields of the generated quotation structure
; initial: a list of words used as a starting point to generate a quotation, or #f if there is no initial
(define (quotation-generate gen #:date [date #f] #:author [author #f] #:initial [initial #f])
  (quotation
    author
    date
    (string-join
      (markov-generate
        (quotation-generator-chains gen)
        initial)
      " ")))

; Get a random quote from the samples
; date, author: fields to filter when getting the samples, or #f if they are not used for filtering
; Returns #f if there is no quote checking the filters
(define (quotation-get-sample gen #:date [date #f] #:author [author #f])
  (define filtered (filter
                     (lambda (x)
                        (and (or (not date)
                                 (equal? date (quotation-date x)))
                             (or (not author)
                                 (equal? author (quotation-author x)))))
                     (quotation-generator-samples gen)))
  (if (null? filtered)
      #f
      (random:from-list filtered)))
