module YesodDsl.ExpandMacros (expandMacros) where
import YesodDsl.AST
import Data.List
expandMacros :: Module -> Module
expandMacros m = m {
        modRoutes = map fRoute $ modRoutes m
    }
    where
        fRoute r = r {
            routeHandlers = map fHandler $ routeHandlers r
        }

        fHandler h = h {
            handlerParams = map fHandlerParam $ handlerParams h
        }

        fHandlerParam (Select sq) = Select (fSelectQuery sq)
        fHandlerParam (Require sq) = Require (fSelectQuery sq)
        fHandlerParam (DeleteFrom en vn (Just e)) = DeleteFrom en vn (Just $ fExpr e)
        fHandlerParam hp = hp

        fSelectQuery sq = sq {
            sqJoins = map fJoin $ sqJoins sq,
            sqWhere = maybe Nothing (Just . fExpr) $ sqWhere sq
        }
        fJoin j = j {
            joinExpr = maybe Nothing (Just . fExpr) $ joinExpr j
        }
        fExpr (AndExpr e1 e2) = AndExpr (fExpr e1) (fExpr e2)
        fExpr (OrExpr e1 e2) = OrExpr (fExpr e1) (fExpr e2)
        fExpr (NotExpr e) = NotExpr (fExpr e)
        fExpr (BinOpExpr ve1 bo ve2) = BinOpExpr (fValExpr ve1) bo
                                                 (fValExpr ve2)
        fValExpr (ConcatManyExpr ves) = ConcatManyExpr $ map fValExpr ves
        fValExpr (ValBinOpExpr ve1 op ve2) = ValBinOpExpr (fValExpr ve1) op (fValExpr ve2)
        fValExpr (FloorExpr ve) = FloorExpr (fValExpr ve)
        fValExpr (CeilingExpr ve) = CeilingExpr (fValExpr ve)
        fValExpr (ExtractExpr fn ve) = ExtractExpr fn (fValExpr ve)
        fValExpr (SubQueryExpr sq) = SubQueryExpr $ fSelectQuery sq
        fValExpr (ApplyExpr fn ps) = expandApplyExpr fn ps
        fValExpr ve = ve


        expandApplyExpr fn ps = case find (\d -> defineName d == fn) (modDefines m) of
            Just d -> if length (defineParams d) == length ps
                then case defineContent d of
                    (DefineSubQuery sq) -> SubQueryExpr (expandSubQuery sq $ zip (defineParams d) ps)
                else error $ "Expected " ++ show (length $ defineParams d)
                             ++ " parameters for macro " ++ fn ++ " got " ++
                             show (length ps)
            _ -> error $ "Reference to undefined macro " ++ fn
                        

        expandSubQuery sq subs = foldl repSubQuery sq subs
        repSubQuery sq sub = sq {
            sqFields = map (repSelectField sub) $ sqFields sq,
            sqJoins = map (repJoin sub) $ sqJoins sq,
            sqOrderBy = map (\(fr,sd) -> (repFieldRef sub fr, sd)) $ sqOrderBy sq,
            sqWhere = sqWhere sq >>= return . (repExpr sub)
                
        }
        repSelectField (pn,pv) spf@(SelectParamField vn pn' mvn) 
            | pn == pn' = SelectField vn pv mvn
            | otherwise = spf
        repSelectField _ sf = sf

        repJoin sub j = j {
                joinExpr = joinExpr j >>= return . (repExpr sub)
            }

        repFieldRef (pn,pv) fr@(FieldRefParamField vn pn') 
            | pn == pn' = FieldRefNormal vn pv
            | otherwise = fr
        repFieldRef _ fr  = fr
        
        repExpr sub (AndExpr e1 e2) = AndExpr (repExpr sub e1) (repExpr sub e2)
        repExpr sub (OrExpr e1 e2) = OrExpr (repExpr sub e1) (repExpr sub e2)
        repExpr sub (NotExpr e1) = NotExpr (repExpr sub e1)
        repExpr sub (BinOpExpr ve1 bo ve2) = BinOpExpr (repValExpr sub ve1)
                                                       bo
                                                       (repValExpr sub ve2)
        repExpr sub (ExistsExpr sq) = ExistsExpr (repSubQuery sq sub)                                               
        repValExpr sub (FieldExpr fr) = FieldExpr (repFieldRef sub fr)
        repValExpr sub (ConcatManyExpr ves) = ConcatManyExpr (map (repValExpr sub) ves)
        repValExpr sub (ValBinOpExpr ve1 vbo ve2) = 
            ValBinOpExpr (repValExpr sub ve1) vbo (repValExpr sub ve2)
        repValExpr sub (FloorExpr ve) = FloorExpr $ repValExpr sub ve
        repValExpr sub (CeilingExpr ve) = CeilingExpr $repValExpr sub ve
        repValExpr sub (ExtractExpr fn ve) = ExtractExpr fn $ repValExpr sub ve
        repValExpr sub (SubQueryExpr sq) = SubQueryExpr $ repSubQuery sq sub
        repValExpr _ (ApplyExpr fn ps) = expandApplyExpr fn ps
        repValExpr _ ve = ve