{-| The package provides configuration loader and pre-defined resolver.

An configuration example:

@
_vars:
  window:
    width: 800
    height: 600
app:
  - name: message-layer
    properties:
      window:
        image: resources/window-base.png
        position:
          x: 0
          y: ${${var:window.height} - ${ref:..size.height}}
        size:
          width: ${var:window.width}
          height: 150
@

== Syntax

=== @_vars@

You can define a new variable. Use object syntax under the @_vars@ field.

The variables can be referenced in all siblings and under their siblings to the @_vars@, in the variable syntax @${var:_path_}@.

=== Expr

In each field, you can specify an expression defined in the loader.

- @${}@: enclose the expr by @${}@, to tell the parsers that the field is an expr not a plain string.
- @${ref:_path_}@: specify any path to refer any other value. The path resolution is performed once, not recursively resolved. @_path_@ consists of field names splitted by a period. Use double dots @..@ for a parent.
- @${var:_path_}@: specify any path to value defined at the field. @_path_@ consists of field names splitted by a period.
- arithmetic operator: addition, subtraction, multiplication and division (@+,-,*,/@) can also be used in @${}@.

-}
module MiniLight.Component (
  module MiniLight.Component.Types,
  loadAppConfig,

  Resolver,
  defResolver
) where

import qualified Data.Aeson as Aeson
import qualified Data.Text as T
import MiniLight.Light
import MiniLight.Component.Types
import MiniLight.Component.Loader
import qualified MiniLight.Component.AnimationLayer as AnimationLayer
import qualified MiniLight.Component.Button as Button
import qualified MiniLight.Component.Layer as Layer
import qualified MiniLight.Component.MessageEngine as MessageEngine
import qualified MiniLight.Component.MessageLayer as MessageLayer

foldResult :: (String -> b) -> (a -> b) -> Aeson.Result a -> b
foldResult f g r = case r of
  Aeson.Error   err -> f err
  Aeson.Success a   -> g a

type Resolver = T.Text -> Aeson.Value -> MiniLight Component

-- | Pre-defined resolver supports all components in this library.
defResolver :: Resolver
defResolver name props = case name of
  "animation-layer" -> newComponent
    =<< AnimationLayer.new (foldResult error id $ Aeson.fromJSON props)
  "button" ->
    newComponent =<< Button.new (foldResult error id $ Aeson.fromJSON props)
  "layer" ->
    newComponent =<< Layer.new (foldResult error id $ Aeson.fromJSON props)
  "message-engine" -> newComponent
    =<< MessageEngine.new (foldResult error id $ Aeson.fromJSON props)
  "message-layer" -> newComponent
    =<< MessageLayer.new (foldResult error id $ Aeson.fromJSON props)
  "tiled-layer" -> newComponent
    =<< Layer.newNineTile (foldResult error id $ Aeson.fromJSON props)
  _ -> error $ T.unpack $ "Component not defined: `" <> name <> "`"