packages feed

edentv-4.1.0.0: EdenTvInteract.hs

{- The Eden Trace Viewer (or simply EdenTV) is a tool that can generate diagrams   to visualize the behaviour of Eden programs.
   Copyright (C) 2005-2010  Phillips Universitaet Marburg

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

-}

{-# OPTIONS_GHC -cpp #-}
-- JB first draft: commented out features missing in Gtk2hs-0.9.11
module EdenTvInteract where

import EdenTvType
import EdenTvBasic
import Graphics.UI.Gtk hiding (get, eventButton)
import Graphics.UI.Gtk.Gdk.Events
import qualified Graphics.UI.Gtk.Gdk.GC as G
import Graphics.UI.Gtk.Glade
-- JB: not in Gtk2hs-0.9.11 any more:
-- import Graphics.UI.Gtk.Mogul
import Graphics.UI.Gtk.ModelView.TreeView
import Graphics.UI.Gtk.ModelView.ListStore
import Graphics.Rendering.Cairo
import Data.Tree hiding (drawTree)
import Data.Char
import Control.Concurrent.MVar

import Data.List

import Debug.Trace

-- get dimensions of drawinarea:
--   visible Rect and the upper left and lower right corners to draw in
type Dim  = (Double,Double,Double,Double)
type DimV = (Int,Int,Int,Int)
getCorners :: DrawingArea -> IO (DimV,Dim)
getCorners pic = do
	win   <- widgetGetDrawWindow pic
	(w,h) <- widgetGetSize pic
	vR    <- drawableGetVisibleRegion win
	Rectangle vx vy vw vh   <- regionGetClipbox vR
	return ((vx, vy, vw, vh), (border + 50, border + 20,
		(realToFrac w) - 20, (realToFrac h) - 20))



buildConfMsg :: MVar ViewerState -> GladeXML -> DrawingArea -> Int -> IO ()
buildConfMsg st glade pic v = do 
        -- get access to the widgets:
        confM <- xmlGetWidget glade castToTreeView "confM"
        confP <- xmlGetWidget glade castToTreeView "confP"
        noteb <- xmlGetWidget glade castToNotebook "notebook1"

        -- Machine checkBoxes
       	mId   <- treeViewColumnNew
        treeViewColumnSetTitle mId "machine id"
        treeViewAppendColumn confM mId
        rendererMId <- cellRendererTextNew
        cellLayoutPackStart mId rendererMId True

        mSnt   <- treeViewColumnNew
        treeViewColumnSetTitle mSnt "show outgoing"
        treeViewAppendColumn confM mSnt
        rendererMSnt <- cellRendererToggleNew
        cellLayoutPackStart mSnt rendererMSnt True

      	mRcv   <- treeViewColumnNew
        treeViewColumnSetTitle mRcv "show incoming"
        treeViewAppendColumn confM mRcv
        rendererMRcv <- cellRendererToggleNew
        cellLayoutPackStart mRcv rendererMRcv True



        state <- readMVar st
        dataM <- listStoreNew (confMachines state)

        -- activate right tab view
        case v of
             1 -> notebookSetCurrentPage noteb 1
             3 -> notebookSetCurrentPage noteb 1
             0 -> notebookSetCurrentPage noteb 0
             _ -> notebookSetCurrentPage noteb 0


        -- update the model when the toggle buttons are activated
        on rendererMRcv cellToggled $ \pathStr -> do
           let (i:_) = stringToTreePath pathStr
           (mid,(inb,outb)) <- listStoreGetValue dataM i
           listStoreSetValue dataM i (mid,(not inb,outb))
        on rendererMSnt cellToggled $ \pathStr -> do
           let (i:_) = stringToTreePath pathStr
           (mid,(inb,outb)) <- listStoreGetValue dataM i
           listStoreSetValue dataM i (mid,(inb,not outb))

        cellLayoutSetAttributes mId rendererMId dataM $ \(id,_) -> [ cellText := (show id) ]
        cellLayoutSetAttributes mRcv rendererMRcv dataM $ \(_,(inb,_)) -> [ cellToggleActive := inb ]
        cellLayoutSetAttributes mSnt rendererMSnt dataM $ \(_,(_,outb)) -> [ cellToggleActive := outb ]

        treeViewSetModel confM dataM
	treeViewColumnsAutosize confM

        buttonStoreM <- xmlGetWidget glade castToButton "store_m"
        buttonAllInM <- xmlGetWidget glade castToButton "all_in_m"
        buttonAllOutM <- xmlGetWidget glade castToButton "all_out_m"
        buttonNoneInM <- xmlGetWidget glade castToButton "none_in_m"
        buttonNoneOutM <- xmlGetWidget glade castToButton "none_out_m"

        onClicked buttonStoreM (do oldState <- takeMVar st
                                   val      <- listStoreToList dataM
                                   --putStrLn ("saving: " ++ (show val))
                                   putMVar st (oldState {confMachines = val})
                                   widgetQueueDraw pic)

        onClicked buttonAllInM (do vals <- listStoreToList dataM
                                   let newVals = setAllIn True vals
                                   listStoreClear dataM
                                   sequence_ $ map (\x -> listStoreAppend dataM x) newVals)

        onClicked buttonAllOutM (do vals <- listStoreToList dataM
                                    let newVals = setAllOut True vals
                                    listStoreClear dataM
                                    sequence_ $ map (\x -> listStoreAppend dataM x) newVals)

        onClicked buttonNoneInM (do vals <- listStoreToList dataM
                                    let newVals = setAllIn False vals
                                    listStoreClear dataM
                                    sequence_ $ map (\x -> listStoreAppend dataM x) newVals)

        onClicked buttonNoneOutM (do vals <- listStoreToList dataM
                                     let newVals = setAllOut False vals
                                     listStoreClear dataM
                                     sequence_ $ map (\x -> listStoreAppend dataM x) newVals)



        -- Process checkBoxes
       	pId   <- treeViewColumnNew
        treeViewColumnSetTitle pId "process id"
        treeViewAppendColumn confP pId
        rendererPId <- cellRendererTextNew
        cellLayoutPackStart pId rendererPId True

        pSnt   <- treeViewColumnNew
        treeViewColumnSetTitle pSnt "show outgoing"
        treeViewAppendColumn confP pSnt
        rendererPSnt <- cellRendererToggleNew
        cellLayoutPackStart pSnt rendererPSnt True

      	pRcv   <- treeViewColumnNew
        treeViewColumnSetTitle pRcv "show incoming"
        treeViewAppendColumn confP pRcv
        rendererPRcv <- cellRendererToggleNew
        cellLayoutPackStart pRcv rendererPRcv True


        state <- readMVar st
        dataP <- listStoreNew (confProcesses state)

        -- update the model when the toggle buttons are activated
        on rendererPRcv cellToggled $ \pathStr -> do
           let (i:_) = stringToTreePath pathStr
           (mid,(inb,outb)) <- listStoreGetValue dataP i
           listStoreSetValue dataP i (mid,(not inb,outb))
        on rendererPSnt cellToggled $ \pathStr -> do
           let (i:_) = stringToTreePath pathStr
           (mid,(inb,outb)) <- listStoreGetValue dataP i
           listStoreSetValue dataP i (mid,(inb,not outb))

        cellLayoutSetAttributes pId rendererPId dataP $ \(id,_) -> [ cellText := (show id) ]
        cellLayoutSetAttributes pRcv rendererPRcv dataP $ \(_,(inb,_)) -> [ cellToggleActive := inb ]
        cellLayoutSetAttributes pSnt rendererPSnt dataP $ \(_,(_,outb)) -> [ cellToggleActive := outb ]

        treeViewSetModel confP dataP
	treeViewColumnsAutosize confP

        buttonStoreP <- xmlGetWidget glade castToButton "store_p"
        buttonAllInP <- xmlGetWidget glade castToButton "all_in_p"
        buttonAllOutP <- xmlGetWidget glade castToButton "all_out_p"
        buttonNoneInP <- xmlGetWidget glade castToButton "none_in_p"
        buttonNoneOutP <- xmlGetWidget glade castToButton "none_out_p"

        onClicked buttonStoreP (do oldState <- takeMVar st
                                   val      <- listStoreToList dataP
                                   --putStrLn ("saving: " ++ (show val))
                                   putMVar st (oldState {confProcesses = val})
                                   widgetQueueDraw pic)

        onClicked buttonAllInP (do vals <- listStoreToList dataP
                                   let newVals = setAllIn True vals
                                   listStoreClear dataP
                                   sequence_ $ map (\x -> listStoreAppend dataP x) newVals)

        onClicked buttonAllOutP (do vals <- listStoreToList dataP
                                    let newVals = setAllOut True vals
                                    listStoreClear dataP
                                    sequence_ $ map (\x -> listStoreAppend dataP x) newVals)

        onClicked buttonNoneInP (do vals <- listStoreToList dataP
                                    let newVals = setAllIn False vals
                                    listStoreClear dataP
                                    sequence_ $ map (\x -> listStoreAppend dataP x) newVals)

        onClicked buttonNoneOutP (do vals <- listStoreToList dataP
                                     let newVals = setAllOut False vals
                                     listStoreClear dataP
                                     sequence_ $ map (\x -> listStoreAppend dataP x) newVals)

        return ()


        where setAll :: (Bool, Bool) -> [(a,(Bool,Bool))] -> [(a,(Bool,Bool))]
              setAll to ((x,_):xs) = (x,to) : (setAll to xs)
              setAll _ [] = []
              setAllIn :: Bool -> [(a,(Bool,Bool))] -> [(a,(Bool,Bool))]
              setAllIn to ((x,(_,out)):xs) = (x,(to,out)) : (setAllIn to xs)
              setAllIn _ [] = []
              setAllOut :: Bool -> [(a,(Bool,Bool))] -> [(a,(Bool,Bool))]
              setAllOut to ((x,(i,_)):xs) = (x,(i,to)) : (setAllOut to xs)
              setAllOut _ [] = []




data MachineInfo = MachineInfo { machId :: String, runtime :: String, numProcesses :: String, numSent :: String, numRcvd :: String}
data ProcessInfo = ProcessInfo { machIdP :: String, procId :: String, runtimeP :: String, numThreads :: String, numSentP :: String, numRcvdP :: String}
data ThreadInfo = ThreadInfo { machIdT :: String, procIdT :: String, thrdId :: String, runtimeT :: String}

showTraceInfo :: Events -> String -> String -> GladeXML -> IO ()
showTraceInfo ((ms,ps,ts),mt,mxst,(msgs,_,heads,pt,_),(minT,maxT,_,_,_),_) filename statusString glade = do
	-- get access to the widgets:
	infoA   <- xmlGetWidget glade castToLabel    "infoA"
	infoM   <- xmlGetWidget glade castToTreeView "infoM"
	infoP   <- xmlGetWidget glade castToTreeView "infoP"
	infoT   <- xmlGetWidget glade castToTreeView "infoT"
	-- Global information:
	labelSetText infoA ("Tracefile: " ++ filename ++ "\n" ++
		map (commaToNewline) statusString ++ "\n\nProcesstree:\n" ++ drawTree pt)
	-- Machine information:
-- #if __GLASGOW_HASKELL__ < 606
	--skelM <- emptyListSkel
	mId   <- treeViewColumnNew
        treeViewColumnSetTitle mId "machine id"
        treeViewAppendColumn infoM mId
        rendererMId <- cellRendererTextNew
        cellLayoutPackStart mId rendererMId True

	mTime <- treeViewColumnNew
        treeViewColumnSetTitle mTime "runtime (s)"
        treeViewAppendColumn infoM mTime
        rendererMTime <- cellRendererTextNew
        cellLayoutPackStart mTime rendererMTime True
        
	mPrcs <- treeViewColumnNew
        treeViewColumnSetTitle mPrcs "processes"
        treeViewAppendColumn infoM mPrcs
        rendererMPrcs <- cellRendererTextNew
        cellLayoutPackStart mPrcs rendererMPrcs True

	--mTrds <- treeViewColumnNew
        --treeViewColumnSetTitle mTrds "threads"
        --treeViewAppendColumn infoM mTrds

	mSent <- treeViewColumnNew
        treeViewColumnSetTitle mSent "sent messages"
        treeViewAppendColumn infoM mSent
        rendererMSent <- cellRendererTextNew
        cellLayoutPackStart mSent rendererMSent True

	mRcvd <- treeViewColumnNew
        treeViewColumnSetTitle mRcvd "received messages"
        treeViewAppendColumn infoM mRcvd
        rendererMRcvd <- cellRendererTextNew
        cellLayoutPackStart mRcvd rendererMRcvd True

        dataM <- listStoreNew []
        -- newListStore skelM

	-- insert machine data
	inspectMachine ms dataM

        cellLayoutSetAttributes mId rendererMId dataM $ \row -> [ cellText := machId row ]
        cellLayoutSetAttributes mTime rendererMTime dataM $ \row -> [ cellText := runtime row ]
        cellLayoutSetAttributes mPrcs rendererMPrcs dataM $ \row -> [ cellText := numProcesses row ]
        cellLayoutSetAttributes mSent rendererMSent dataM $ \row -> [ cellText := numSent row ]
        cellLayoutSetAttributes mRcvd rendererMRcvd dataM $ \row -> [ cellText := numRcvd row ]

	treeViewSetModel infoM dataM
	treeViewColumnsAutosize infoM


	-- Process information:
	--skelP <- emptyListSkel
	pMid  <- treeViewColumnNew
        treeViewColumnSetTitle pMid "on machine"
        treeViewAppendColumn infoP pMid
        rendererPMid <- cellRendererTextNew
        cellLayoutPackStart pMid rendererPMid True

       	pId  <- treeViewColumnNew
        treeViewColumnSetTitle pId "process id"
        treeViewAppendColumn infoP pId
        rendererPId <- cellRendererTextNew
        cellLayoutPackStart pId rendererPId True

       	pTime  <- treeViewColumnNew
        treeViewColumnSetTitle pTime "runtime (s)"
        treeViewAppendColumn infoP pTime
        rendererPTime <- cellRendererTextNew
        cellLayoutPackStart pTime rendererPTime True

       	pTrds  <- treeViewColumnNew
        treeViewColumnSetTitle pTrds "threads"
        treeViewAppendColumn infoP pTrds
        rendererPTrds <- cellRendererTextNew
        cellLayoutPackStart pTrds rendererPTrds True

	pSent  <- treeViewColumnNew
        treeViewColumnSetTitle pSent "sent messages"
        treeViewAppendColumn infoP pSent
        rendererPSent <- cellRendererTextNew
        cellLayoutPackStart pSent rendererPSent True

       	pRcvd  <- treeViewColumnNew
        treeViewColumnSetTitle pRcvd "received messages"
        treeViewAppendColumn infoP pRcvd
        rendererPRcvd <- cellRendererTextNew
        cellLayoutPackStart pRcvd rendererPRcvd True

	dataP <- listStoreNew []
	
        inspectProcess (reverse ps) dataP 




        cellLayoutSetAttributes pMid rendererPMid dataP $ \row -> [ cellText := machIdP row ]
        cellLayoutSetAttributes pId rendererPId dataP $ \row -> [ cellText := procId row ]
        cellLayoutSetAttributes pTime rendererPTime dataP $ \row -> [ cellText := runtimeP row ]
        cellLayoutSetAttributes pTrds rendererPTrds dataP $ \row -> [ cellText := numThreads row ]
        cellLayoutSetAttributes pSent rendererPSent dataP $ \row -> [ cellText := numSentP row ]
        cellLayoutSetAttributes pRcvd rendererPRcvd dataP $ \row -> [ cellText := numRcvdP row ]

	treeViewSetModel infoP dataP
	treeViewColumnsAutosize infoP



	-- Thread information:
	--skelT <- emptyListSkel
        tMid  <- treeViewColumnNew
        treeViewColumnSetTitle tMid "on machine"
        treeViewAppendColumn infoT tMid
        rendererTMid <- cellRendererTextNew
        cellLayoutPackStart tMid rendererTMid True

        tPid  <- treeViewColumnNew
        treeViewColumnSetTitle tPid "in process"
        treeViewAppendColumn infoT tPid
        rendererTPid <- cellRendererTextNew
        cellLayoutPackStart tPid rendererTPid True

        tId  <- treeViewColumnNew
        treeViewColumnSetTitle tId "thread id"
        treeViewAppendColumn infoT tId
        rendererTId <- cellRendererTextNew
        cellLayoutPackStart tId rendererTId True

        tTime  <- treeViewColumnNew
        treeViewColumnSetTitle tTime "runtime (s)"
        treeViewAppendColumn infoT tTime
        rendererTTime <- cellRendererTextNew
        cellLayoutPackStart tTime rendererTTime True

	dataT <- listStoreNew []

        cellLayoutSetAttributes tMid rendererTMid dataT $ \row -> [ cellText := machIdT row ]
        cellLayoutSetAttributes tPid rendererTPid dataT $ \row -> [ cellText := procIdT row ]
        cellLayoutSetAttributes tId rendererTId dataT $ \row -> [ cellText := thrdId row ]
        cellLayoutSetAttributes tTime rendererTTime dataT $ \row -> [ cellText := runtimeT row ]

	inspectThread ts dataT


	treeViewSetModel infoT dataT
	treeViewColumnsAutosize infoT

	where	inspectMachine ((i,_,_,(p,s,r),evts):ms) store = do
			listStorePrepend store (MachineInfo {machId=("Machine " ++ show i), 
                                                             runtime=(formatFloat (getEventTime (head evts) - (getStartTime i mt))), 
                                                             numProcesses=(show p), 
                                                             numSent=(show s), 
                                                             numRcvd=(show r)})
			
			inspectMachine ms store 
		inspectMachine _ _  = return ()
		inspectProcess (((m,i),_,_,stat,evts):ps) store = do
			-- because of the processIDs being reused, there may be more then one
			-- StartProcess-/KillProcess-Events in evts. One fkt. to find them all:
			filterProcesses evts stat (getEventTime (head evts)) 0 ""
			where	filterProcesses :: [ProcessEvent] -> (Int,Int,Int) -> Seconds -> Int -> String -> IO ()
				filterProcesses (e:es) st@(t,s,r) killTime n l = case e of
					KillProcess time (t',s',r') -> filterProcesses es (t',s',r') time (n+1) l
					LabelProcess _ label -> filterProcesses es st killTime n label
					NewProcess time -> do

						listStoreAppend store (ProcessInfo {machIdP=(show m), 
                                                                                   procId=("Process " ++ show i ++ "/" ++ show n), 
                                                                                   runtimeP=(formatFloat (killTime - time)), 
                                                                                   numThreads= (show t), 
                                                                                   numSentP= (show s), 
                                                                                   numRcvdP= (show r)})
						filterProcesses es st killTime n l
					_ -> filterProcesses es st killTime n l
				filterProcesses _ _ _ _ _ = inspectProcess ps store
		inspectProcess _ _  = return ()
                
		inspectThread ((((m,p),i),evts):ts) store = do
		              
                        listStorePrepend store (ThreadInfo{ machIdT=(show m),
                                                            procIdT=(show p),
                                                            thrdId=("Thread " ++ show i),
                                                            runtimeT=(formatFloat (getEventTime (head evts) - (getEventTime (last evts))))})
			
			inspectThread ts store
		inspectThread _ _ = return ()

		commaToNewline c
			| c == ','  = '\n'
			| otherwise = c




handleDragnDrop :: MVar ViewerState -> DrawingArea -> Events -> Double -> Double -> IO ()
handleDragnDrop st pic _ mx my = do
        state <- readMVar st
        
        ((vx,vy,vw,vh),(ulx,uly,lrx,lry)) <- getCorners pic
	--widgetQueueDrawArea pic (floor mx - 100) vy 200 vh
	if mx > ulx && my > uly && mx < lrx && my < lry -- if  my > uly && my < lry
		then do	win <- widgetGetDrawWindow pic
                        let oldpixbuf = oldView state :: Maybe Pixbuf
                        gc <- G.gcNew win
                        --redraw old portion of screen
                        case oldpixbuf of
                             Just pb -> drawPixbuf win gc pb 0 0 0 0 (-1) (-1) RgbDitherNormal 0 0
                             Nothing -> return ()
			--drawWindowProcessUpdates win False
			renderWithDrawable win $ do
                                --colorWhite
				--rectangle (fromIntegral vx) lry (50) (uly-lry)
                                --fill
				colorRedA
				moveTo ((fromIntegral vx)+20) (my)
				lineTo ((fromIntegral vx)+30) (my)
				stroke
				
		else return ()

performDragnDrop v st pic _ mx my = do
  state <- takeMVar st
  ((vx,vy,vw,vh),(ulx,uly,lrx,lry)) <- getCorners pic
  let matrix = case v of
                2 -> matrixT state
		1 -> matrixP state
		_ -> matrixM state
      ySkip  = (lry - uly) / (fromIntegral (length matrix))
      index  = fromIntegral (floor (1 + ((my - uly) / ySkip)))
      oldIndex = head $ sort $ selRow state
  if null (selRow state)
   then putMVar st state
   else do
    if index == oldIndex
     then do
       if deleteSel state 
        then do
         (w,h) <- widgetGetSize pic
         win   <- widgetGetDrawWindow pic
         let pLines = selRow state
             skip'  = round ySkip
         putMVar st (state { selRow = [], deleteSel = False, noDND = False  })
         let pRects = map (\pLine -> Rectangle 0 (floor ((pLine-1)*ySkip + uly)-1) w (skip'+2)) pLines
         sequence_ (map (\r -> drawWindowInvalidateRect win r False) pRects)
        else putMVar st (state { deleteSel = False, noDND = False  })
     else if noDND state
             then putMVar st (state { deleteSel = False, noDND = False })
          else do
            case v of
              3 -> do
                  let arr = matrixGP state
                      ns  = selRow state
                      (m,a) = handleMove (floor index) ns arr
                  putMVar st (state {selRow = m, matrixGP = a, deleteSel = False})
              2 -> do
                 let arr = matrixT state
                     ns  = selRow state
                     (m,a) = handleMove (floor index) ns arr
                 putMVar st (state {selRow = m, matrixT = a, deleteSel = False})
                    
              1 -> do
                  let arr = matrixP state
                      ns  = selRow state
                      (m,a) = handleMove (floor index) ns arr
                  putMVar st (state {selRow = m, matrixP = a, deleteSel = False})
              _ -> do
                  let arr = matrixM state
                      ns  = selRow state
                      (m,a) = handleMove (floor index) ns arr
                  putMVar st (state {selRow = m, matrixM = a, deleteSel = False})
            return ()

handleMouseMove :: MVar ViewerState -> MVar EdenTvState -> Bool -> DrawingArea -> Events -> Double -> Double -> IO ()
handleMouseMove st edentvState hideStartupPhase pic (_,_,(maxStartupTimeInSeconds, _),_,(minT,maxT,maxST,_,_),_) mx my = do
	((vx,vy,vw,vh),(ulx,uly,lrx,lry)) <- getCorners pic
	widgetQueueDrawArea pic (floor mx - 100) vy 200 vh
	if  mx > (fromIntegral vx + ulx) && my > uly && my < lry
		then do	
			state <- readMVar st
			let useDiff = locTime state
			
			globalState <- readMVar edentvState
			let colorsMap = colors globalState
			
			-- if the startup-phase is not shown, adapt minT
			let minT'
				 | hideStartupPhase = maxStartupTimeInSeconds
				 | otherwise = minT
				 
			let time 
				| useDiff   = posToTime minT' (maxT+maxST) ulx lrx mx
				| otherwise = posToTime minT' maxT ulx lrx mx
			    text = formatFloat time
			    
			win <- widgetGetDrawWindow pic
			drawWindowProcessUpdates win False
			renderWithDrawable win $ do
				ext   <- textExtents text
				let dx = (\ (TextExtents _ _ w _ _ _) -> w) ext
				    lb = fromIntegral vx        + ulx + 5
				    rb = fromIntegral (vx + vw) - (border + 2) - dx
				    x0 = mx - (dx/2) -- centered at mouse position
				    x1 = max x0 lb -- not over left border
				    xm = min x1 rb -- not over right border
				getColor markerLine colorsMap
				moveTo mx (fromIntegral  vy + uly)
				lineTo mx (fromIntegral (vy + vh - 5))
				stroke
				moveTo xm (fromIntegral vy + 15)
				getColor markerLabel colorsMap
				showText text
		else return ()

removeElem :: (Eq a) => a -> [a] -> [a]
removeElem o (x:xs)
  | o == x    = xs
  | otherwise = x : removeElem o xs
removeElem _ [] = [] 

handleButtonPress :: DrawingArea -> Int -> MVar ViewerState -> Event -> Events -> IO Bool
handleButtonPress pic v st e ((m,p,t),mt,(mxs,mxst),_,(minT,maxT,maxST,_,maxD),_) = do
	(_,(ulx,uly,lrx,lry)) <- getCorners pic
	oldState <- readMVar st

	let	matrix  = case v of
                                3 -> matrixGP oldState
				2 -> matrixT oldState
				1 -> matrixP oldState
				_ -> matrixM oldState
		ySkip   = (lry - uly) / (fromIntegral (length matrix))
		(mx,my) = (eventX e, eventY e)
		line    = if v /= 3
                             then fromIntegral (floor (1 + ((my - uly) / ySkip)))
                             else let y = my - uly
                                  in findRow 0 y machineOffsets
                                     where findRow i y ((numP,numSkip):xs) 
                                             | y < (numP*procSkip + numSkip*10) = i
                                             | otherwise                        = findRow (i+1) y xs
                                           findRow i _ [] = i

                numProcs :: [(MachineID, Int)]
                numProcs = map (\(mId,_,_,(numP,_,_),_) -> (mId, numP)) m

                numM = fromIntegral $ length m
                procSkip = (lry - uly - ((numM-1)*10) )/ (fromIntegral (length (matrixP oldState)))            
                sortedMachines = [(numM+1) - (posToMachine pos (matrixGP oldState)) | pos <- [1..numM]]
                  where posToMachine :: Double -> [Double] -> Double
                        posToMachine pos sort = posAcc 1 pos sort
                          where posAcc i pos (s:ss)
                                  | pos == s  = i
                                  | otherwise = posAcc (i+1) pos ss
                                posAcc _ _ [] = 0

                machineOffsets = buildOffsets (0,0) sortedMachines
                  where buildOffsets :: (Double, Double) -> [Double] -> [(Double,Double)] 
                        buildOffsets (numP, numSkip) (mId:mIds) = (numP, numSkip) : (buildOffsets ((fromIntegral curP)+numP, numSkip+1) mIds)
                          where Just curP = lookup  (floor mId) numProcs
                        buildOffsets _ [] = []

		line'   = elemIndex line matrix -- get position of selLine in matrix
		elemIndex :: Eq a => a -> [a] -> Int
		elemIndex = ei 0 -- start search at index 0
			where	ei :: Eq a => Int -> a -> [a] -> Int
				ei i e (c:cs)
					| e == c = i -- elem found at index i
					| otherwise = ei (i+1) e cs -- not found yet
				ei _ _ [] = -1  -- elem not found

        --putStrLn $ "sort: " ++ (show (matrixGP oldState))                        
        --putStrLn $ "sortedMachines: " ++ (show sortedMachines)
        --putStrLn $ "machineOffsets: " ++ (show machineOffsets)
        --putStrLn $ "line: " ++ (show line)

	case eventButton e of
		LeftButton -> if mx > ulx && my > uly && mx < lrx && my < lry
			then do
				state <- takeMVar st
				(w,h) <- widgetGetSize pic
				win   <- widgetGetDrawWindow pic
				let pLines = selRow oldState
			    	    skip'  = round ySkip
                                    --pRects = map (\pLine -> (pLine, Rectangle 0 (floor ((pLine-1)*ySkip + uly)-1) w (skip'+2))) pLines
			    	    --rect0  = Rectangle 0 (floor ((pLine-1)*ySkip + uly)-1) w (skip'+2)
			    	    rect1  = Rectangle 0 (floor ((line-1)*ySkip  + uly)-1) w (skip'+2)
				if line `elem` pLines
					then do if Shift `elem` (Graphics.UI.Gtk.Gdk.Events.eventModifier e)
                                                   then putMVar st (state { selRow = removeElem line pLines, noDND = True })
                                                   else do 
                                                        putMVar st (state { deleteSel = True })
                                                        --putMVar st (state { selRow = [] })
                                                        --let pRects = map (\pLine -> Rectangle 0 (floor ((pLine-1)*ySkip + uly)-1) w (skip'+2)) pLines
                                                        --sequence_ (map (\r -> drawWindowInvalidateRect win r False) pRects)
						drawWindowInvalidateRect win rect1 False
					else do if Shift `elem` (Graphics.UI.Gtk.Gdk.Events.eventModifier e)
                                                   then putMVar st (state { selRow = (line:pLines), noDND = True })
                                                   else do
                                                        putMVar st (state { selRow = [line] })
                                                        let pRects = map (\pLine -> Rectangle 0 (floor ((pLine-1)*ySkip + uly)-1) w (skip'+2)) pLines
                                                        sequence_ (map (\r -> drawWindowInvalidateRect win r False) pRects)
						--drawWindowInvalidateRect win rect0 False
						drawWindowInvalidateRect win rect1 False
				return True
			else return True
		MiddleButton -> return True
		RightButton -> if mx > ulx && my > uly && my < lry
			then do
				let tData = (\ (_,evts)       -> evts) (t!!line')
				    pData = (\ (_,_,_,_,evts) -> evts) (p!!line')
				    mData = (\ (_,_,_,_,evts) -> evts) (m!!line')
				    (selMachine,selName) = case v of
					2 -> case t!!line' of (((m,p),t),_)   -> (m,'T': show m ++ ':': show p ++ ':': show t)
					1 -> case p!!line' of ((m,p),_,_,_,_) -> (m,'P': show m ++ ':': show p)
					0 -> case m!!line' of (m,_,_,_,_)     -> (m,'M': show m)
				    diffTime = (getStartTime selMachine mxst)
				    scaledTime :: Double -> String
				    scaledTime t = if locTime oldState
				    	then formatFloat (t + diffTime)
					else formatFloat (t - minT)
				    selTime = if locTime oldState
					then (posToTime minT (maxT+maxST) ulx lrx mx) - diffTime
					else (posToTime minT maxT ulx lrx mx) + minT
				    labelText = case v of
				    	2 -> case take 1 (dropWhile (\e -> getEventTime e > selTime) tData) of
						[NewThread s o]     -> "New thread " ++ selName ++
									"\nTime: " ++ (scaledTime s) ++
									"\noutport: " ++ show o
						[KillThread s]      -> "Kill thread " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[RunThread s]       -> "Run thread " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[SuspendThread s]   -> "Suspend thread " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[BlockThread s i r] -> "Block thread " ++ selName ++
									"\nTime: " ++ (scaledTime s) ++
									"\nInport: " ++ show i ++
									"\nReason: " ++ show r
						[GCThread s g a c l]-> "Garbage collection " ++ selName ++
									"\nTime: " ++ (scaledTime s) ++
									"\nGeneration: " ++ show g ++
									"\nAllocated: " ++ show a ++
									"\nCollected: "  ++ show c ++
									"\nLive data: " ++ show l
						_                   -> "No event to show"
					1 -> case take 1 (dropWhile (\e -> getEventTime e > selTime) pData) of
						[NewProcess s]       -> "New process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[KillProcess s _]    -> "Kill process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[IdleProcess s]      -> "Idle process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[RunningProcess s]   -> "Running process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[SuspendedProcess s] -> "Suspended process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[BlockedProcess s]   -> "Blocked process " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[GCProcess s g a c l]-> "Garbage collection " ++ selName ++
									"\nTime: " ++ (scaledTime s) ++
									"\nGeneration: " ++ show g ++
									"\nAllocated: " ++ show a ++
									"\nCollected: "  ++ show c ++
									"\nLive data: " ++ show l
						_                    -> "No event to show"
					0 -> case take 1 (dropWhile (\e -> getEventTime e > selTime) mData) of
						[StartMachine s]     -> "Start machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[EndMachine s]       -> "End machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[GCMachine s g a c l]-> "Garbage collection " ++ selName ++
									"\nTime: " ++ (scaledTime s) ++
									"\nGeneration: " ++ show g ++
									"\nAllocated: " ++ show a ++
									"\nCollected: "  ++ show c ++
									"\nLive data: " ++ show l
						[IdleMachine s]      -> "Idle machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[RunningMachine s]   -> "Running machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[SuspendedMachine s] -> "Suspended machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						[BlockedMachine s]   -> "Blocked machine " ++ selName ++
									"\nTime: " ++ (scaledTime s)
						_                    -> "No event to show"
				infoDlg  <- dialogNew
				windowSetTitle infoDlg "Event-info"
				windowSetPosition infoDlg WinPosCenterOnParent
				dialogAddButton infoDlg stockOk ResponseOk
				upper <- dialogGetUpper infoDlg
				infoText <- labelNew (Just labelText)
				boxPackStartDefaults upper infoText
				widgetShowAll infoDlg
				dialogRun infoDlg
				widgetDestroy infoDlg
				return True
			else return True


handleMove :: Int -> [Double] -> [Double] -> ([Double],[Double])
handleMove index moveRows allRows =  result 
  where orderedMoveRows = map (\row -> 1 + (position (floor row) allRows) ) $ sort moveRows
        len = length allRows
        lenMoved = length moveRows
        lenOther = length orderedOtherRows
        orderedOtherRows = sortBy sortOther $ filterOther 1 allRows

        sortOther :: Double -> Double -> Ordering
        sortOther a b 
          | (allRows!!((floor a)-1)) < (allRows!!((floor b)-1)) = LT
          | otherwise = GT

        moveUp = head moveRows > (fromIntegral index)

        placesBefore = if (index `mod` len) == 0
                          then 0
                          else (index - 1) `mod` len
        placesAfter = if (index `mod` len) == 0 
                         then (len - 1) `mod` len
                         else (len - placesBefore - lenMoved) `mod` len

        

        filterOther i (x:xs)
         | i `elem` orderedMoveRows = filterOther (i+1) xs
         | otherwise                = i : (filterOther (i+1) xs)
        filterOther _ [] = []
       
        process :: Int -> [Double] -> [Double]
        process i (x:xs)
          | (fromIntegral i) `elem` orderedMoveRows = (fromIntegral movPos ) : (process (i+1) xs)
          | otherwise                               = (fromIntegral movPos') : (process (i+1) xs)
          where movedRowOffset = position i orderedMoveRows
                movPos         = pos (index + movedRowOffset)  
                
                alteredRowOffset = position i orderedOtherRows
                movPos'          = if alteredRowOffset < placesBefore
                                      then pos (1+alteredRowOffset)
                                      else pos ( index + lenMoved + alteredRowOffset - placesBefore)
        process _ [] = []
        
        pos :: Int -> Int
        pos x = ((x-1) `mod` len) + 1

        position i (x:xs)
         | (fromIntegral i) == x    = 0
         | otherwise = 1 + (position i xs)
        position _ [] = 0

        selection = map (fromIntegral . pos) [index..(index+lenMoved-1)]

        result     = (selection, process 1 allRows)
  
handleMoveUp :: DrawingArea -> Int -> MVar ViewerState -> IO ()
handleMoveUp pic i st = do
	state <- takeMVar st
	((x,_,w,_),(ulx,uly,lrx,lry)) <- getCorners pic
	let ns = selRow state
	if not $ null ns
		then let n = head ns in case i of
                        3 -> do let arr   = matrixGP state
				    rows  = fromIntegral (length (arr))
				    ySkip = (lry - uly) / rows
                                    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))-1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixGP = a})
				widgetQueueDraw pic
			2 -> do let arr   = matrixT state
				    rows  = fromIntegral (length (arr))
				    ySkip = (lry - uly) / rows
                                    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))-1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixT = a}) 
				widgetQueueDraw pic
			1 -> do let arr   = matrixP state
				    rows  = fromIntegral (length (arr))
				    ySkip = (lry - uly) / rows
                                    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))-1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixP = a})
				widgetQueueDraw pic
			_ -> do let arr   = matrixM state
				    rows  = fromIntegral (length (arr))
				    ySkip = (lry - uly) / rows
                                    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))-1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixM = a})
				widgetQueueDraw pic
        	else putMVar st state

