{-# LANGUAGE TypeFamilies, OverloadedStrings #-}

{- |
   Module      : Data.Graph.Planar.Serialisation.PlanarCode
   Description : Implementation of PLANAR CODE.
   Copyright   : (c) Ivan Lazar Miljenovic
   License     : 3-Clause BSD-style
   Maintainer  : Ivan.Miljenovic@gmail.com

module Data.Graph.Planar.Serialisation.PlanarCode(PlanarCode(..)) where

import Data.Graph.Planar(SerialisedGraph)
import Data.Graph.Planar.Serialisation.Internal

import Blaze.ByteString.Builder
import Data.Attoparsec.ByteString.Lazy
import qualified Data.ByteString as SBS
import Data.Foldable(foldMap)
import Control.Applicative((<*))
import Data.Monoid(Monoid(..))
import Control.Monad(replicateM)

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

{- |

 PLANAR_CODE is the most common encoding for planar graphs, and is
 supported by various generation and visualisation tools.  It is a
 binary format and not intended to be human-readable.

 The default encoding only supports graphs with @<256@ nodes, and
 takes @2*|E|+|N|+1@ bytes per graph.

 Please note that PLANAR_CODE is /not/ suitable for graphs with
 multiple loops on vertices (multiple edges with distinct endpoints
 however are catered for).  As such, no guarantees are made about what
 happens with multiple loops.

data PlanarCode = PlanarCode
                  deriving (Eq, Ord, Show, Read)

instance PlanarEncoding PlanarCode where
  type NLabel PlanarCode = ()
  type ELabel PlanarCode = ()

  putSG = const putPlanarCode

  getSG = const getPlanarCode

  putName = const $ fromByteString ">>planar_code<<"

  getName = string ">>planar_code<<" >> return PlanarCode

  sepByNewline = const False

putPlanarCode :: ((Int,Int),SerialisedGraph n e) -> Builder
putPlanarCode ((ord,_),sg) = fromWord8 (fromIntegral ord)
                             `mappend` foldMap (fromWrite . putNode . nodeEdgesSer)
    putNode es = foldMap (writeWord8 . succ . fromIntegral . toNodeSer) es
                 `mappend` writeWord8 0
                 -- Need succ here, because the SerialisedGraph is 0-based, but PC is 1-based.

getPlanarCode :: Parser (SerialisedGraph () ())
getPlanarCode = do num <- fromIntegral `fmap` anyWord8
                   ess <- replicateM num getNode
                   -- Convert to 0-based Word values
                   let ess' = map (map $ fromIntegral . pred) ess
                   return $ processPC ess'
    getNode = SBS.unpack `fmap` takeWhile1 (/= 0) <* anyWord8 -- will be 0