{-|
    Module exporting functionality for working with the
    file and directory panes of the file browser. It contains
    functionality for adding elements, operating upon elements
    and changing the view.
-}

module TreeViewOperations
    (
        operateSelection,
        createListing,
        upDirectory,
        openSelection,
        openDirectory
    )
where

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.EventM
import Buttons
import Components
import Util
import Types
import DirectoryOperations
import qualified SystemOperations as S
import TreeFunctions
import qualified Control.Exception as E
import System.FilePath ( (</>), splitFileName, takeExtension )
import System.Directory (doesDirectoryExist, getDirectoryContents, doesFileExist, getCurrentDirectory, setCurrentDirectory, getModificationTime )
import Control.Monad ( forM, forM_, filterM )
import Control.Monad.Trans ( liftIO )

{-|
    Function to operate upon a user selection. Determines where the
    selection is coming from, i.e. directory store or file store.
    Based on this it carries out a supplied action, and then refreshes
    the view.
-}

operateSelection :: ( [FilePath] -> IO ( ) ) -- ^ The action to be completed if the selection is within the directory store.
                 -> ( [FilePath] -> IO ( ) ) -- ^ The action to be completed if the selection is within the file store.
                 -> FileBrowser -- ^ The 'FileBrowser' to obtain the selection from.
                 -> IO ( )
operateSelection dirFunc fileFunc b = do
    toggleButtonGetActive ( switch b ) >>= ( \t ->
        case t of
            True -> selectOperate ( dirStore b ) ( dirTree b ) dirFunc
            False -> selectOperate ( fileStore b ) ( fileTree b ) fileFunc )
    getCurrentDirectory >>= ( \c -> refreshBrowser c b )

{-|
    Function to obtain complete an operation on a user selection. Obtains all
    the selected elements from the supplied store and carries out the function
    upon those elements.
-}
        
selectOperate :: ListStore StoreRow -- ^ The store to take the selection from.
              -> TreeView -- ^ The 'TreeView' containing the store.
              -> ( [FilePath] -> IO ( ) ) -- ^ The action to be completed on the selections.
              -> IO ( )
selectOperate store tree function = getMultipleSelection tree store >>= ( \r ->
    case r of
        Nothing -> return ( )
        ( Just r ) ->  function r )

{-|
    Function to open a selection made by the user. It firstly checks
    where the user selection(s) are and based on that either opens
    a directory or all files selected by the user.
-}

openSelection :: FileBrowser -- ^ The 'FileBrowser' containing the selection(s).
              -> IO ( )
openSelection browse = toggleButtonGetActive ( switch browse ) >>= ( \t ->
        case t of
            True -> openDirectory browse
            False -> getMultipleSelection ( fileTree browse ) ( fileStore browse ) >>= ( \name ->
                        case name of
                            Nothing -> return ( )
                            ( Just name ) -> openFile name >> return ( ) ) )

{-|
    Function to open a file from the file store.
-}

openFileStore :: FileBrowser -- ^ The 'FileBrowser' containing the selection.
              -> TreePath -- ^ The path to the selection.
              -> IO ( )
openFileStore browse path = listStoreGetValue ( fileStore browse ) ( head path ) >>= (\r -> (\d -> putStrLn(d++(name r)) >> openFile [d++(name r)] ) =<< getCurrentDirectory )

{-|
    Function to open a directory selected by the user. It gets the selected directory
    and concatenates it to the current directory and refreshes the 'FileBrowser' with
    the new path.
-}

openDirectory :: FileBrowser -- ^ The browser to be updated.
              -> IO ( )
openDirectory browse =
        getSingleSelection ( dirTree browse ) ( dirStore browse ) >>= ( \d ->
        case d of
                Nothing -> return ( )
                ( Just d ) -> getCurrentDirectory >>= ( \c -> refreshBrowser ( c ++ "/" ++ d ) browse ) )

{-|
    Creates the initial 'StoreRow's to be added
    to the ListStores. Creates all the cell entrys
    for each row.
-}    

createInitialRows :: [FilePath] -- ^ The list of file paths.
                  -> IO [StoreRow] -- ^ The list of resulting 'StoreRow'.
createInitialRows paths = forM paths $ \p -> do
    i <- createIcon p
    s <- getFileSize p
    t <- getModificationTime p
    pm <- permissionString p
    return ( StoreRow p i s ( show t ) pm )

{-|
    Create a default drag source for each of the
    'ListStore'.
-}

defaultDrag :: Int -- ^ Number to be associated with this listStore.
            -> SelectionTypeTag -- ^ Selection to set data with.
            -> Maybe ( DragSourceIface model row ) -- ^ The DragSource.
