module Opaleye.Internal.Optimize where

import           Prelude hiding (product)

import qualified Opaleye.Internal.PrimQuery as PQ

import qualified Data.List.NonEmpty as NEL

optimize :: PQ.PrimQuery -> PQ.PrimQuery
optimize = mergeProduct . removeUnit

removeUnit :: PQ.PrimQuery -> PQ.PrimQuery
removeUnit = PQ.foldPrimQuery (PQ.Unit, PQ.BaseTable, product, PQ.Aggregate,
                                PQ.Order, PQ.Limit, PQ.Join, PQ.Values,
                                PQ.Binary)
  where product pqs pes = PQ.Product pqs' pes
          where pqs' = case NEL.filter (not . PQ.isUnit) pqs of
                         [] -> return PQ.Unit
                         xs -> NEL.fromList xs

mergeProduct :: PQ.PrimQuery -> PQ.PrimQuery
mergeProduct = PQ.foldPrimQuery (PQ.Unit, PQ.BaseTable, product, PQ.Aggregate,
                                PQ.Order, PQ.Limit, PQ.Join, PQ.Values,
                                PQ.Binary)
  where product pqs pes = PQ.Product pqs' (pes ++ pes')
          where pqs' = pqs >>= queries
                queries (PQ.Product qs _) = qs
                queries q = return q
                pes' = NEL.toList pqs >>= conds
                conds (PQ.Product _ cs) = cs
                conds _ = []