module Sifflet.Data.Functoid
    (Functoid(..)
    , functoidName, functoidArgNames, functoidHeader
    , FunctoidLayout(..), flayout
    , flayoutBBox, flayoutSize, flayoutWidth, flayoutBottom
    , flayoutWiden)

where

import Data.Graph.Inductive as G

import Sifflet.Data.Geometry
import Sifflet.Language.Expr
import Sifflet.Data.TreeLayout

-- | A Functoid is either a FunctoidParts, representing a partially
-- defined function, or a (completely defined) Function.
-- A FunctoidParts represents an incompletely defined function,
-- which has a function name, argument names, and a list of graph nodes 
-- representing the function body which might not yet form
-- a complete expression tree, e.g., nodes might
-- be unconnected.

data Functoid = FunctoidParts {fpName :: String,
                               fpArgs :: [String],
                               fpNodes :: [G.Node]}
              | FunctoidFunc {fpFunc :: Function}
      deriving (Show)

functoidName :: Functoid -> String
functoidName (FunctoidParts {fpName = name}) = name
functoidName (FunctoidFunc function) = functionName function

functoidArgNames :: Functoid -> [String]
functoidArgNames (FunctoidParts {fpArgs = args}) = args
functoidArgNames (FunctoidFunc function) = functionArgNames function

functoidHeader :: Functoid -> String
functoidHeader f = unwords (functoidName f : functoidArgNames f)

-- | What is a FunctoidLayout?  You can think of it as a 
-- tree layout for a Frame,
-- or for a Functoid, or as a Forest of Tree Layouts!
-- For an Edit frame, it must be a forest, since the nodes are not
-- yet necessarily connected into a tree.
-- For a call frame, it will be a single TreeLayout (singleton list).

data FunctoidLayout = FLayoutTree (TreeLayout ExprNode)
                    | FLayoutForest [TreeLayout ExprNode] BBox

-- | Graphically lay out the Functoid.
-- mvalues: Nothing implies an uncalled frame,
--          Just values implies a called frame, even with 0 arguments.

flayout :: Style -> Functoid -> Env -> Maybe [Value] -> FunctoidLayout
flayout style functoid env mvalues =
    case functoid of
      FunctoidParts {} ->
          -- STUB: [] and bbox ***
          -- WELL: is this supposed to CREATE the tlo or RETRIEVE the tlo?
          -- Either way, it won't work without access to the WGraph!
          FLayoutForest [] (BBox (hpad style) (vpad style) 300 300)
      FunctoidFunc function ->
          let expr = functionBody function
              exprTree = case mvalues of
                           Nothing -> exprToTree expr
                           Just _values -> evalTree (exprToTree expr) env
              tlo = treeLayout style (exprNodeIoletCounter env) exprTree
          in FLayoutTree tlo

flayoutBBox :: FunctoidLayout -> BBox
flayoutBBox aflayout =
    case aflayout of
      FLayoutTree t -> layoutTreeBB t
      FLayoutForest _ bbox -> bbox

flayoutSize :: FunctoidLayout -> Size
flayoutSize = bbSize . flayoutBBox

flayoutWidth :: FunctoidLayout -> Double
flayoutWidth = bbWidth . flayoutBBox

flayoutBottom :: FunctoidLayout -> Double
flayoutBottom = bbBottom . flayoutBBox

flayoutWiden :: FunctoidLayout -> Double -> FunctoidLayout 
flayoutWiden aflayout minWidth =
    case aflayout of
      FLayoutTree t -> FLayoutTree (treeLayoutWiden t minWidth)
      FLayoutForest f bbox -> FLayoutForest f bbox -- STUB ***

instance Translate FunctoidLayout where
    translate dx dy fl =
        case fl of
          FLayoutTree t -> 
              FLayoutTree (translate dx dy t)
          FLayoutForest f b ->
              FLayoutForest (translate dx dy f) (translate dx dy b)