defaultDrag x tag = (Just DragSourceIface 
    {
        treeDragSourceRowDraggable = \_ _ -> return True,
        treeDragSourceDragDataGet = \_ (i:_) -> do
            selectionDataSet tag [x,i]
            return True,
        treeDragSourceDragDataDelete = \_ _ -> return True
    })
                                    
{-|
    Function to create a listing containing a supplied list of elements for display within
    the browser. The resulting 'TreeView' will have a column named with the supplied title.
-}

createListing :: (String,String) -- ^ The title for the column.
              -> ([FilePath],[FilePath]) -- ^ The list of elements to be displayed.
              -> IO ( TreeView, TreeView, ScrolledWindow, ScrolledWindow, ListStore StoreRow, ListStore StoreRow ) -- ^ The resulting 'TreeView', 'ScrolledWindow' and 'ListStore'
createListing (dtitle,ftitle) (ds,fs) = do
        compTypeTag <- atomNew ""
        dir <- scrolledWindowNew Nothing Nothing
        files <- scrolledWindowNew Nothing Nothing
        dtext <- createInitialRows ds
        ftext <- createInitialRows fs
        listF <- listStoreNewDND ftext ( defaultDrag 1 compTypeTag )
                    Nothing
        listD <- listStoreNewDND dtext ( defaultDrag 0 compTypeTag )
                    (Just DragDestIface 
                    {
                        treeDragDestRowDropPossible = \_ _ -> do
                            s <- selectionDataGet compTypeTag
                            case s :: Maybe [Int] of
                                Just (x:xs:_) -> return True
                                _ -> return False,
                        treeDragDestDragDataReceived = \store (i:_) -> do
                            s <- selectionDataGet compTypeTag
                            case s :: Maybe [Int] of
                                Just (x:y:_) -> do
                                    liftIO $ ( do 
                                                dirVal <- listStoreGetValue store i 
                                                dragVal <- listStoreGetValue ( if (x==0) then store else listF ) y
                                                S.moveFile ( name dragVal ) ( name dirVal )
                                                (dirs, files) <- viewDirectory =<< getCurrentDirectory
                                                refreshPane dirs store
                                                refreshPane files listF )
                                    return True
                                _ -> return False
                          })
        tViewD <- treeViewNewWithModel listD
        tViewF <- treeViewNewWithModel listF
        imageCell <- cellRendererPixbufNew
        textCell <- cellRendererTextNew
        sizetext <- cellRendererTextNew
        modtext <- cellRendererTextNew
        permtext <- cellRendererTextNew
        tl <- targetListNew
        targetListAdd tl targetTreeModelRow [TargetSameApp] 0
        forM_ [(dtitle, tViewD,listD,dir),(ftitle,tViewF,listF,files)] $ \(t,tv,s,sw) -> do
            sel <- treeViewGetSelection tv
            treeSelectionSetMode sel SelectionMultiple
            treeViewEnableModelDragDest tv tl [ActionMove]
            treeViewEnableModelDragSource tv [Button1] tl [ActionMove]
            nCol <- treeViewColumnNew
            sCol <- treeViewColumnNew
            mCol <- treeViewColumnNew
            pCol <- treeViewColumnNew
            forM [(nCol,t),(sCol,"Size"),(mCol,"Last Modified"),(pCol,"Permissions")] $ \(col,title) -> do
                treeViewColumnSetTitle col title
                treeViewColumnSetResizable col True
            cellLayoutPackStart nCol imageCell False
            forM_ [(nCol,textCell),(sCol,sizetext),(mCol,modtext),(pCol,permtext)] $ \(c,b) ->
                cellLayoutPackStart c b False
            cellLayoutSetAttributes nCol textCell s $ \r -> [cellText := name r]
            cellLayoutSetAttributes nCol imageCell s $ \r -> [cellPixbuf := icon r] 
            cellLayoutSetAttributes sCol sizetext s $ \r -> [cellText := size r]
            cellLayoutSetAttributes mCol modtext s $ \r -> [cellText := lastAccess r]
            cellLayoutSetAttributes pCol permtext s $ \r -> [cellText := permissions r]
            forM_ [nCol,sCol,mCol,pCol] $ \col ->
                treeViewAppendColumn tv col
            containerAdd sw tv
            
        return ( tViewD, tViewF, dir, files, listD, listF )

{-|
        Function to move up one directory.
         i.e. If currently in \"\/home\/michael\" change to \"\/home\"
-}

upDirectory :: FileBrowser -- ^ The 'FileBrowser' to update.
            -> IO ( )
upDirectory b = do
        cur <- getCurrentDirectory
        let ( super, _ ) = splitFileName cur
        setCurrentDirectory super
        refreshBrowser super b
