NameLayout

module NameLayout(LName(..),placeNL,spaceNL,modNL,marginNL,sepNL,hvAlignNL,marginHVAlignNL,nullNL,hBoxNL,hBoxNL',vBoxNL, vBoxNL',leafNL, NameLayout, nameF,
 listNF, nameLayoutF) where

--import NonStdTrace(trace)
import LayoutRequest
import NullF
import Spops
--import Command
--import Event
import FRequest
--import Xtypes
import EitherUtils(plookup)
import Data.Maybe(fromJust)
import Fudget
import Path
import Geometry
import Placers
import Spacers
--import Message
import ListF
import Loopthrough
import Cont
--import LayoutDir
import AlignP
--import Alignment
import Utils
import Maptrace
import AutoLayout
import ParF

type LName = String
newtype NameLayout = NL (MLNode LName) -- abstract

-- The layout structure datatype
type MLNode a = (Maybe LayoutRequest, LNode a)
data LNode a = 
      LNode Int Placer (Maybe (Rect -> [Rect])) [MLNode a]
    | LLeaf (LLeaf a) deriving Show

data LLeaf a = Name a | Req LayoutRequest deriving Show

type NPath = [Int]

-----------------------------------------------------------------------------------
-- Exported functions

placeNL :: Placer -> [NameLayout] -> NameLayout
placeNL lter ns = let dns = map deNL ns in
   buildnl $ LNode (length (filter (nothing.fst) dns)) lter Nothing dns

spaceNL :: Spacer -> NameLayout -> NameLayout
spaceNL = modNL . spacerP

modNL :: (Placer -> Placer) -> NameLayout -> NameLayout
modNL ltermod (NL (req,n)) = NL $ case n of
   LNode i lter f ls -> (req,LNode i (ltermod lter) f ls)
   LLeaf l -> 
      let lter = ltermod idP
          P lter' = lter in 
      case l of
	 Req r -> leafReq $ fst $ lter' $ [r]
	 _ -> (Nothing,LNode 1 lter Nothing [(req,n)])

marginNL = spaceNL . marginS
sepNL = spaceNL . sepS

hvAlignNL = spaceNL `oo` hvAlignS
marginHVAlignNL sep ha va = spaceNL (marginHVAlignS sep ha va)

hBoxNL = placeNL $ horizontalP
hBoxNL' d = placeNL $ horizontalP' d
vBoxNL = placeNL $ verticalP
vBoxNL' d = placeNL $ verticalP' d
leafNL name = buildnl $ LLeaf $ Name name
nullNL = NL $ leafReq $ plainLayout (Point 1 1) False False

nameF :: LName -> F a b -> F a b
nameF n = putMessageFu (Low (LCmd (LayoutName n))) . autoLayoutF

-- local

nothing Nothing = True
nothing _ = False

buildnl :: LNode LName -> NameLayout
buildnl x = NL (Nothing,x)
deNL (NL x) = x

leafReq :: LayoutRequest -> MLNode a
leafReq req = (Just req,LLeaf $ Req $ req)

listNF :: (Eq a, Show a) => [(a, F b c)] -> F (a, b) (a, c)  
listNF fs = listF [(t, nameF (show t) f) | (t, f) <- fs]

