module Hoogle.Operations.RankSolve(Cmp(..), rankSolve) where

import General.Code
import qualified Data.IntMap as IntMap

-- Privilaged imports
import Hoogle.DataBase.TypeSearch.Cost(Cost(..))


-- invariant, costs must remain sorted
data Cmp = [Cost] :< [Cost]
           deriving (Show,Eq)


rankSolve :: [Cmp] -> IO ()
rankSolve xs | missing /= [] = error $ "Missing costs: " ++ show missing
             | otherwise = putStr $ showBind res
    where
        res = fix (solve xs2) emptyBind
        xs2 = nub $ concatMap simplify xs
        missing = costs \\ concat [a ++ b | a :< b <- xs2]


costs = [minBound..maxBound] :: [Cost]

type Bind = IntMap.IntMap (Int,Int)

emptyBind :: Bind
emptyBind = IntMap.fromAscList [(fromEnum i, (1,1000)) | i <- costs]


showBind :: Bind -> String
showBind mp = unlines ["cost " ++ padR ncosts (show (toEnum a :: Cost)) ++
                       " = " ++ padL 4 (show b) ++ "  -- " ++ show b ++ ".." ++ show c 
                      | (a,(b,c)) <- IntMap.toList mp]
    where ncosts = maximum $ map (length . show) costs

padL, padR :: Int -> String -> String
padL n xs = replicate (n - length xs) ' ' ++ xs
padR n xs = xs ++ replicate (n - length xs) ' '



simplify :: Cmp -> [Cmp]
simplify c@(xs :< ys)
        | null xs2 = []
        | null ys2 = error $ "rankSolve, contradiction exists: " ++ show c
        | otherwise = [xs2 :< ys2]
    where
        common = intersect xs ys
        xs2 = xs \\ common
        ys2 = ys \\ common


solve :: [Cmp] -> Bind -> Bind
solve xs mp = foldl' f mp xs
    where
        -- all x in xs, x.max = ys.max - 1
        -- all y in ys, y.min = xs.min + 1
        f mp (xs :< ys) = upd (second $ min (maxRhs - 1)) xs $
                          upd (first  $ max (minLhs + 1)) ys mp
            where
                minLhs = grab fst xs mp
                maxRhs = grab snd ys mp


        grab side xs mp = sum $ map (side . (mp IntMap.!) . fromEnum) xs

        -- TODO: does not deal well with multiple elements on either side
        --       some constraints may not be satisfied
        upd op [x] mp = IntMap.update (Just . op) (fromEnum x) mp
        upd op _ mp = mp