{-|
Description : Working with lines of code
-}
module Language.Haskell.Formatter.Process.LineTool
       (Shifter, Shift, countEmptyLines, createShifter, shiftCode) where
import qualified Data.Map.Strict as Map
import qualified Language.Haskell.Formatter.Location as Location
import qualified Language.Haskell.Formatter.Process.Code as Code

newtype Shifter = Shifter (Map.Map Location.Line Shift)
                    deriving (Eq, Ord, Show)

type Shift = Int

countEmptyLines :: Location.Line -> Location.Line -> Int
countEmptyLines endLine startLine = pred lineDifference
  where lineDifference = Location.minus startLine endLine

createShifter :: Map.Map Location.Line Shift -> Shifter
createShifter relativeShifter = Shifter absoluteShifter
  where (_, absoluteShifter) = Map.mapAccum accumulate noShift relativeShifter
        accumulate absoluteShift relativeShift
          = (absoluteShift', absoluteShift')
          where absoluteShift' = absoluteShift + relativeShift

noShift :: Shift
noShift = 0

shiftCode :: Shifter -> Code.LocatableCode -> Code.LocatableCode
shiftCode shifter = fmap $ shiftNestedPortion shifter
  where shiftNestedPortion = Location.replaceNestedPortionLines . shiftLine

shiftLine :: Shifter -> Location.Line -> Location.Line
shiftLine shifter line = Location.plus shift line
  where shift = lookupShift line shifter

lookupShift :: Location.Line -> Shifter -> Shift
lookupShift line (Shifter shifter)
  = case Map.lookupLE line shifter of
        Nothing -> noShift
        Just (_, shift) -> shift