module Elm.Decoder
    ( decoder
    , listDecoder
    ) where

import           Elm.Helpers
import           Elm.Types

elmDecoder :: Expr -> String
elmDecoder (Primitive t) = case t of
    String ->
        "Json.Decode.string"
    Int ->
        "Json.Decode.int"
    Float ->
        "Json.Decode.float"
    Date ->
        "Json.Decode.Extra.date"
    Bool ->
        "Json.Decode.bool"
    p -> error $ "No decoder for Primitive known: " ++ show p
elmDecoder (Field f) = elmDecoder f
elmDecoder (DataType name _) = "decode" ++ name
elmDecoder (Product (Primitive List) (Primitive Char)) = elmDecoder $ Primitive String
elmDecoder (Product (Primitive List) t) =
    "Json.Decode.list " ++ if isProduct t
                           then "(" ++ elmDecoder t ++ ")"
                           else elmDecoder t
elmDecoder (Product (Primitive Maybe) t) =
    "Json.Decode.maybe " ++ if isProduct t
                            then "(" ++ elmDecoder t ++ ")"
                            else elmDecoder t
elmDecoder d = error $ "No decoder for definition known: " ++ show d

decoderFunction :: String -> String
decoderFunction = (++) "decode"

decoder :: Expr -> String
decoder (DataType name (Record _ expr)) = format
    [ func ++ " : Json.Decode.Decoder " ++ name
    , func ++ " ="
    , tab 4 "Json.Decode.succeed " ++ name
    , format $ map (tab 8) $ build expr
    ]
    where func = decoderFunction name
          build (Product s1 s2) = build s1 ++ build s2
          build (Selector _ selector t) =
              [ concat $ surround "|: (" " := " ")"
                  [ show selector
                  , elmDecoder t
                  ]
              ]
          build d = error $ "Unsupported Record decoder Selector definition: " ++ show d
decoder (DataType name s) = format
    [ func ++ " : Json.Decode.Decoder " ++  name
    , func ++ " ="
    , tab 4 "let"
    , tab 8 "resolver c = case c of"
    , format $ map (tab 12 . toCase) $ flatten s
    , tab 12 "_ -> Json.Decode.fail (\"Unable to resolve \" ++ c ++ \" to a " ++ name ++ "\")"
    , tab 4 "in"
    , tab 8 $ elmDecoder (Primitive String) ++ " `Json.Decode.andThen` resolver"
    ]
    where func = decoderFunction name
          flatten (Sum a b) = flatten a ++ flatten b
          flatten (Constructor constructor _) = [constructor]
          flatten d = error $ "Unsupported Sum decoder Constructor definition: " ++ show d
          toCase constructor = "\"" ++ constructor ++ "\" -> Json.Decode.succeed " ++ constructor
decoder d = error $ "Unsupported decoder definition: " ++ show d

listDecoder :: Expr -> String
listDecoder (DataType name _) = format
    [ func ++ " : Json.Decode.Decoder (List " ++ name ++ ")"
    , func ++ " ="
    , tab 4 "Json.Decode.list " ++ decoderFunction name
    ]
    where func = "decode" ++ name ++ "List"
listDecoder d = error $ "Unsupported list decoder definition: " ++ show d