(ns nal.deriver.list-expansion
  (:require [nal.deriver.utils :refer [walk]]
            [clojure.string :as s]))
(def max-elements-in-list 7)

Finds the first :list element in the rule.

(defn get-list
  [prefix statement]
  (cond
    (and (keyword? statement) (s/starts-with? (str statement) prefix))
    statement
    (coll? statement) (some identity (map #(get-list prefix %) statement))
    :default nil))

Generates n symbols with prefix.

(defn gen-symbols
  [prefix n]
  (map #(symbol (str prefix (inc %))) (range n)))

Replaces :list/ with symbols.

(defn replace-list-elemets
  [statement l list-name n]
  (walk statement
    (and (coll? el) (some #{l} el))
    (mapcat (fn [e] (if (= l e)
                   (concat '() (gen-symbols list-name n))
                   (list e))) el)))

Fetches name of the list.

(defn list-name
  [l]
  (->> l str (drop 6) s/join))
(defn expand-:from-element
  "Expands :from/ element in rule, so as a result will be created n rules, in
  each of them :form/A will be replaced with A1 or A2 or A3, etc."
  [statement from-name list-name n]
  (map (fn [idx]
         (walk statement
           (= from-name el) (symbol (str list-name idx))))
       (range 1 (inc n))))

Expands rules with :list/ elements, as a result will be created 5 rules, where :list/ will be replaced with A1, A1 A2, ..., A1..A5.

(defn generate-all-lists
  [r]
  (let [list (get-list ":list" r)
        l-name (list-name list)]
    (mapcat #(let [st (replace-list-elemets r list l-name %)]
              (if-let [from-name (get-list ":from" st)]
                (expand-:from-element st from-name l-name %)
                [st]))
            (range 1 (inc max-elements-in-list)))))

Checks if rule contains any :list element.

(defn contains-list?
  [r]
  (get-list ":list" r))