{-# LANGUAGE FlexibleContexts, ImplicitParams #-}
module GF.Compile.CFGtoPGF (cf2pgf) where

import GF.Grammar.CFG
import GF.Infra.UseIO
import GF.Infra.Option
import GF.Compile.OptimizePGF

import PGF2

import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import Data.Array.IArray
import Data.List
import Data.Maybe(fromMaybe)

--------------------------
-- the compiler ----------
--------------------------

cf2pgf :: Options -> FilePath -> ParamCFG -> Map.Map Fun Double -> PGF
cf2pgf opts fpath cf probs = error "TODO: cf2pgf" {-
 build (let abstr = cf2abstr cf probs
        in newPGF [] aname abstr [(cname, cf2concr opts abstr cf)])
 where
   name = justModuleName fpath
   aname = name ++ "Abs"
   cname = name

cf2abstr :: (?builder :: Builder s) => ParamCFG -> Map.Map Fun Double -> B s AbstrInfo
cf2abstr cfg probs = newAbstr aflags acats afuns
  where
    aflags = [("startcat", LStr (fst (cfgStartCat cfg)))]

    acats  = [(c', [], toLogProb (fromMaybe 0 (Map.lookup c' probs))) | cat <- allCats' cfg, let c' = cat2id cat]
    afuns  = [(f', dTyp [hypo Explicit "_" (dTyp [] (cat2id c) []) | NonTerminal c <- ruleRhs rule] (cat2id (ruleLhs rule)) [], 0, [], toLogProb (fromMaybe 0 (Map.lookup f' funs_probs)))
                            | rule <- allRules cfg
                            , let f' = mkRuleName rule]

    funs_probs = (Map.fromList . concat . Map.elems . fmap pad . Map.fromListWith (++))
                     [(cat,[(f',Map.lookup f' probs)]) | rule <- allRules cfg,
                                                         let cat = cat2id (ruleLhs rule),
                                                         let f' = mkRuleName rule]
      where
        pad :: [(a,Maybe Double)] -> [(a,Double)]
        pad pfs = [(f,fromMaybe deflt mb_p) | (f,mb_p) <- pfs]
          where
            deflt = case length [f | (f,Nothing) <- pfs] of
                      0 -> 0
                      n -> max 0 ((1 - sum [d | (f,Just d) <- pfs]) / fromIntegral n)

    toLogProb = realToFrac . negate . log

    cat2id = fst

cf2concr :: (?builder :: Builder s) => Options -> B s AbstrInfo -> ParamCFG -> B s ConcrInfo
cf2concr opts abstr cfg = 
  let (lindefs',linrefs',productions',cncfuns',sequences',cnccats') =
           (if flag optOptimizePGF opts then optimizePGF (fst (cfgStartCat cfg)) else id)
                (lindefsrefs,lindefsrefs,IntMap.toList productions,cncfuns,sequences,cnccats)
  in newConcr abstr [] []
              lindefs' linrefs'
              productions' cncfuns'
              sequences' cnccats' totalCats
  where
    cats  = allCats' cfg
    rules = allRules cfg

    idSeq = [SymCat 0 0]

    sequences0 = Set.fromList (idSeq : 
                               map mkSequence rules)
    sequences  = Set.toList sequences0

    idFun = ("_",[Set.findIndex idSeq sequences0])
    ((fun_cnt,cncfuns0),productions0) = mapAccumL (convertRule cs) (1,[idFun]) rules
    productions = foldl addProd IntMap.empty (concat (productions0++coercions))
    cncfuns = reverse cncfuns0

    lbls = ["s"]
    (fid,cnccats) = (mapAccumL mkCncCat 0 . Map.toList . Map.fromListWith max)
                         [(c,p) | (c,ps) <- cats, p <- ps]
    ((totalCats,cs), coercions) = mapAccumL mkCoercions (fid,Map.empty) cats

    lindefsrefs = map mkLinDefRef cats

    convertRule cs (funid,funs) rule =
      let args   = [PArg [] (cat2arg c) | NonTerminal c <- ruleRhs rule]
          prod   = PApply funid args
          seqid  = Set.findIndex (mkSequence rule) sequences0
          fun    = (mkRuleName rule, [seqid])
          funid' = funid+1
      in funid' `seq` ((funid',fun:funs),let (c,ps) = ruleLhs rule in [(cat2fid c p, prod) | p <- ps])

    mkSequence rule = snd $ mapAccumL convertSymbol 0 (ruleRhs rule)
      where
        convertSymbol d (NonTerminal (c,_)) = (d+1,if c `elem` ["Int","Float","String"] then SymLit d 0 else SymCat d 0)
        convertSymbol d (Terminal t)        = (d,  SymKS t)

    mkCncCat fid (cat,n)
      | cat == "Int"    = (fid, (cat, fidInt,    fidInt,    lbls))
      | cat == "Float"  = (fid, (cat, fidFloat,  fidFloat,  lbls))
      | cat == "String" = (fid, (cat, fidString, fidString, lbls))
      | otherwise       = let fid' = fid+n+1
                          in fid' `seq` (fid', (cat, fid, fid+n, lbls))

    mkCoercions (fid,cs) c@(cat,[p]) = ((fid,cs),[])
    mkCoercions (fid,cs) c@(cat,ps ) =
      let fid' = fid+1
      in fid' `seq` ((fid', Map.insert c fid cs), [(fid,PCoerce (cat2fid cat p)) | p <- ps])

    mkLinDefRef (cat,_) =
      (cat2fid cat 0,[0])

    addProd prods (fid,prod) =
      case IntMap.lookup fid prods of
        Just set -> IntMap.insert fid (prod:set) prods
        Nothing  -> IntMap.insert fid [prod]     prods

    cat2fid cat p =
      case [start | (cat',start,_,_) <- cnccats, cat == cat'] of
        (start:_) -> fid+p
        _         -> error "cat2fid"

    cat2arg c@(cat,[p]) = cat2fid cat p
    cat2arg c@(cat,ps ) =
      case Map.lookup c cs of
        Just fid -> fid
        Nothing  -> error "cat2arg"

mkRuleName rule =
  case ruleName rule of
	CFObj n _ -> n
	_         -> "_"
-}
