(.module:
  [library
   [lux {"-" Module Definition}
    ["[0]" ffi {"+" import: do_to object}]
    [abstract
     [monad {"+" do}]]
    [control
     pipe
     ["[0]" maybe]
     ["[0]" try {"+" Try}]
     ["[0]" exception {"+" exception:}]
     ["[0]" io {"+" IO io}]
     [concurrency
      ["[0]" atom {"+" Atom atom}]]]
    [data
     [binary {"+" Binary}]
     ["[0]" product]
     ["[0]" text ("[1]@[0]" hash)
      ["%" format {"+" format}]]
     [collection
      ["[0]" array]
      ["[0]" dictionary {"+" Dictionary}]]]
    [target
     [jvm
      ["[0]" loader {"+" Library}]
      ["[0]" type
       ["[0]" descriptor]]]]
    [tool
     [compiler
      [language
       [lux
        ["[0]" version]
        ["[0]" generation]]]
      [meta
       [io {"+" lux_context}]
       [archive
        [descriptor {"+" Module}]
        ["[0]" artifact]]]]]]]
  [///
   [host
    ["[0]" jvm {"+" Inst Definition Host State}
     ["[0]" def]
     ["[0]" inst]]]]
  )

(import: java/lang/reflect/Field
  ["[1]::[0]"
   (get ["?" java/lang/Object] "try" "?" java/lang/Object)])

(import: (java/lang/Class a)
  ["[1]::[0]"
   (getField [java/lang/String] "try" java/lang/reflect/Field)])

(import: java/lang/Object
  ["[1]::[0]"
   (getClass [] (java/lang/Class java/lang/Object))])

(import: java/lang/ClassLoader)

(type: .public ByteCode Binary)

(def: .public value_field Text "_value")
(def: .public $Value (type.class "java.lang.Object" (list)))

(exception: .public (cannot_load [class Text
                                  error Text])
  (exception.report
   ["Class" class]
   ["Error" error]))

(exception: .public (invalid_field [class Text
                                    field Text
                                    error Text])
  (exception.report
   ["Class" class]
   ["Field" field]
   ["Error" error]))

(exception: .public (invalid_value [class Text])
  (exception.report
   ["Class" class]))

(def: (class_value class_name class)
  (-> Text (java/lang/Class java/lang/Object) (Try Any))
  (case (java/lang/Class::getField ..value_field class)
    {try.#Success field}
    (case (java/lang/reflect/Field::get {.#None} field)
      {try.#Success ?value}
      (case ?value
        {.#Some value}
        {try.#Success value}
        
        {.#None}
        (exception.except ..invalid_value class_name))
      
      {try.#Failure error}
      (exception.except ..cannot_load [class_name error]))
    
    {try.#Failure error}
    (exception.except ..invalid_field [class_name ..value_field error])))

(def: class_path_separator ".")

(def: .public bytecode_name
  (-> Text Text)
  (text.replaced ..class_path_separator .module_separator))

(def: .public (class_name [module_id artifact_id])
  (-> generation.Context Text)
  (format lux_context
          ..class_path_separator (%.nat version.version)
          ..class_path_separator (%.nat module_id)
          ..class_path_separator (%.nat artifact_id)))

(def: (evaluate! library loader context valueI)
  (-> Library java/lang/ClassLoader generation.Context Inst (Try [Any Definition]))
  (let [eval_class (..class_name context)
        bytecode_name (..bytecode_name eval_class)
        bytecode (def.class {jvm.#V1_6}
                            {jvm.#Public} jvm.noneC
                            bytecode_name
                            (list) $Value
                            (list)
                            (|>> (def.field {jvm.#Public} ($_ jvm.++F jvm.finalF jvm.staticF)
                                            ..value_field ..$Value)
                                 (def.method {jvm.#Public} ($_ jvm.++M jvm.staticM jvm.strictM)
                                             "<clinit>"
                                             (type.method [(list) (list) type.void (list)])
                                             (|>> valueI
                                                  (inst.PUTSTATIC (type.class bytecode_name (list)) ..value_field ..$Value)
                                                  inst.RETURN))))]
    (io.run! (do (try.with io.monad)
               [_ (loader.store eval_class bytecode library)
                class (loader.load eval_class loader)
                value (# io.monad in (..class_value eval_class class))]
               (in [value
                    [eval_class bytecode]])))))

(def: (execute! library loader [class_name class_bytecode])
  (-> Library java/lang/ClassLoader Definition (Try Any))
  (io.run! (do (try.with io.monad)
             [existing_class? (|> (atom.read! library)
                                  (# io.monad each (function (_ library)
                                                     (dictionary.key? library class_name)))
                                  (try.lifted io.monad)
                                  (: (IO (Try Bit))))
              _ (if existing_class?
                  (in [])
                  (loader.store class_name class_bytecode library))]
             (loader.load class_name loader))))

(def: (define! library loader context custom valueI)
  (-> Library java/lang/ClassLoader generation.Context (Maybe Text) Inst (Try [Text Any Definition]))
  (do try.monad
    [[value definition] (evaluate! library loader context valueI)]
    (in [(maybe.else (..class_name context)
                     custom)
         value definition])))

(def: .public host
  (IO [java/lang/ClassLoader Host])
  (io (let [library (loader.new_library [])
            loader (loader.memory library)]
        [loader
         (: Host
            (implementation
             (def: (evaluate context valueI)
               (# try.monad each product.left
                  (..evaluate! library loader context valueI)))
             
             (def: execute
               (..execute! library loader))
             
             (def: define
               (..define! library loader))

             (def: (ingest context bytecode)
               [(..class_name context) bytecode])

             (def: (re_learn context custom [_ bytecode])
               (io.run!
                (loader.store (maybe.else (..class_name context) custom) bytecode library)))
             
             (def: (re_load context custom [directive_name bytecode])
               (io.run!
                (do (try.with io.monad)
                  [.let [class_name (maybe.else (..class_name context)
                                                custom)]
                   _ (loader.store class_name bytecode library)
                   class (loader.load class_name loader)]
                  (# io.monad in (..class_value class_name class)))))))])))

(def: .public $Variant
  (type.array ..$Value))

(def: .public $Tuple
  (type.array ..$Value))

(def: .public $Runtime
  (type.class (..class_name [0 0]) (list)))

(def: .public $Function
  (type.class (..class_name [0 1]) (list)))