(.require
 [library
  [lux (.except)
   ["$" documentation]
   [data
    ["[0]" text (.only \n)
     ["%" \\format (.only format)]]]
   [meta
    [macro
     ["[0]" template]]]]]
 [\\library
  ["[0]" /]])

(`` (def .public documentation
      (List $.Documentation)
      (list ($.module /._
                      "")

            ($.definition /.Privacy)
            ($.definition /.State)
            ($.definition /.Inheritance)
            ($.definition /.class_names_cannot_contain_periods)
            ($.definition /.class_name_cannot_be_a_type_variable)
            ($.definition /.cannot_convert_to_jvm_type)
            ($.definition /.cannot_cast_to_non_object)

            (,, (with_template [<name>]
                  [(`` ($.definition <name>
                         (format "The type of a (boxed) " (,, (template.text [<name>])) " object.")))]

                  [/.Boolean]
                  [/.Byte]
                  [/.Short]
                  [/.Integer]
                  [/.Long]
                  [/.Float]
                  [/.Double]
                  [/.Character]
                  ))

            (,, (with_template [<name>]
                  [(`` ($.definition <name>
                         (format "The type of an (unboxed) " (,, (template.text [<name>])) " value.")))]

                  [/.boolean]
                  [/.byte]
                  [/.short]
                  [/.int]
                  [/.long]
                  [/.float]
                  [/.double]
                  [/.char]
                  ))

            (,, (with_template [<name> <from> <to>]
                  [(`` ($.definition <name>
                         "Type converter."))]

                  [/.byte_to_long    Byte      Long]

                  [/.short_to_long   Short     Long]
                  
                  [/.double_to_int   Double    Integer]
                  [/.double_to_long  Double    Long]
                  [/.double_to_float Double    Float]

                  [/.float_to_int    Float     Integer]
                  [/.float_to_long   Float     Long]
                  [/.float_to_double Float     Double]
                  
                  [/.int_to_byte     Integer   Byte]
                  [/.int_to_short    Integer   Short]
                  [/.int_to_long     Integer   Long]
                  [/.int_to_float    Integer   Float]
                  [/.int_to_double   Integer   Double]
                  [/.int_to_char     Integer   Character]

                  [/.long_to_byte    Long      Byte]
                  [/.long_to_short   Long      Short]
                  [/.long_to_int     Long      Integer]
                  [/.long_to_float   Long      Float]
                  [/.long_to_double  Long      Double]

                  [/.char_to_byte    Character Byte]
                  [/.char_to_short   Character Short]
                  [/.char_to_int     Character Integer]
                  [/.char_to_long    Character Long]

                  [/.long_to_char    Long      Character]
                  [/.byte_to_int     Byte      Integer]
                  [/.short_to_int    Short     Integer]
                  [/.byte_to_char    Byte      Character]
                  [/.short_to_char   Short     Character]
                  ))

            ($.definition /.class
              "Allows defining JVM classes in Lux code."
              [(class "final" (TestClass A) [Runnable]
                 ... Fields
                 ("private" foo boolean)
                 ("private" bar A)
                 ("private" baz java/lang/Object)
                 ... Methods
                 ("public" [] (new [value A]) []
                  (exec
                    (:= ::foo true)
                    (:= ::bar value)
                    (:= ::baz "")
                    []))
                 ("public" (virtual) java/lang/Object
                  "")
                 ("public" "static" (static) java/lang/Object
                  "")
                 (Runnable [] (run) void
                           [])
                 )

               "The tuple corresponds to parent interfaces."
               "An optional super-class can be specified before the tuple. If not specified, java.lang.Object will be assumed."
               "Fields and methods defined in the class can be used with special syntax."
               "For example:"
               "::resolved, for accessing the 'resolved' field."
               "(:= ::resolved true) for modifying it."
               "(::new! []) for calling the class's constructor."
               "(::resolve! container [value]) for calling the 'resolve' method."])

            ($.definition /.interface
              "Allows defining JVM interfaces."
              [(interface TestInterface
                 ([] foo [boolean String] void "throws" [Exception]))])

            ($.definition /.object
              "Allows defining anonymous classes."
              ["The 1st tuple corresponds to class-level type-variables."
               "The 2nd tuple corresponds to parent interfaces."
               "The 3rd tuple corresponds to arguments to the super class constructor."
               "An optional super-class can be specified before the 1st tuple. If not specified, java.lang.Object will be assumed."
               (object [] [Runnable]
                 []
                 (Runnable [] (run self) void
                           (exec
                             (do_something some_value)
                             [])))])

            ($.definition /.null
              "The null pointer."
              [(null)])

            ($.definition /.null?
              "Test for the null pointer."
              [(= true
                  (null? (null)))]
              [(= false
                  (null? "YOLO"))])

            ($.definition /.???
              "Takes a (potentially null) ObjectType reference and creates a (Maybe ObjectType) for it."
              [(= (??? (is java/lang/String (null)))
                  {.#None})]
              [(= (??? "YOLO")
                  {.#Some "YOLO"})])

            ($.definition /.!!!
              (format "Takes a (Maybe ObjectType) and returns a ObjectType."
                      \n "A .#None would get translated into a (null).")
              [(= (null)
                  (!!! (??? (is java/lang/Thread (null)))))]
              [(= "foo"
                  (!!! (??? "foo")))])

            ($.definition /.as
              (format "Checks whether an object is an instance of a particular class."
                      \n "Caveat emptor: Cannot check for polymorphism, so avoid using parameterized classes.")
              [(when (as String "YOLO")
                 {.#Some value_as_string}
                 {.#None})])

            ($.definition /.synchronized
              "Evaluates body, while holding a lock on a given object."
              [(synchronized object_to_be_locked
                 (exec
                   (do something)
                   (dosomething else)
                   (finish the computation)))])

            ($.definition /.to
              "Call a variety of methods on an object. Then, return the object."
              [(to object
                   (ClassName::method1 arg0 arg1 arg2)
                   (ClassName::method2 arg3 arg4 arg5))])

            ($.definition /.import
              "Allows importing JVM classes, and using them as types."
              ["Their methods, fields and enum options can also be imported."
               (import java/lang/Object
                 "[1]::[0]"
                 (new [])
                 (equals [java/lang/Object] boolean)
                 (wait [int] "io" "try" void))]
              ["Special options can also be given for the return values."
               "'?' means that the values will be returned inside a Maybe type. That way, null becomes .#None."
               "'try' means that the computation might throw an exception, and the return value will be wrapped by the Try type."
               "'io' means the computation has side effects, and will be wrapped by the IO type."
               "These options must show up in the following order ['io' 'try' '?'] (although, each option can be used independently)."
               (import java/lang/String
                 "[1]::[0]"
                 (new [[byte]])
                 ("static" valueOf [char] java/lang/String)
                 ("static" valueOf "as" int_valueOf [int] java/lang/String))

               (import (java/util/List e)
                 "[1]::[0]"
                 (size [] int)
                 (get [int] e))

               (import (java/util/ArrayList a)
                 "[1]::[0]"
                 ([T] toArray [[T]] [T]))]
              ["The class-type that is generated is of the fully-qualified name."
               "This avoids a clash between the java.util.List type, and Lux's own List type."
               "All enum options to be imported must be specified."
               (import java/lang/Character$UnicodeScript
                 "[1]::[0]"
                 ("enum" ARABIC CYRILLIC LATIN))]
              ["It should also be noted, the only types that may show up in method arguments or return values may be Java classes, arrays, primitives, void or type-vars."
               "Lux types, such as Maybe cannot be named (otherwise, they'd be confused for Java classes)."
               (import (lux/concurrency/async/JvmAsync A)
                 "[1]::[0]"
                 (resolve [A] boolean)
                 (poll [] A)
                 (wasResolved [] boolean)
                 (waitOn [lux/Function] void)
                 ("static" [A] make [A] (lux/concurrency/async/JvmAsync A)))]
              ["Also, the names of the imported members will look like Class::member"
               (java/lang/Object::new [])
               (java/lang/Object::equals [other_object] my_object)
               (java/util/List::size [] my_list)
               java/lang/Character$UnicodeScript::LATIN])

            ($.definition /.array
              "Create an array of the given type, with the given size."
              [(array java/lang/Object 10)])

            ($.definition /.length
              "Gives the length of an array."
              [(length my_array)])

            ($.definition /.read!
              "Loads an element from an array."
              [(read! 10 my_array)])

            ($.definition /.write!
              "Stores an element into an array."
              [(write! 10 my_object my_array)])

            ($.definition /.class_for
              "Loads the class as a java.lang.Class object."
              [(class_for java/lang/String)])

            ($.definition /.type
              ""
              [(is Type
                   (type java/lang/String))])

            ($.definition /.is
              ""
              [(is java/lang/Object
                   (is java/lang/String
                       ???))])
            )))