shiftDown,shiftUp :: (Eq a, Num a) => a -> [a] -> [a]
shiftDown max (x:xs)
	| x == max  =   1   : shiftDown max xs
	| otherwise = x + 1 : shiftDown max xs
shiftDown _ _ = []
shiftUp max (x:xs)
	| x == 1    =  max  : shiftUp max xs
	| otherwise = x - 1 : shiftUp max xs
shiftUp _ _ = []

handleMoveDown :: DrawingArea -> Int -> MVar ViewerState -> IO ()
handleMoveDown pic i st = do
	state <- takeMVar st
	(w,h) <- widgetGetSize pic
	((x,_,w,_),(ulx,uly,lrx,lry)) <- getCorners pic
	let ns = selRow state -- TODO hack
	if not $ null ns
		then case i of
                        3 -> do let arr   = (matrixGP state)
				    rows  = fromIntegral (length arr)
				    ySkip = (lry - uly) / rows
				    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))+1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixGP = a})
				widgetQueueDraw pic
			2 -> do let arr   = (matrixT state)
				    rows  = fromIntegral (length arr)
				    ySkip = (lry - uly) / rows
				    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))+1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixT = a})
				widgetQueueDraw pic
			1 -> do let arr   = (matrixP state)
				    rows  = fromIntegral (length arr)
				    ySkip = (lry - uly) / rows
				    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))+1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixP = a})
				widgetQueueDraw pic
			_ -> do let arr   = (matrixM state)
				    rows  = fromIntegral (length arr)
				    ySkip = (lry - uly) / rows
				    sortedRows = sort ns
                                    ind   = (floor (head sortedRows))+1
				    (m,a) = handleMove ind ns arr
				putMVar st (state {selRow = m, matrixM = a})
				widgetQueueDraw pic
		else putMVar st state