{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
-- | Browse the folders and files inside our root folder
module Language.Haskell.Reload.FileBrowser
  ( FSItem(..)
  , listFiles
  , getMIMEText
  , HiddenFiles(..)
  )where

import Data.Typeable (Typeable)
import Data.Char (toLower)
import Data.List (sort,isPrefixOf)
import System.Directory
import System.FilePath
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
-- import qualified Data.ByteString as B
import qualified Data.Map as DM
import Network.Mime
import Data.Aeson
import Control.Monad
import Data.Default

-- | A File System item, wrapping the path
data FSItem =
    -- | A directory
    Dir
      {fsiPath :: FilePath}
  | -- | A path
    File
      {fsiPath :: FilePath
      ,fsiMime :: MimeType}
  deriving (Show,Read,Eq,Typeable)

-- | Instance for sorting
instance Ord FSItem where
  (Dir _)  <= (File _ _) = True
  (File _ _) <= (Dir _)  = False
  f1       <= f2       = can f1 <= can f2
    where can = map toLower . takeFileName . fsiPath

-- | Json Instance
instance ToJSON FSItem where
    toJSON (Dir p)= object ["type" .= ("dir"::T.Text),"path" .= p]
    toJSON (File p m)= object ["type" .= ("file"::T.Text),"path" .= p,"mime" .= T.decodeUtf8 m]
instance FromJSON FSItem where
    parseJSON (Object m) = do
        t <- m .: "type"
        case (t :: T.Text) of
          "dir" ->
              Dir <$> (m .: "path")
          "file" ->
                File <$> (m .: "path")
                     <*> (T.encodeUtf8 <$> m .: "mime")
          _   -> mzero
    parseJSON _          = mzero

data HiddenFiles = ShowHidden | HideHidden
  deriving (Show,Read,Eq,Ord,Bounded,Enum,Typeable)

instance Default HiddenFiles where
  def = HideHidden

-- | List all files inside a given directory
listFiles :: FilePath -> FilePath -> HiddenFiles -> IO [FSItem]
listFiles root cd hf = do
  fs <- getDirectoryContents $ root </> cd
  let visible = filter (isVisible . takeFileName) fs
  sort <$> mapM tofs visible
  where
    isVisible :: FilePath -> Bool
    isVisible fn 
      | fn == "." = False
      | fn == ".." = False
      | "." `isPrefixOf` fn = hf == ShowHidden
      | otherwise = True 
    tofs fp = do
      let full = root </> cd </> fp
      isFile <- doesFileExist full
      can <- canonicalizePath full
      let rel=makeRelative root can
      return $ if isFile
        then File rel $ getMIME rel
        else Dir rel



-- | Extended mime map with haskell and YAML
extendedMimeMap :: DM.Map Extension MimeType
extendedMimeMap = foldr (uncurry DM.insert) defaultMimeMap
                    [("hs","text/x-haskell"),("lhs","text/x-haskell"),
                     ("yaml","text/x-yaml"),("cabal","text/x-cabal")
                    ]



-- | Get the mime type for the given file name
getMIME :: FilePath -> MimeType
getMIME = mimeByExt extendedMimeMap "text/plain" . T.pack
--case takeExtension fp of
--  ".hs"  -> "haskell"
--  ".lhs" -> "haskell"
--  _      -> "text"

-- | Get MIME type as text
getMIMEText :: FilePath -> T.Text
getMIMEText = T.decodeUtf8 . getMIME

-- -- | Get the file contents as a Text. Assumes UTF-8 encoding
-- getFileContents :: FilePath -> IO T.Text
-- getFileContents fp = do
--   bs <- B.readFile fp
--   return $ T.decodeUtf8 bs

-- -- -- | Set the file contents as a Text. Assumes UTF-8 encoding
-- -- setFileContents :: FilePath -> T.Text -> IO ()
-- setFileContents fp cnts =
--   B.writeFile fp $ T.encodeUtf8 cnts