(.require
 [library
  [lux (.except and)
   [control
    ["[0]" maybe]]
   [data
    ["[0]" text (.only)
     ["%" \\format (.only format)]
     ["[0]" encoding (.only Encoding)]]
    [collection
     ["[0]" list (.use "[1]#[0]" functor)]]]
   [math
    [number
     ["[0]" nat]]]
   [type
    [primitive (.except Frame pattern)]]
   [world
    [net (.only URL)]]]]
 ["[0]" /
  ["[1][0]" selector (.only Selector Combinator Specializer Generic)]
  ["[1][0]" value (.only Value Animation Percentage)]
  ["[1][0]" property (.only Property)]
  ["[1][0]" font (.only Font)]
  ["[1][0]" style]
  ["[1][0]" query (.only Query)]])

(primitive .public Common Any)
(primitive .public Special Any)

(primitive .public (CSS brand)
  Text

  (def .public css
    (-> (CSS Any) Text)
    (|>> representation))

  (def .public empty
    (CSS Any)
    (abstraction ""))

  (type .public Style
    (List (Ex (_ brand) [(Property brand) (Value brand)])))

  (def .public (rule selector style)
    (-> (Selector Any) Style (CSS Common))
    (abstraction (format (/selector.selector selector) "{" (/style.inline (/style.style style)) "}")))

  (def .public char_set
    (-> Encoding (CSS Special))
    (|>> encoding.name
         %.text
         (text.enclosed ["@charset " ";"])
         abstraction))

  (def .public (font font)
    (-> Font (CSS Special))
    (let [with_unicode (case (the /font.#unicode_range font)
                         {.#Some unicode_range}
                         (let [unicode_range' (format "U+" (at nat.hex encoded (the /font.#start unicode_range))
                                                      "-" (at nat.hex encoded (the /font.#end unicode_range)))]
                           (list ["unicode-range" unicode_range']))
                         
                         {.#None}
                         (list))]
      (|> (list.partial ["font-family" (the /font.#family font)]
                        ["src" (format "url(" (the /font.#source font) ")")]
                        ["font-stretch" (|> font (the /font.#stretch) (maybe.else /value.normal_stretch) /value.value)]
                        ["font-style" (|> font (the /font.#style) (maybe.else /value.normal_style) /value.value)]
                        ["font-weight" (|> font (the /font.#weight) (maybe.else /value.normal_weight) /value.value)]
                        with_unicode)
          (list#each (function (_ [property value])
                       (format property ": " value ";")))
          text.together
          (text.enclosed ["{" "}"])
          (format "@font-face")
          abstraction)))

  (def .public (import url query)
    (-> URL (Maybe Query) (CSS Special))
    (abstraction (format (format "@import url(" (%.text url) ")")
                         (case query
                           {.#Some query}
                           (format " " (/query.query query))
                           
                           {.#None}
                           "")
                         ";")))

  (def separator
    text.new_line)

  (type .public Frame
    (Record
     [#when (Value Percentage)
      #what Style]))

  (def .public (key_frames animation frames)
    (-> (Value Animation) (List Frame) (CSS Special))
    (abstraction (format "@keyframes " (/value.value animation) " {"
                         (|> frames
                             (list#each (function (_ frame)
                                          (format (/value.value (the #when frame)) " {"
                                                  (/style.inline (/style.style (the #what frame)))
                                                  "}")))
                             (text.interposed ..separator))
                         "}")))

  (def !composite
    (template (!composite <pre> <post>)
      [(abstraction
        (format (representation <pre>)
                ..separator
                (representation <post>)))]))
  
  (def .public (and pre post)
    (All (_ kind) (-> (CSS kind) (CSS kind) (CSS kind)))
    (!composite pre post))

  (def .public (in_context combinator selector css)
    (-> Combinator (Selector Any) (CSS Common) (CSS Common))
    (|> css
        representation
        (text.all_split_by ..separator)
        (list#each (let [prefix (|> selector
                                    (combinator (/selector.tag ""))
                                    /selector.selector)]
                     (|>> (format prefix))))
        (text.interposed ..separator)
        abstraction))

  (def .public (dependent combinator selector style inner)
    (-> Combinator (Selector Any) Style (CSS Common) (CSS Common))
    (!composite (..rule selector style)
                (..in_context combinator selector inner)))

  (with_template [<name> <combinator>]
    [(def .public <name>
       (-> (Selector Any) Style (CSS Common) (CSS Common))
       (..dependent <combinator>))]

    [with_descendants /selector.in]
    [with_children /selector.sub]
    )

  (def .public (in_case specializer selector css)
    (All (_ kind)
      (-> (Specializer kind) (Selector (Generic Any)) (CSS Common) (CSS Common)))
    (|> css
        representation
        (text.all_split_by ..separator)
        (list#each (let [prefix (|> selector
                                    (specializer (as_expected (/selector.tag "")))
                                    /selector.selector)]
                     (|>> (format prefix))))
        (text.interposed ..separator)
        abstraction))

  (def .public (specialized combinator selector style inner)
    (All (_ kind)
      (-> (Specializer kind) (Selector (Generic Any)) Style (CSS Common) (CSS Common)))
    (!composite (..rule selector style)
                (..in_case combinator selector inner)))

  (with_template [<name> <combinator>]
    [(def .public <name>
       (-> (Selector (Generic Any)) Style (CSS Common) (CSS Common))
       (..specialized <combinator>))]

    [with_case /selector.and]
    [with_part /selector.at]
    [with_element /selector.for]
    )
  )