module Type.Constrain.Declaration where

import Control.Monad
import Control.Applicative ((<$>))

import qualified Data.Map as Map

import qualified Type.Constrain.Expression as TcExpr
import qualified Type.Environment as Env

import SourceSyntax.Declaration
import qualified SourceSyntax.Everything as Src
import qualified SourceSyntax.Type as Type

toExpr :: [Declaration t v] -> [Src.Def t v]
toExpr = concatMap toDefs

toDefs :: Declaration t v -> [Src.Def t v]
toDefs decl =
  case decl of
    Definition def -> [def]

    Datatype name tvars constructors -> concatMap toDefs constructors
      where
        toDefs (ctor, tipes) =
            let vars = take (length tipes) arguments
                tbody = Type.Data name $ map Type.Var tvars
                body = Src.none . Src.Data ctor $ map (Src.none . Src.Var) vars
            in  [ Src.TypeAnnotation ctor $ foldr Type.Lambda tbody tipes
                , Src.Def (Src.PVar ctor) $ buildFunction body vars
                ]

    TypeAlias name tvars tipe@(Type.Record fields ext) ->
        [ Src.TypeAnnotation name $ foldr Type.Lambda tipe args
        , Src.Def (Src.PVar name) $ buildFunction record vars ]
      where
        args = case ext of
                 Type.EmptyRecord -> map snd fields
                 _ -> map snd fields ++ [ext]

        var = Src.none . Src.Var
        vars = take (length args) arguments

        efields = zip (map fst fields) (map var vars)
        record = case ext of
                   Type.EmptyRecord -> Src.none $ Src.Record efields
                   _ -> foldl (\r (f,v) -> Src.none $ Src.Insert r f v) (var $ last vars) efields

    -- Type aliases must be added to an extended equality dictionary,
    -- but they do not require any basic constraints.
    TypeAlias _ _ _ -> []

    ImportEvent _ expr@(Src.L s _) name tipe ->
        [ Src.TypeAnnotation name tipe
        , Src.Def (Src.PVar name) (Src.L s $ Src.App (Src.L s $ Src.Var "constant") expr) ]

    ExportEvent _ name tipe ->
        [ Src.TypeAnnotation name tipe ]

    -- no constraints are needed for fixity declarations
    Fixity _ _ _ -> []


arguments :: [String]
arguments = map (:[]) ['a'..'z'] ++ map (\n -> "_" ++ show n) [1..]

buildFunction body@(Src.L s _) vars =
    foldr (\p e -> Src.L s (Src.Lambda p e)) body (map Src.PVar vars)