-----------------------------------------------------------------------------
-- 
-- Module      :  Data.hs
-- Copyright   :  (c) Neil Mitchell 2007
-- License     :  
--
-- Maintainer  :  
-- Stability   :  unstable
-- Portability :  not portable, uses Gtk2Hs
--
-- Defines the core data structures for GuiHaskell.
--
-- Data passes around some global state. Data includes
-- EvalState, which holds the states of the individual
-- compilers that GuiHaskell can run.
--
-----------------------------------------------------------------------------

module Data (
	Data(..), Evaluator(..), Handles(..),
	empty, getHandles, setHandles, setCurrentFile,
	setupFonts, appendText, appendRed, applyEscape,
	promptCmd
	) where

import PropLang.Gtk
import PropLang.Variable

import Data.Map (Map)
import qualified Data.Map as M

import Control.Concurrent (ThreadId)
import System.IO (Handle)
import System.Process (ProcessHandle)
import Text.EscapeCodes

import Control.Monad
import Numeric

import Graphics.UI.Gtk hiding (Action, Window, ComboBox, MenuItem, TextView, ToolButton, FontButton, Event, onClicked, onChanged)


data Data = Data {
    -- Main Window and friends
      window :: Window
    , txtOut :: TextView
    , txtIn :: TextView
    , txtSelect :: TextEntry
    , sb :: StatusBar
    
    , tbRun :: ToolButton
    , tbStop :: ToolButton
    , tbRestart :: ToolButton
    , tbOpen :: ToolButton
    , tbRecent :: ToolButton
    , tbProfile :: ToolButton
    , tbPref :: ToolButton

    , cbCompiler :: ComboBox

    , fbFont :: FontButton

    , miFile :: MenuItem
    , miOpen :: MenuItem
    , miQuit :: MenuItem
    , miEdit :: MenuItem
    , miCut :: MenuItem
    , miCopy :: MenuItem
    , miPaste :: MenuItem
    , miView :: MenuItem
    , miTools :: MenuItem
    , miRun :: MenuItem
    , miProfile :: MenuItem
    , miPref :: MenuItem
    , miHelp :: MenuItem
    , miAbout :: MenuItem

    -- Preferences Dialog and friends
    , wndPref :: Window
    , txtExecutable :: TextEntry
    , txtProfCFlags :: TextEntry
    , txtProfRFlags :: TextEntry
    , tbClose :: ToolButton

    -- About dialog
    , wndAbout :: Window

    , running :: Var Bool -- is the code executing
    , filename :: Var (Maybe FilePath) -- the main file loaded
    , outputTags :: Var [String]
    , history :: Var ([String], [String]) -- command history

    -- Configuration variables
    , profCFlags :: Var String
    , profRFlags :: Var String
    , executable :: Var FilePath
    , font :: Var String

    --
    -- Stores the current evaluator and
    -- the states of background evaluators
    --
    -- When a new evaluator is chosen, the
    -- current evaluator is swapped into the list
    -- and the new evalutor is put into current
    , current :: Var Evaluator
    , states :: Var (Map Evaluator Handles)
    }

--
-- A data structure for storing the compiler-specific
-- details
--
data Handles = Handles {
    handle :: Handle,
    process :: ProcessHandle,
    outId :: ThreadId,
    errId :: ThreadId
    }

-- hack!
-- shouldn't matter as long as you use Var like an IORef
-- maybe ProcessHandle should instantiate Eq
instance Eq Handles where
    _ == _ = True

data Evaluator = Hugs | GHCi deriving (Show, Read, Eq, Ord)


-- So Main doesn't need to import Map
empty :: Map Evaluator Handles
empty = M.empty

-- Probably belongs in Evaluator.hs
promptCmd :: Evaluator -> String -> String
promptCmd Hugs xs = ":set -p\"" ++ xs ++ "\""
promptCmd GHCi xs = ":set prompt " ++ xs

-- Get the current evaluator
getHandles :: Data -> IO (Maybe Handles)
getHandles dat = do
    c <- getVar $ current dat
    s <- getVar $ states dat
    return $ M.lookup c s

-- Set the handles for the current evaluator
setHandles :: Data -> Maybe Handles -> IO ()
setHandles dat hndls = do
    c <- getVar $ current dat
    s <- getVar $ states dat
    case hndls of
	Nothing -> states dat -< M.delete c s
	Just x  ->
	    case M.lookup c s of
		Nothing -> states dat -< M.insert c x s
		Just _  -> states dat -< M.adjust (\_ -> x) c s

-- Set the currently open file
setCurrentFile :: Data -> Maybe FilePath -> IO ()
setCurrentFile dat path = do
    filename dat -< path

--
--
--
setupFonts :: Data -> IO ()
setupFonts dat@Data{txtOut=out,txtIn=inp} = do
    buf <- textviewBuffer out
    tags <- textBufferGetTagTable buf
    fontStr <- getVar $ (fbFont dat)!text
    
    mapM (addTags tags) [minBound..maxBound]

    fdesc <- fontDescriptionFromString fontStr

    widgetModifyFont (getTextViewRaw out) (Just fdesc)
    widgetModifyFont (getTextViewRaw inp) (Just fdesc)

    where
        addTags tags col = do
            let name = show col
                (r,g,b) = getColor col
                f x = let xs = showHex x "" in ['0' | length xs == 1] ++ xs
                css = "#" ++ f r ++ f g ++ f b
            
            tagFg <- textTagNew (Just $ "fg" ++ name)
            tagBg <- textTagNew (Just $ "bg" ++ name)
            textTagTableAdd tags tagFg
            textTagTableAdd tags tagBg
            set tagFg [textTagForeground := css]
            set tagBg [textTagBackground := css]

--
-- Append text to output area
--
appendText :: Data -> String -> IO ()
appendText dat@Data{txtOut=out} s = do
    buf <- textviewBuffer out
    end <- textBufferGetEndIter buf
    textBufferInsert buf end s
    
    len <- textBufferGetCharCount buf
    strt <- textBufferGetIterAtOffset buf (len - length s)
    end2 <- textBufferGetEndIter buf
    tags <- getVar (outputTags dat)
    mapM_ (f buf strt end2) tags
    where
        f buf strt end tag = textBufferApplyTagByName buf tag strt end


appendRed :: Data -> String -> IO ()
appendRed dat msg = do
    let tags = outputTags dat
    res <- getVar tags
    tags -< ["fgRed"]
    appendText dat msg
    tags -< res


applyEscape :: Data -> EscapeCode -> IO ()
applyEscape dat (FormatAttribute Normal) = outputTags dat -< []
applyEscape dat (FormatForeground Green) = outputTags dat -< ["fgGreen"]
applyEscape _ _ = return ()


--when_ :: Monad m => Bool -> m () -> m ()
--when_ b x = when b (x >> return ())
