{-# LANGUAGE TypeFamilies #-}

{- |
   Module      : Data.Graph.Planar.Serialisation
   Description : Serialisation for planar graphs.
   Copyright   : (c) Ivan Lazar Miljenovic
   License     : 3-Clause BSD-style
   Maintainer  : Ivan.Miljenovic@gmail.com

 -}
module Data.Graph.Planar.Serialisation
    ( PlanarEncoding(..)
    , encodePlanarFile
    , encodePlanarFileFrom
    , decodePlanarFile
    ) where

import Data.Graph.Planar hiding (isEmpty)
import Data.Graph.Planar.Serialisation.Internal

import Blaze.ByteString.Builder(toLazyByteString)
import Blaze.ByteString.Builder.Char8(fromChar)
import Data.Attoparsec.ByteString.Lazy(parse, eitherResult, many1, (<?>))
import Data.Attoparsec.ByteString.Char8(endOfLine)
import qualified Data.ByteString.Lazy as B
import Data.Monoid(mempty, mappend)
import Control.Applicative((<*))
import Control.Monad(foldM)

-- -----------------------------------------------------------------------------

-- | Encode a list of planar graphs to file using the specified
--   encoding.
encodePlanarFile :: (PlanarEncoding ser) => ser -> FilePath
                    -> [PlanarGraph (NLabel ser) (ELabel ser)]
                    -> IO Int
encodePlanarFile ser fp = encodePlanarFileFrom ser fp . map ((,) Nothing)

-- | Encode a list of planar graphs to file using the specified
--   encoding, with the serialisation traversing from the an
--   optionally specified edge.
encodePlanarFileFrom :: (PlanarEncoding ser) => ser -> FilePath
                        -> [(Maybe Edge,PlanarGraph (NLabel ser) (ELabel ser))]
                        -> IO Int
encodePlanarFileFrom ser fp pgs = do B.writeFile fp $ toLazyByteString header
                                     foldM printCount 0 pgs
  where
    header = putName ser

    maybeNewline | sepByNewline ser = fromChar '\n'
                 | otherwise        = mempty

    printCount c pg = c `seq` (B.appendFile fp (toB pg) >> return (c+1))
    toB pg = toLazyByteString $ putSG ser (toSer pg) `mappend` maybeNewline

    toSer (me,pg) = ((order pg, size pg), serialiseBFS pg me)

-- | Read in a file containing encoded graphs.  The 'PlanarEncoding'
--   argument is only used for its /type/ to determine which parser to
--   use.
decodePlanarFile        :: (PlanarEncoding ser) => ser -> FilePath
                           -> IO [PlanarGraph (NLabel ser) (ELabel ser)]
decodePlanarFile ser fp = do bs <- B.readFile fp
                             case eitherResult $ parse parser bs of
                               Left err  -> error $ "Could not parse file " ++ fp ++ " with the error: " ++ err
                               Right sgs -> return $ map deserialise sgs

    where
      parser = do nm <- fmap (`asTypeOf` ser) getName <?> "Parsing encoding header"
                  many1 $ getSG nm <* maybeNewLine

      maybeNewLine | sepByNewline ser = endOfLine
                   | otherwise        = return ()