-- The main layout function
nameLayoutF :: NameLayout -> F a b -> F a b
nameLayoutF (NL ltree) (F fsp) =
    let layoutSP =
            getAllPNames (countLNames ltree) [] $ \pnames ->
            let (pathTable, ltree') = rebuildTree pnames [] ltree
	    in lSP pathTable ltree'
        lSP pt ltree = 
            let same = lSP pt ltree in
            getSP $ \msg ->
	    case msg of
	      -- A message from the fudget
	      Left (Low (path, LCmd (LayoutRequest lr))) ->
		  ctrace "nameLayoutF" lr $
		  let ltree' = updateTree path (pathlookup pt path) ltree lr
		  in case ltree' of 
			(Just lreq, _) -> --trace (show ltree') $
			    putSP (Right (Low ([], layoutRequestCmd lreq))) $
			    lSP pt ltree'
			_ -> lSP pt ltree'
	      Left x -> putSP (Right x) $ same
	      -- A message to the fudget
	      Right (Low (path, LEvt (LayoutPlace r))) ->
		  putsSP (map (Left. Low) $ traverseTree r ltree) $ same
	      Right x -> putSP (Left x) $ same
    in parF nullF $ F{-ff-} $ loopThroughRightSP layoutSP fsp
--  fix for autolayout

-----------------------------------------------------------------------------------
-- Local functions

-- Counts the number of named leafs in a layout structure
countLNames :: MLNode a -> Int
countLNames (_, LLeaf (Name _)) = 1
countLNames (_, LLeaf _) = 0
countLNames (_, LNode _ _ _ ns) = sum (map countLNames ns)

-- Traverses the layout structure, returning a mapping from leaf names to paths
getAllPNames :: Int -> [(LName, Path)] -> 
	     Cont (SP (Either (FCommand a) b) c) [(LName,Path)]
getAllPNames 0 pnames c = c pnames
getAllPNames n pnames c =
    waitForSP layoutName $ \pname ->
    getAllPNames (n-1) (pname:pnames) c
    where layoutName (Left (Low (path, LCmd (LayoutName name)))) = 
              Just(name, path)
          layoutName _ = Nothing

-- Rebuilds the layout structure. 
-- Returns also a mapping from ordinary paths to number paths.
rebuildTree :: [(LName, Path)] -> NPath -> MLNode LName ->
               ([(Path, NPath)], MLNode Path)
rebuildTree pnames np (_, LLeaf (Name name)) = 
    case lookup name pnames of
        Nothing -> error ("Couldn't find name "++ show name ++ 
			  " in (name, path) table.")
	Just path -> ([(path, np)], (Nothing, LLeaf $ Name path))
rebuildTree pnames np (_, (LLeaf (Req r))) = ([],(Just r, (LLeaf (Req r))))
rebuildTree pnames np (_, LNode c lter Nothing ns) =
    (concat ts, (Nothing, LNode c lter Nothing ns'))
    where (ts, ns') = unzip (zipWith (rebuildTree pnames) 
                                     (map ((np++) . (:[])) [1..]) ns)


-- Inserts layout requests in the layout structure.
-- Trigged by some fudget emitting a layout request
updateTree :: Path -> 
              Maybe NPath -> 
	      MLNode Path -> 
	      LayoutRequest -> 
              MLNode Path
updateTree path Nothing _ lr = 
    error ("Hmmm. Couldn't find path " ++ show path ++
    "in updateTree\nSomeone has probably forgotten to name a fudget.")
updateTree path (Just npath) lo lr = snd $ upd npath lo lr
    where upd _ (mlr, LLeaf (Name p)) lr = 
                 (nothing mlr, (Just lr, LLeaf (Name path)))
          upd (n:np) (mlr, LNode c lter@(P lter') mr ns) lr =
	      let (before, this:after) = splitAt (n-1) ns
	          (ready, child) = upd np this lr
	          c' = if ready then max (c-1) 0 else c
		  ns' = before ++ [child] ++ after
	      in if c' == 0 then
	             let (lreq, rectf) = lter' (map (fromJust . fst) ns')
		     in (nothing mlr, 
		         (Just lreq, LNode c' lter (Just rectf) ns'))
		 else
		     (False, (Nothing, LNode c' lter mr ns'))
          upd _ othernode _ = (False,othernode)

-- We have got a rectangle. Emit commands to all subfudgets saying how large
-- they should be. 
traverseTree :: Rect -> MLNode Path -> [TEvent]
traverseTree r (_, LLeaf (Name path)) = [(path, LEvt $ LayoutPlace r)]
traverseTree r (_, LLeaf _) = []
traverseTree r (_, LNode _ _ (Just rectf) ns) =
    concat (zipWith traverseTree (rectf r) ns) 

pathlookup table p = plookup (flip subPath p) table