(.using
 [library
  [lux {"-" Module type}
   [abstract
    [monad {"+" do}]]
   [control
    ["[0]" maybe]
    ["<>" parser
     ["<[0]>" code {"+" Parser}]]]
   [data
    ["[0]" text]
    [collection
     ["[0]" set {"+" Set}]
     ["[0]" dictionary {"+" Dictionary}
      ["[0]" plist {"+" PList}]]]]
   [tool
    [compiler
     [meta
      [cli
       [compiler {"+" Compiler}]]
      [archive
       [module
        [descriptor {"+" Module}]]]]]]
   [world
    [net {"+" URL}]]]]
 ["[0]" // "_"
  ["/" profile]
  ["[1][0]" runtime {"+" Runtime}]
  ["[1][0]" project {"+" Project}]
  ["[1][0]" dependency]
  ["[1][0]" format]
  ["[1][0]" repository "_"
   ["[1]" remote]]
  ["[1][0]" artifact {"+" Artifact}
   ["[1]/[0]" type]]])

(def: (singular input tag parser)
  (All (_ a) (-> (Dictionary Text Code) Text (Parser a) (Parser a)))
  (<code>.locally (maybe.list (dictionary.value tag input))
                  parser))

(def: (plural input tag parser)
  (All (_ a) (-> (Dictionary Text Code) Text (Parser a) (Parser (List a))))
  (<code>.locally (maybe.list (dictionary.value tag input))
                  (<code>.tuple (<>.some parser))))

