{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}

module YesodDsl.Generator.Classes where
import YesodDsl.AST
import YesodDsl.AST
import Data.Maybe
import qualified Data.Text as T
import Data.List
import Text.Shakespeare.Text hiding (toText)
import YesodDsl.Generator.Models
import YesodDsl.Generator.Common
import Data.String.Utils (rstrip)
classFieldName :: Class -> Field -> String
classFieldName i f = (lowerFirst . className) i ++ (upperFirst . fieldName) f

classDefField :: Class -> Field -> String
classDefField c f = T.unpack $(codegenFile "codegen/class-field.cg")

classFieldTypeName :: Class -> Field -> String
classFieldTypeName c f= rstrip $ T.unpack $(codegenFile "codegen/class-field-type-field-name.cg")

classFieldType :: Class -> [Field]-> String
classFieldType c fs = if null fs
    then ""
    else T.unpack $(codegenFile "codegen/class-field-type.cg")

classInstanceField :: Class -> Entity -> Field -> String
classInstanceField c e f = T.unpack $(codegenFile "codegen/class-instance-field.cg")

classInstance :: Class -> Entity -> String
classInstance c e = T.unpack $(codegenFile "codegen/class-instance-header.cg")
                  ++ (concatMap (classInstanceField c e) (classFields c))

classEntityInstanceField :: Class -> [Entity] -> Field -> String
classEntityInstanceField c es f = T.unpack $(codegenFile "codegen/class-entity-instance-field.cg")
    where caseEntity e = T.unpack $(codegenFile "codegen/class-entity-instance-field-entity.cg")


classEntityInstances :: Class -> [Entity] -> String
classEntityInstances c es = T.unpack $(codegenFile "codegen/class-entity-instances.cg")
    ++ (concatMap (classEntityInstanceField c es) (classFields c))
    where 
        entityInstance e = T.unpack $(codegenFile "codegen/class-entity-instance.cg")
        entityInstanceId e = T.unpack $(codegenFile "codegen/class-entity-instance-id.cg")

classSelectFilterDataType :: Class -> String
classSelectFilterDataType c = T.unpack $(codegenFile "codegen/class-select-filter-data-type.cg")
    where 
        fieldFilterDataType f = rstrip $ T.unpack $(codegenFile "codegen/class-select-filter-data-type-field.cg")

classSelect :: Class -> [Entity] -> String
classSelect c es = maybeFilterDataType 
    ++ T.unpack $(codegenFile "codegen/class-select.cg")
    where 
        selectEntity e = T.unpack $(codegenFile "codegen/class-select-entity.cg")
        wrapResult e = T.unpack $(codegenFile "codegen/class-select-result.cg")
        hasClassFields = not . null $ classFields c
        maybeFilter e = if hasClassFields
            then T.unpack $(codegenFile "codegen/class-select-entity-filter.cg")
            else ""
        maybeFilterDataType = if hasClassFields 
            then classSelectFilterDataType c
            else ""
        maybeFilterParam = if hasClassFields then "filters" :: String else ""
        filterField e f = T.unpack $(codegenFile "codegen/class-select-entity-filter-field.cg")
        maybeFilterType = if hasClassFields then rstrip $T.unpack $(codegenFile "codegen/class-select-filter-type.cg") else ""


classUpdate :: Class -> [Entity] -> String
classUpdate c es 
    | hasClassFields = T.unpack $(codegenFile "codegen/class-update-data-type.cg")
        ++ (T.unpack $(codegenFile "codegen/class-update.cg"))
    | otherwise = ""
    where
        hasClassFields = not . null $ classFields c
        updateEntity e = T.unpack $(codegenFile "codegen/class-update-entity.cg")
        fieldUpdateDataType f = rstrip $ T.unpack $(codegenFile "codegen/class-update-data-type-field.cg")
        updateEntityField e f = T.unpack $(codegenFile "codegen/class-update-entity-field.cg")
        maybeFilter e = if hasClassFields
            then T.unpack $(codegenFile "codegen/class-select-entity-filter.cg")
            else ""
        filterField e f = T.unpack $(codegenFile "codegen/class-select-entity-filter-field.cg")
        
instancesOf :: Module -> Class -> [Entity]
instancesOf m c = [ e | e <- modEntities m,  (className c) `elem` (entityInstances e)]


classInstances :: Module -> Class -> String
classInstances m c = T.unpack $(codegenFile "codegen/class-header.cg")
                   ++ (concatMap (classDefField c) (classFields c))
                   ++ (classFieldType c $ classFields c)
                   ++ (concatMap (classInstance c) (instancesOf m c))
                   ++ (classEntityInstances c (instancesOf m c))
                   ++ (classSelect c $ instancesOf m c)
                   ++ (classUpdate c $ instancesOf m c)

                                           
entityClassFieldWrappers :: Module -> Entity -> String
entityClassFieldWrappers m e = concatMap fieldWrapper $ entityClassFields e
    where
        fieldWrapper f@(Field _ _ _ _ (EntityField cName)) = case find (\c -> className c == cName) $ modClasses m of
            Just c ->  T.unpack $(codegenFile "codegen/entity-class-field-wrapper.cg")
            Nothing -> ""
        fieldWrapper _ = ""
        wrapInstance c f e2 = T.unpack $(codegenFile "codegen/entity-class-field-wrapper-wrap-instance.cg")
classes :: Module -> String
classes m = 
    (concatMap (classInstances m) (modClasses m))
    ++ (concatMap (entityClassFieldWrappers m) (modEntities m))