module Sifflet.UI.Types 
    (VPUI(..)
    , WinId, VPUIWindow(..)
    , vpuiFileChanged
    , vpuiUserEnvAList
    
    , vpuiInsertWindow
    , vpuiTryGetWindow
    , vpuiGetWindow
    , vpuiUpdateWindow
    , vpuiReplaceWindow
    , vpuiUpdateWindowIO
    , vpuiRemoveVPUIWindow
    
    , vpuiWindowLookupCanvas, vpuiWindowGetCanvas
    , vpuiWindowSetCanvas, vpuiWindowModCanvas 
    , vpuiWindowModCanvasIO
    
    , vpuiModCanvas, vpuiModCanvasIO
                   
    
    , vpuiWindowWindow
    , VPToolkit(..)
    , Toolbox(..)
    , Tool(..)
    , ToolContext(..)
    , CanvasToolOp
    , ToolOp
    , toToolOpVW 
    , Workspace(..)
    , FunctionEditor(..)
    , fedFunctionName
    , VCanvas(..)
    , Selection(..)
    , Dragging(..)
    )
where
import Data.Map as Map 
import Data.Graph.Inductive as G
import Graphics.UI.Gtk.Gdk.EventM (Modifier(..))
import Sifflet.Data.Geometry
import Sifflet.Data.TreeLayout
import Sifflet.Data.WGraph
import Sifflet.Language.Expr
import Sifflet.UI.Frame
import Sifflet.UI.LittleGtk
import Sifflet.UI.RPanel
import Sifflet.Util
data VPUI = VPUI {
      vpuiWindows :: Map WinId VPUIWindow,  
      vpuiToolkits :: [(String, VPToolkit)], 
                                             
      vpuiFilePath :: Maybe FilePath,       
      vpuiStyle :: Style,              
      vpuiInitialEnv :: Env,           
      vpuiGlobalEnv :: Env,   
      vpuiFileEnv :: Env      
                              
    }