(def: group
  (Parser //artifact.Group)
  <code>.text)

(def: name
  (Parser //artifact.Name)
  <code>.text)

(def: version
  (Parser //artifact.Version)
  <code>.text)

(def: artifact'
  (Parser //artifact.Artifact)
  ($_ <>.and ..group ..name ..version))

(def: artifact
  (Parser //artifact.Artifact)
  (<code>.tuple ..artifact'))

(def: url
  (Parser URL)
  <code>.text)

(def: scm
  (Parser /.SCM)
  ..url)

(def: description
  (Parser Text)
  <code>.text)

(def: license
  (Parser /.License)
  (do [! <>.monad]
    [input (# ! each
              (dictionary.of_list text.hash)
              (<code>.tuple (<>.some (<>.and <code>.text
                                             <code>.any))))]
    ($_ <>.and
        (..singular input "name" ..name)
        (..singular input "url" ..url)
        (<>.else {/.#Repo}
                 (..singular input "type"
                             (<>.or (<code>.this (' "repo"))
                                    (<code>.this (' "manual"))))))))

(def: organization
  (Parser /.Organization)
  (do [! <>.monad]
    [input (# ! each
              (dictionary.of_list text.hash)
              (<code>.tuple (<>.some (<>.and <code>.text
                                             <code>.any))))]
    ($_ <>.and
        (..singular input "name" ..name)
        (..singular input "url" ..url))))

(def: developer
  (Parser /.Developer)
  (do [! <>.monad]
    [input (# ! each
              (dictionary.of_list text.hash)
              (<code>.tuple (<>.some (<>.and <code>.text
                                             <code>.any))))]
    ($_ <>.and
        (..singular input "name" ..name)
        (..singular input "url" ..url)
        (<>.maybe (..singular input "organization" ..organization))
        )))

(def: contributor
  (Parser /.Contributor)
  ..developer)

(def: info
  (Parser /.Info)
  (do [! <>.monad]
    [input (# ! each
              (dictionary.of_list text.hash)
              (<code>.tuple (<>.some (<>.and <code>.text
                                             <code>.any))))]
    ($_ <>.and
        (<>.maybe (..singular input "url" ..url))
        (<>.maybe (..singular input "scm" ..scm))
        (<>.maybe (..singular input "description" ..description))
        (<>.else (list) (..plural input "licenses" ..license))
        (<>.maybe (..singular input "organization" ..organization))
        (<>.else (list) (..plural input "developers" ..developer))
        (<>.else (list) (..plural input "contributors" ..contributor))
        )))

(def: repository
  (Parser //repository.Address)
  ..url)

(def: type
  (Parser //artifact/type.Type)
  <code>.text)

(def: dependency
  (Parser //dependency.Dependency)
  (<code>.tuple
   ($_ <>.and
       ..artifact'
       (<>.else //artifact/type.lux_library ..type)
       )))

(def: compiler
  (Parser Compiler)
  (<code>.tuple
   ($_ <>.and
       <code>.global
       (<>.some <code>.text)
       )))

(def: source
  (Parser /.Source)
  <code>.text)

(def: target
  (Parser /.Target)
  <code>.text)

(def: module
  (Parser Module)
  <code>.text)

(def: deploy_repository
  (Parser [Text //repository.Address])
  (<>.and <code>.text
          ..repository))

(def: configuration/1
  (Parser [Text Text])
  (<>.and <code>.text
          <code>.text))

(def: runtime
  (Parser Runtime)
  (<code>.tuple (<>.and <code>.text
                        (<>.some <code>.text))))

(def: profile
  (Parser /.Profile)
  (do [! <>.monad]
    [input (# ! each
              (dictionary.of_list text.hash)
              (<code>.tuple (<>.some (<>.and <code>.text
                                             <code>.any))))
     .let [^parents (is (Parser (List /.Name))
                        (<>.else (list)
                                 (..plural input "parents" <code>.text)))
           ^identity (is (Parser (Maybe Artifact))
                         (<>.maybe
                          (..singular input "identity" ..artifact)))
           ^info (is (Parser (Maybe /.Info))
                     (<>.maybe
                      (..singular input "info" ..info)))
           ^repositories (is (Parser (Set //repository.Address))
                             (|> (..plural input "repositories" ..repository)
                                 (# ! each (set.of_list text.hash))
                                 (<>.else (set.empty text.hash))
                                 (# ! each (set.has /.default_repository))))
           ^dependencies (is (Parser (Set //dependency.Dependency))
                             (|> (..plural input "dependencies" ..dependency)
                                 (# ! each (set.of_list //dependency.hash))
                                 (<>.else (set.empty //dependency.hash))))
           ^lux (|> ..dependency
                    (..singular input //format.lux_compiler_label)
                    (<>.else /.default_compiler))
           ^compilers (|> ..compiler
                          (..plural input "compilers")
                          (<>.else (list)))
           ^sources (is (Parser (Set /.Source))
                        (|> (..plural input "sources" ..source)
                            (# ! each (set.of_list text.hash))
                            (<>.else (set.of_list text.hash (list /.default_source)))))
           ^target (is (Parser /.Target)
                       (|> ..target
                           (..singular input "target")
                           (<>.else /.default_target)))
           ^program (is (Parser (Maybe Module))
                        (<>.maybe
                         (..singular input "program" ..module)))
           ^test (is (Parser (Maybe Module))
                     (<>.maybe
                      (..singular input "test" ..module)))
           ^deploy_repositories (is (Parser (Dictionary Text //repository.Address))
                                    (<| (# ! each (dictionary.of_list text.hash))
                                        (<>.else (list))
                                        (..plural input "deploy_repositories" ..deploy_repository)))
           ^configuration (is (Parser (PList Text))
                              (<| (<>.else (list))
                                  (..plural input "configuration" ..configuration/1)))
           ^java (|> ..runtime
                     (..singular input "java")
                     (<>.else //runtime.default_java))
           ^js (|> ..runtime
                   (..singular input "js")
                   (<>.else //runtime.default_js))
           ^python (|> ..runtime
                       (..singular input "python")
                       (<>.else //runtime.default_python))
           ^lua (|> ..runtime
                    (..singular input "lua")
                    (<>.else //runtime.default_lua))
           ^ruby (|> ..runtime
                     (..singular input "ruby")
                     (<>.else //runtime.default_ruby))]]
    ($_ <>.and
        ^parents
        ^identity
        ^info
        ^repositories
        ^dependencies
        ^lux
        ^compilers
        ^sources
        ^target
        ^program
        ^test
        ^deploy_repositories
        ^configuration
        ^java
        ^js
        ^python
        ^lua
        ^ruby
        )))

(def: .public project
  (Parser Project)
  (# <>.monad each
     (dictionary.of_list text.hash)
     (<code>.tuple (<>.many (<>.and <code>.text
                                    ..profile)))))