#lang scheme
(require "patterns.ss")
(require "argument-lists.ss")
(define-struct ruleset
  (name
   (rules #:mutable)
   namespace)
  #:inspector (make-inspector))
(define (rule-print rule port write?)
  (when write? (write-string "<" port))
  (fprintf port "(~a ~a)"
           (rule-name rule)
           (ruleset-name (rule-ruleset rule)))
  (when write? (write-string ">" port)))
(define-values (struct:rule
                make-rule
                rule?
                rule-field-ref
                set-rule-field!)
  (make-struct-type 'rule #f 8 0 #f
                    (list (cons prop:custom-write rule-print))
                    (make-inspector)))
(define rule-name
  (make-struct-field-accessor rule-field-ref 0 'name))
(define set-rule-name!
  (make-struct-field-mutator set-rule-field! 0 'name))
(define rule-ruleset
  (make-struct-field-accessor rule-field-ref 1 'ruleset))
(define set-rule-ruleset!
  (make-struct-field-mutator set-rule-field! 1 'ruleset))
(define rule-goals
  (make-struct-field-accessor rule-field-ref 2 'goals))
(define set-rule-goals!
  (make-struct-field-mutator set-rule-field! 2 'goals))
(define rule-preconditions
  (make-struct-field-accessor rule-field-ref 3 'preconditions))
(define set-rule-preconditions!
  (make-struct-field-mutator set-rule-field! 3 'preconditions))
(define rule-actions
  (make-struct-field-accessor rule-field-ref 4 'actions))
(define set-rule-actions!
  (make-struct-field-mutator set-rule-field! 4 'actions))
(define rule-priority
  (make-struct-field-accessor rule-field-ref 5 'priority))
(define set-rule-priority!
  (make-struct-field-mutator set-rule-field! 5 'priority))
(define rule-order
  (make-struct-field-accessor rule-field-ref 6 'order))
(define set-rule-order!
  (make-struct-field-mutator set-rule-field! 6 'order))
(define rule-specificity
  (make-struct-field-accessor rule-field-ref 7 'specificity))
(define set-rule-specificity!
  (make-struct-field-mutator set-rule-field! 7 'specificity))
(define (add-rule name ruleset goals preconditions actions priority)
  (let ((rule (make-rule name ruleset
                         goals preconditions actions
                         priority
                         (+ (length (ruleset-rules ruleset)) 1)
                         (length preconditions))))
    (set-ruleset-rules! ruleset
                        (append (ruleset-rules ruleset)
                                (list rule)))))
(define-syntax define-ruleset
  (syntax-rules ()
    ((define-ruleset name)
     (define name
       (make-ruleset 'name 
                     '()
                     (current-namespace))))))
(define-syntax define-rule
  (syntax-rules (<== ==>)
    (     (define-rule (name ruleset)
       item ...)
     (define-rule (name ruleset #:priority 0)
       item ...))
    (     (define-rule (name ruleset #:priority priority)
       item)
     (add-rule 'name
               ruleset
               '(item)
               '()
               #f
               priority))
    (     (define-rule (name ruleset #:priority priority)
       item-1 item-2 ...)
     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (item-1)
       item-2 ...))
    (     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (goal ...))
     (add-rule 'name
               ruleset
               '(goal ...)
               '()
               #f
               priority))
    (     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (goal ...)
       <== item)
     (add-rule 'name
               ruleset
               '(goal ...)
               '(item)
               #f
               priority))
    (     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (goal ...)
       <== item-1 item-2 ...)
     (define-rule
       "gather data or actions"
       (name ruleset #:priority priority)
       (goal ...)
       (item-1)
       item-2 ...))
    (     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (data ...)
       ==> item ...)
     (add-rule 'name
               ruleset
               '()
               '(data ...)
               #'(item ...)
               priority))
    (     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (goal-or-data ...)
       item-1 item-2 ...)
     (define-rule
       "gather goals or data"
       (name ruleset #:priority priority)
       (goal-or-data ... item-1)
       item-2 ...))
    (     (define-rule
       "gather data or actions"
       (name ruleset #:priority priority)
       (goal ...)
       (data ...))
     (add-rule 'name
               ruleset
               '(goal ...)
               '(data ...)
               #f
               priority))
    (     (define-rule
       "gather-data-or-actions"
       (name ruleset #:priority priority)
       (goal ...)
       (data ...)
       ==> action ...)
     (add-rule 'name
               ruleset
               '(goal ...)
               '(data ...)
               #'(action ...)
               priority))
    (     (define-rule
       "gather data or actions"
       (name ruleset #:priority priority)
       (goal ...)
       (data-or-action ...)
       item-1 item-2 ...)
     (define-rule
       "gather data or actions"
       (name ruleset #:priority priority)
       (goal ...)
       (data-or-action ... item-1)
       item-2 ...))))
(provide
 define-ruleset
 define-rule)
(provide/contract
 (ruleset?
  (-> any/c boolean?))
 (make-ruleset
  (-> symbol? (listof rule?) any/c ruleset?))
 (ruleset-name
  (-> ruleset? symbol?))
 (ruleset-rules
  (-> ruleset? (listof rule?)))
 (set-ruleset-rules!
  (-> ruleset? (listof rule?) void?))
 (rule?
  (-> any/c boolean?))
 (make-rule
  (-> symbol?
      ruleset?
      list?
      list?
      (or/c syntax? false/c)
      real?
      real?
      real?
      rule?))
 (rule-name
  (-> rule? symbol?))
 (rule-ruleset
  (-> rule? ruleset?))
 (rule-goals
  (-> rule? list?))
 (rule-preconditions
  (-> rule? list?))
 (rule-actions
  (-> rule? (or/c syntax? false/c)))
 (rule-priority
  (-> rule? real?))
 (rule-order
  (-> rule? real?))
 (rule-specificity
  (-> rule? real?))
 (add-rule
  (-> symbol?
      ruleset?
      list?
      list?
      (or/c syntax? false/c)
      real?
      void?)))