{-| Grid compression
-}
module GridBox.Compress
  ( Length
  , CompressedGrid
  , CompressedRow
  , CompressedCols
  , CompressedCol
  , compressGrid
  , compressRow
  , compressBoxes
  , compressGroupedRows
  , compressGroupedRow
  , compressCols
  , fromCols
  , mergeCols
  ) where

import GridBox.Grid (Grid)
import GridBox.Row (Row)
import GridBox.Box (Box, BoxRow, BoxCol)
import GridBox.Grouping (GroupedRow, GroupedByRow, groupBoxesByRow)

-- | Length of a compressed col
type Length = Int

-- | Compressed grid
type CompressedGrid = [CompressedRow]

-- | Compressed row
type CompressedRow = (BoxRow, CompressedCols)

-- | Compressed cols
type CompressedCols = [CompressedCol]

-- | Compressed col
type CompressedCol = (BoxCol, Length)

-- | Compress grid
compressGrid :: Grid -> CompressedGrid
compressGrid = compressBoxes . concat

-- | Compress row
compressRow :: Row -> CompressedGrid
compressRow = compressBoxes

-- | Compress boxes
compressBoxes :: [Box] -> CompressedGrid
compressBoxes boxes =
  compressGroupedRows $ groupBoxesByRow boxes

-- | Compress grouped rows
compressGroupedRows :: GroupedByRow -> CompressedGrid
compressGroupedRows rows =
  map compressGroupedRow rows

-- | Compress grouped row
compressGroupedRow :: GroupedRow -> CompressedRow
compressGroupedRow (a, cols) =
  (a, compressCols cols)

-- | Compress cols
compressCols :: [BoxCol] -> CompressedCols
compressCols =
  mergeCols . fromCols

-- | Convert list of box cols to compressed cols with length = 1
fromCols :: [BoxCol] -> CompressedCols
fromCols =
  map (\l -> (l, 1))

-- | Merge cols that have neighbours.
-- Each col a and b is merged to c. The index of c is the index of a and the
-- length of c is the sum of length a and b. 
mergeCols :: CompressedCols -> CompressedCols
mergeCols [] = []
mergeCols (a:[]) = [a]
mergeCols ((ac, al):(bc, bl):xs)
    | ac == bc - al = mergeCols $ (ac, al+bl):xs
    | otherwise = (ac, al) : (mergeCols $ (bc, bl):xs)