    module Hoogle.Score.Type(
        Score, TypeCost(..), TextMatch(..),
        textScore, typeScore,
        scoreCosts, cost
        ) where

    import Data.List
    import Data.Monoid

    data TypeCost
        = CostAliasFwd
        | CostAliasBwd
        | CostUnbox
        | CostRebox
        | CostRestrict
        | CostUnrestrict
        | CostDupVarResult
        | CostDupVarQuery
        | CostInstanceDel
        | CostInstanceAdd
        | CostDeadArg
        | CostArgReorder
          deriving (Show,Eq,Ord,Enum,Bounded)

    cost :: TypeCost -> Int
    cost CostAliasFwd     =    1  -- 1..1000
    cost CostAliasBwd     =    1  -- 1..997
    cost CostUnbox        =    5  -- 5..1000
    cost CostRebox        =    4  -- 4..999
    cost CostRestrict     =    5  -- 5..1000
    cost CostUnrestrict   =    4  -- 4..1000
    cost CostDupVarResult =    4  -- 4..999
    cost CostDupVarQuery  =    5  -- 5..1000
    cost CostInstanceDel  =    4  -- 4..999
    cost CostInstanceAdd  =    4  -- 4..999
    cost CostDeadArg      =    3  -- 3..998
    cost CostArgReorder   =    1  -- 1..1000

    data TextMatch
        = MatchExact
        | MatchPrefix
        | MatchExactCI              -- exact letter match, but case mismatch
        | MatchPrefixCI
        | MatchSubstr
          deriving (Show,Eq,Ord,Enum,Bounded)

    -- | A score, representing how close a match is. Lower scores are better.
    data Score = Score Int [TypeCost] [TextMatch]

    instance Monoid Score where
        mempty = Score 0 [] []
        mappend (Score x1 x2 x3) (Score y1 y2 y3) =
            Score (x1+y1) (sort $ x2++y2) (sort $ x3++y3)

    textScore :: TextMatch -> Score
    textScore x = Score 0 [] [x]

    typeScore :: [TypeCost] -> Score
    typeScore xs = Score (sum $ map cost xs) (sort xs) []

    scoreCosts :: Score -> [TypeCost]
    scoreCosts (Score _ x _) = x

    instance Show Score where
        show (Score _ a b) =
            intercalate "+" $ map (drop 4 . show) a ++ map (drop 5 . show) b

    instance Eq Score where
        Score x1 x2 [] == Score y1 y2 y3 = [] == y3 || x1 == y1
        Score x1 x2 x3 == Score y1 y2 [] = x3 == [] || x1 == y1
        Score x1 x2 x3 == Score y1 y2 y3 = head x3 == head y3 || x1 == y1

    instance Ord Score where
        compare (Score x1 x2 x3) (Score y1 y2 y3) = compare (x3,x1) (y3,y1)