vpuiFileChanged :: VPUI -> Bool
vpuiFileChanged vpui = vpuiGlobalEnv vpui /= vpuiFileEnv vpui
vpuiUserEnvAList :: VPUI -> [(String, Value)]
vpuiUserEnvAList vpui =
    let env' = vpuiGlobalEnv vpui 
        env = vpuiInitialEnv vpui
    in if length env == 1 && length env' == 1
       then assocs (Map.difference (head env') (head env))
       else errcats ["vpuiUserEnv: env lengths are not one",
                   "|env'|:", show (length env'),
                   "|env|:", show (length env)]
vpuiInsertWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
vpuiInsertWindow vpui winId vw =
    vpui {vpuiWindows = Map.insert winId vw (vpuiWindows vpui)}
vpuiTryGetWindow :: VPUI -> WinId -> Maybe VPUIWindow
vpuiTryGetWindow vpui winId = Map.lookup winId (vpuiWindows vpui)
vpuiGetWindow :: VPUI -> WinId -> VPUIWindow
vpuiGetWindow vpui winId = vpuiWindows vpui ! winId
vpuiReplaceWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
vpuiReplaceWindow vpui winId vpuiWin =
    let winMap = vpuiWindows vpui
        winMap' = insert winId vpuiWin winMap
    in vpui {vpuiWindows = winMap'}
vpuiUpdateWindow :: VPUI -> WinId -> (VPUIWindow -> VPUIWindow) -> VPUI
vpuiUpdateWindow vpui winId updater =
    let winMap = vpuiWindows vpui
        winMap' = adjust updater winId winMap
    in vpui {vpuiWindows = winMap'}
vpuiUpdateWindowIO :: WinId -> (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI
vpuiUpdateWindowIO winId updater vpui = do
  {
    let winMap = vpuiWindows vpui
        vw = winMap ! winId
  ; vw' <- updater vw
  ; let winMap' = insert winId vw' winMap
  ; return $ vpui {vpuiWindows = winMap'}
  }
vpuiRemoveVPUIWindow :: WinId -> VPUI -> VPUI
vpuiRemoveVPUIWindow winId vpui =
    let winMap = vpuiWindows vpui
        winMap' = delete winId winMap
    in vpui {vpuiWindows = winMap'}
data VPUIWindow = 
                  VPUIWorkWin Workspace Window
                | FunctionPadWindow Window [(String, RPanel)]
vpuiWindowWindow :: VPUIWindow -> Window
vpuiWindowWindow vw =
    case vw of
      VPUIWorkWin _ w -> w
      FunctionPadWindow w _ -> w
vpuiWindowLookupCanvas :: VPUIWindow -> Maybe VCanvas
vpuiWindowLookupCanvas vw =
    case vw of
      VPUIWorkWin ws _ -> Just (wsCanvas ws)
      _ -> Nothing
vpuiWindowGetCanvas :: VPUIWindow -> VCanvas
vpuiWindowGetCanvas vw =
    case vpuiWindowLookupCanvas vw of
      Nothing -> error "vpuiWindowGetCanvas: no canvas found"
      Just canvas -> canvas
vpuiWindowSetCanvas :: VPUIWindow -> VCanvas -> VPUIWindow
vpuiWindowSetCanvas vw canvas =
    case vw of
      VPUIWorkWin ws w -> VPUIWorkWin (ws {wsCanvas = canvas}) w
      _ -> error "vpuiWindowSetCanvas: not a workspace window"
vpuiWindowModCanvas :: VPUIWindow -> (VCanvas -> VCanvas) -> VPUIWindow
vpuiWindowModCanvas vw f =
    case vpuiWindowLookupCanvas vw of
      Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"
      Just canvas -> vpuiWindowSetCanvas vw (f canvas)
vpuiWindowModCanvasIO :: VPUIWindow -> (VCanvas -> IO VCanvas) -> IO VPUIWindow
vpuiWindowModCanvasIO vw f =
    case vpuiWindowLookupCanvas vw of
      Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"
      Just canvas -> 
          do
            { 
              canvas' <- f canvas
            ; return $ vpuiWindowSetCanvas vw canvas'
            }
vpuiModCanvas :: VPUI -> WinId -> (VCanvas -> VCanvas) -> VPUI
vpuiModCanvas vpui winId modCanvas = 
    let modWindow vw = vpuiWindowModCanvas vw modCanvas
    in vpuiUpdateWindow vpui winId modWindow
vpuiModCanvasIO :: VPUI -> WinId -> (VCanvas -> IO VCanvas) -> IO VPUI
vpuiModCanvasIO vpui winId modCanvas = 
    let modWindow vw = vpuiWindowModCanvasIO vw modCanvas
    in vpuiUpdateWindowIO winId modWindow vpui
type WinId = String
data Workspace = Workspace {wsBox :: VBox, 
                            wsCanvas :: VCanvas, 
                            wsButtonBar :: HBox,
                            wsStatusbar :: Statusbar}
data FunctionEditor = FunctionEditor {fedWindow :: Window,
                                      fedWinTitle :: String,
                                      fedFunction :: Function,
                                      fedCanvas :: VCanvas
                                      
                                     }
fedFunctionName :: FunctionEditor -> String
fedFunctionName = functionName . fedFunction
data VPToolkit = VPToolkit {toolkitName :: String,
                            toolkitWidth :: Int, 
                            toolkitRows :: [[Tool]]}
data Toolbox = Toolbox {toolboxFrame :: GtkFrame
                       , toolboxVBox :: VBox}
type ToolOp 
  = VPUI -> WinId -> ToolContext -> [Modifier] -> Double -> Double -> IO VPUI
type CanvasToolOp
  = VCanvas -> ToolContext -> [Modifier] -> Double -> Double -> IO VCanvas
data Tool = Tool {toolName :: String, 
                  
                  toolActivated :: VCanvas -> IO VCanvas,
                  
                  toolOp :: ToolOp
                 }
toToolOpVW :: CanvasToolOp -> ToolOp
toToolOpVW vcOp vpui winId toolContext mods x y = do
  {
    let vw = vpuiGetWindow vpui winId
        canv = vpuiWindowGetCanvas vw
  ; canv' <- vcOp canv toolContext mods x y
  ; let vw' = vpuiWindowSetCanvas vw canv'
  ; return $ vpuiReplaceWindow vpui winId vw'
  }
    
data ToolContext = TCWorkspace 
                 | TCCallFrame CanvFrame 
                 | TCEditFrame CanvFrame
                 | TCExprNode 
data VCanvas = VCanvas {
      vcLayout :: GtkLayout,
      vcStyle :: Style,
      vcGraph :: WGraph,
      vcFrames :: [CanvFrame],
      vcSize :: Size,
      
      vcMousePos :: (Double, Double),
      vcTool :: Maybe Tool,     
      vcActive :: Maybe Node,   
      vcSelected :: Maybe Selection, 
      vcDragging :: Maybe Dragging 
    }
data Selection = SelectionNode {selNode :: G.Node}
               | SelectionInlet {selNode :: G.Node,
                                 selInEdge :: WEdge} 
               | SelectionOutlet {selNode :: G.Node,
                                 selOutEdge :: WEdge} 
                 deriving (Eq, Read, Show)
data Dragging = Dragging { draggingNode :: G.Node,
                           draggingPosition :: Position
                           }
               deriving (Eq, Read, Show)