-- | Read PGF files created with GF 3.5 and a few older releases
module PGF.OldBinary(getPGF,getPGF',version) where

import PGF.CId
import PGF.Data
import PGF.Optimize
import Data.Binary
import Data.Binary.Get
import Data.Array.IArray
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import qualified Data.Set as Set
import Control.Monad

pgfMajorVersion, pgfMinorVersion :: Word16
version@(pgfMajorVersion, pgfMinorVersion) = (1,0)

getPGF = do v1 <- getWord16be
            v2 <- getWord16be
            let v=(v1,v2)
            if v==version 
              then getPGF'
              else decodingError ("version "++show v++"/="++show version)

getPGF'=do gflags <- getFlags
           absname <- getCId
           abstract <- getAbstract
           concretes <- getMap getCId getConcr
           return $ updateProductionIndices $
                      (PGF{ gflags=gflags
                          , absname=absname, abstract=abstract
                          , concretes=concretes
                          })

getCId = liftM CId get

getAbstract =
        do aflags <- getFlags
           funs <- getMap getCId getFun
           cats <- getMap getCId getCat
           return (Abstr{ aflags=aflags
                        , funs=fmap (\(w,x,y,z) -> (w,x,fmap (flip (,) []) y,z)) funs
                        , cats=fmap (\(x,y) -> (x,y,0)) cats
                        })
getFun :: Get (Type,Int,Maybe [Equation],Double)
getFun = (,,,) `fmap` getType `ap` get `ap` getMaybe (getList getEquation) `ap` get

getCat :: Get ([Hypo],[(Double, CId)])
getCat = getPair (getList getHypo) (getList (getPair get getCId))

getFlags = getMap getCId getLiteral

getConcr =
        do cflags      <- getFlags
           printnames  <- getMap getCId get
           (scnt,seqs) <- getList' getSequence
           (fcnt,cncfuns) <- getList' getCncFun
           lindefs     <- get
           productions <- getIntMap (getSet getProduction)
           cnccats     <- getMap getCId getCncCat
           totalCats   <- get
           let rseq    = listToArray [SymCat 0 0]
               rfun    = CncFun (mkCId "linref") (listToArray [scnt])
               linrefs = IntMap.fromList [(i,[fcnt])|i<-[0..totalCats-1]]
           return (Concr{ cflags=cflags, printnames=printnames
                        , sequences=toArray (scnt+1,seqs++[rseq])
                        , cncfuns=toArray (fcnt+1,cncfuns++[rfun])
                        , lindefs=lindefs, linrefs=linrefs
                        , productions=productions
                        , pproductions = IntMap.empty
                        , lproductions = Map.empty
                        , lexicon = IntMap.empty
                        , cnccats=cnccats, totalCats=totalCats
                        })

getExpr =
        do tag <- getWord8
           case tag of
             0 -> liftM3 EAbs getBindType getCId getExpr
             1 -> liftM2 EApp getExpr getExpr
             2 -> liftM  ELit getLiteral
             3 -> liftM  EMeta get
             4 -> liftM  EFun getCId
             5 -> liftM  EVar get
             6 -> liftM2 ETyped getExpr getType
             7 -> liftM  EImplArg getExpr
             _ -> decodingError "getExpr"

getPatt =
        do tag <- getWord8
           case tag of
             0 -> liftM2 PApp getCId (getList getPatt)
             1 -> liftM  PVar getCId
             2 -> liftM2 PAs getCId getPatt
             3 -> return PWild
             4 -> liftM  PLit getLiteral
             5 -> liftM  PImplArg getPatt
             6 -> liftM  PTilde getExpr
             _ -> decodingError "getPatt"

getEquation = liftM2 Equ (getList getPatt) getExpr

getType = liftM3 DTyp (getList getHypo) getCId (getList getExpr)
getHypo = (,,) `fmap` getBindType `ap` getCId `ap` getType

getBindType =
        do tag <- getWord8
           case tag of
             0 -> return Explicit
             1 -> return Implicit
             _ -> decodingError "getBindType"

getCncFun = liftM2 CncFun getCId (getArray get)

getCncCat = liftM3 CncCat get get (getArray get)

getSequence = listToArray `fmap` getSymbols

getSymbols = concat `fmap` getList getSymbol

getSymbol :: Get [Symbol]
getSymbol =
        do tag <- getWord8
           case tag of
             0 -> (:[]) `fmap` liftM2 SymCat get get
             1 -> (:[]) `fmap` liftM2 SymLit get get
             2 -> (:[]) `fmap` liftM2 SymVar get get
             3 -> liftM  (map SymKS) get
             4 -> (:[]) `fmap` liftM2 SymKP (getList getTokenSymbol) getAlternatives
             _ -> decodingError ("getSymbol "++show tag)

getAlternatives = getList (getPair (getList getTokenSymbol) get)
                  :: Get [([Symbol],[String])]
getTokenSymbol = fmap SymKS get

--getTokens = unwords `fmap` get

getPArg = get >>= \(hypos,fid) -> return (PArg (zip (repeat fidVar) hypos) fid)

getProduction =
        do tag <- getWord8
           case tag of
             0 -> liftM2 PApply  get (getList getPArg)
             1 -> liftM  PCoerce get
             _ -> decodingError "getProduction"

getLiteral =
        do tag <- getWord8
           case tag of
             0 -> liftM  LStr get
             1 -> liftM  LInt get
             2 -> liftM  LFlt get
             _ -> decodingError "getLiteral"


getArray :: IArray a e => Get e -> Get (a Int e)
getArray get1 = toArray `fmap` getList' get1

toArray (n,xs) = listArray (0::Int,n-1) xs
listToArray xs = toArray (length xs,xs)

getArray2 :: (IArray a1 (a2 Int e), IArray a2 e) => Get e -> Get (a1 Int (a2 Int e))
getArray2 get1 = getArray (getArray get1)

getList get1 = snd `fmap` getList' get1

getList' get1 = do n <- get :: Get Int
                   xs <- replicateM n get1
                   return (n,xs)

getMaybe get1 =
    do isJust <- get
       if isJust then fmap Just get1 else return Nothing

getMap getK getV = Map.fromDistinctAscList `fmap` getList (getPair getK getV)
getIntMap getV = IntMap.fromDistinctAscList `fmap` getList (getPair get getV)
getSet getV = Set.fromDistinctAscList `fmap` getList getV

getPair get1 get2 = (,) `fmap` get1 `ap` get2

decodingError explain = fail $ "Unable to read PGF file ("++explain++")"
