----------------------------------------------------------------------
-- |
-- Maintainer  : Peter Ljunglöf
-- Stability   : (stable)
-- Portability : (portable)
--
-- MCFG parsing, the active algorithm, optimized version
-- structure stolen from Krasimir Angelov's GF.Parsing.FCFG.Active
-----------------------------------------------------------------------------

module GF.Parsing.MCFG.FastActive (parse) where

import GF.Data.GeneralDeduction
import GF.Data.Assoc
import GF.Data.Utilities

import GF.Formalism.GCFG
import GF.Formalism.MCFG
import GF.Formalism.Utilities

import GF.Infra.Ident

import GF.Parsing.MCFG.Range
import GF.Parsing.MCFG.PInfo

import GF.System.Tracing

import Control.Monad (guard)

import GF.Infra.Print

import qualified Data.List as List
import qualified Data.Map  as Map
import qualified Data.Set  as Set
import Data.Array

----------------------------------------------------------------------
-- * parsing

-- parse :: (Ord c, Ord n, Ord l, Ord t) => String -> MCFParser c n l t
parse strategy pinfo starts =
    accumAssoc groupSyntaxNodes $
      [ ((cat, found), SNode fun (zip rhs rrecs)) |
        Final (Abs cat rhs fun) found rrecs <- listXChartFinal chart ]
    where chart = process strategy pinfo axioms emptyXChart
    
          -- axioms | isBU  strategy = terminal pinfo toks ++ initialScan pinfo toks
          axioms | isBU  strategy = initialBU pinfo
		 | isTD  strategy = initialTD pinfo starts

isBU  s = s=="b"
isTD  s = s=="t"

-- used in prediction
emptyChildren :: Abstract c n -> [RangeRec l]
emptyChildren (Abs _ rhs _) = replicate (length rhs) []

updateChildren :: Eq l => [RangeRec l] -> Int -> RangeRec l -> [[RangeRec l]]
updateChildren recs i rec = updateNthM update i recs
    where update rec' = do guard (null rec' || rec' == rec)
                           return rec

process :: (Ord c, Ord n, Ord l) => String -> MCFPInfo c n l Range -> [Item c n l] -> XChart c n l -> XChart c n l
process strategy pinfo []           chart = chart
process strategy pinfo (item:items) chart = process strategy pinfo items $! univRule item chart
  where
    univRule item@(Active abs found rng (Lin l syms) lins recs) chart 
        = case syms of
            Cat(c,r,d) : syms' -> 
                case insertXChart chart item c of
	          Nothing    -> chart
	          Just chart -> 
                      let items = -- predict topdown
                                  [ Active abs [] EmptyRange lin lins (emptyChildren abs) |
	     			    isTD strategy,
	     			    Rule abs (Cnc _ _ (lin:lins)) <- topdownRules pinfo ? c ] ++

                                  -- combine
                                  [ Active abs found rng'' (Lin l syms') lins recs' |
                                    Final _ found' _ <- lookupXChartFinal chart c,
                                    rng'  <- projection r found',
	         	            rng'' <- concatRange rng rng',
	         	            recs' <- updateChildren recs d found' ]
	     	      in process strategy pinfo items chart

            -- scan
	    Tok rng' : syms' -> 
                let items = [ Active abs found rng'' (Lin l syms') lins recs |
                              rng'' <- concatRange rng rng' ]
                in process strategy pinfo items chart

            -- complete
            [] -> case lins of
                    (lin':lins') -> univRule (Active abs ((l,rng):found) EmptyRange lin' lins' recs) chart
                    []           -> univRule (Final  abs (reverse ((l,rng):found))             recs) chart 

    univRule item@(Final abs@(Abs cat _ _) found' recs) chart =
      case insertXChart chart item cat of
        Nothing    -> chart
        Just chart -> 
            let items = -- predict bottomup
    			[ Active abs [] rng (Lin l syms') lins children |
                          isBU strategy,
			  Rule abs (Cnc _ _ (Lin l (Cat(c,r,d):syms') : lins)) <- leftcornerCats pinfo ? cat,
                          -- lin' : lins' <- rangeRestRec toks (Lin l syms' : lins),
                          rng <- projection r found',
                          children <- unifyRec (emptyChildren abs) d found' ] ++

                        -- combine
                        [ Active abs found rng'' (Lin l syms') lins recs' |
                          Active abs found rng (Lin l (Cat(c,r,d):syms')) lins recs <- lookupXChartAct chart cat,
                          rng'  <- projection r found',
                          rng'' <- concatRange rng rng',
                          recs' <- updateChildren recs d found' ] 
            in process strategy pinfo items chart

----------------------------------------------------------------------
-- * XChart

data XChart c n l = XChart !(AChart c n l) !(AChart c n l)
type AChart c n l = ParseChart (Item c n l) c

data Item   c n l = Active (Abstract c n) 
                           (RangeRec l)  
			   Range 
			   (Lin c l Range) 
			   (LinRec c l Range) 
			   [RangeRec l]
		  | Final (Abstract c n) (RangeRec l) [RangeRec l]
-- 		  | Passive c (RangeRec l)
		     deriving (Eq, Ord, Show)

emptyXChart :: (Ord c, Ord n, Ord l) => XChart c n l
emptyXChart = XChart emptyChart emptyChart

insertXChart (XChart actives finals) item@(Active _ _ _ _ _ _) c = 
  case chartInsert actives item c of
    Nothing      -> Nothing
    Just actives -> Just (XChart actives finals)

insertXChart (XChart actives finals) item@(Final _ _ _)       c =
  case chartInsert finals item c of
    Nothing     -> Nothing
    Just finals -> Just (XChart actives finals)

lookupXChartAct   (XChart actives finals) c = chartLookup actives c
lookupXChartFinal (XChart actives finals) c = chartLookup finals  c

listXChartAct   (XChart actives finals) = chartList actives
listXChartFinal (XChart actives finals) = chartList finals


----------------------------------------------------------------------
-- Earley --

-- called with all starting categories
initialTD :: (Ord c, Ord n, Ord l) => MCFPInfo c n l Range -> [c] -> [Item c n l]
initialTD pinfo starts = 
    [ Active abs [] (Range (0, 0)) lin' lins' (emptyChildren abs) |
      cat <- starts,
      Rule abs (Cnc _ _ (lin':lins')) <- topdownRules pinfo ? cat ]
       -- lin' : lins' <- rangeRestRec toks lins


----------------------------------------------------------------------
-- Kilbury --

initialBU :: (Ord c, Ord n, Ord l) => MCFPInfo c n l Range -> [Item c n l]
initialBU pinfo =
    [ Active abs [] EmptyRange lin' lins' (emptyChildren abs) |
      -- do tok <- aElems (inputToken toks)
      Rule abs (Cnc _ _ (lin':lins')) <- 
          concatMap snd (aAssocs (leftcornerTokens pinfo)) ++
          -- leftcornerTokens pinfo ? tok ++
          epsilonRules pinfo ]
       -- lin' : lins' <- rangeRestRec toks lins
