module Graphics.Text.TrueType.FontFolders
    ( loadUnixFontFolderList
    , loadWindowsFontFolderList
    , fontFolders
    , findFont
    , FontCache( .. )
    , FontDescriptor( .. )
    , buildFontCache
    ) where

import Control.Applicative( (<$>), (<*>) )
{-import Control.DeepSeq( ($!!) )-}
{-import Data.Monoid( (<>) )-}
import System.Directory( getDirectoryContents
                       , doesDirectoryExist
                       )
import qualified Data.Map as M
import System.Environment( lookupEnv )
import System.FilePath( (</>) )
{-
import Text.XML.HXT.Core( runX
                        , readDocument
                        , withValidate
                        , withSubstDTDEntities
                        , no
                        , multi
                        , getChildren
                        , isElem
                        , hasName
                        , getText
                        , (>>>) )
-- -}

{-import qualified Control.Exception as E-}
import qualified Data.Text as T

import Graphics.Text.TrueType.FontType
import Graphics.Text.TrueType.Header
import Graphics.Text.TrueType.Name

{-catchAny :: IO a -> (E.SomeException -> IO a) -> IO a-}
{-catchAny = E.catch-}

{-
loadParseFontsConf :: IO [FilePath]
loadParseFontsConf = runX (
        readDocument [withValidate no, withSubstDTDEntities no]
                     "/etc/fonts/fonts.conf"
            >>> multi (isElem >>> hasName "dir" >>> getChildren >>> getText))
 
-- -}

loadUnixFontFolderList :: IO [FilePath]
loadUnixFontFolderList =
    -- Quick hack, need to change XML parser to a lighter one
    return ["/usr/share/fonts", "/usr/local/share/fonts", "~/.fonts"]
    {-
   catchAny (do conf <- loadParseFontsConf
                return $!! (</> "truetype") <$> conf)
            (const $ return [])
            --}

loadWindowsFontFolderList :: IO [FilePath]
loadWindowsFontFolderList = toFontFolder <$> lookupEnv "Windir"
  where toFontFolder (Just a) = [a </> "Fonts"]
        toFontFolder Nothing = []

loadOsXFontFolderList :: IO [FilePath]
loadOsXFontFolderList = return
    ["~/Library/Fonts"
    ,"/Library/Fonts"
    ,"/System/Library/Fonts"
    ,"/System Folder/Fonts"
    ]
    

fontFolders :: IO [FilePath]
fontFolders = do
    unix <- loadUnixFontFolderList
    win <- loadWindowsFontFolderList
    osx <- loadOsXFontFolderList
    return $ unix ++ win ++ osx

-- | A font descriptor is a key used to find a font
-- in a font cache.
data FontDescriptor = FontDescriptor
    { -- | The family name of the font
      _descriptorFamilyName :: T.Text
      -- | The desired style
    , _descriptorStyle :: FontStyle
    }
    deriving (Eq, Ord, Show)

-- | A font cache is a cache listing all the found
-- fonts on the system, allowing faster font lookup
-- once created
newtype FontCache = 
    FontCache (M.Map FontDescriptor FilePath)

-- | Look in the system's folder for usable fonts.
buildFontCache :: (FilePath -> IO (Maybe Font)) -> IO FontCache
buildFontCache loader = do
  folders <- fontFolders 
  found <- build [("", v) | v <- folders]
  return . FontCache
         $ M.fromList [(d, path) | (Just d, path) <- found]
  where
    descriptorOf Font { _fontHeader = Just hdr
                      , _fontNames = Just names} =
        Just $ FontDescriptor (fontFamilyName names)
                              (_fHdrMacStyle hdr)
    descriptorOf _ = Nothing

    build [] = return []
    build ((".", _):rest) = build rest
    build (("..", _):rest) = build rest
    build ((_, n):rest) = do
      isDirectory <- doesDirectoryExist n

      if isDirectory then do
        sub <- getDirectoryContents n 
        (++) <$> build [(s, n </> s) | s <- sub]
             <*> build rest
      else do
        f <- loader n
        case f of
          Nothing -> build rest
          Just fo -> ((descriptorOf fo, n) :) <$> build rest

findFont :: (FilePath -> IO (Maybe Font)) -> String -> FontStyle
         -> IO (Maybe FilePath)
findFont loader fontName fontStyle = do
    folders <- fontFolders 
    searchIn [("", v) | v <- folders]
  where
    fontNameText = T.pack fontName
    isMatching n (Font { _fontHeader = Just hdr
                       , _fontNames = Just names})
      | _fHdrMacStyle hdr == fontStyle &&
          fontFamilyName names == fontNameText = Just n
    isMatching _ _ = Nothing

    searchIn [] = return Nothing
    searchIn ((".", _):rest) = searchIn rest
    searchIn (("..", _):rest) = searchIn rest
    searchIn ((_, n):rest) = do
      isDirectory <- doesDirectoryExist n

      let findOrRest Nothing = searchIn rest
          findOrRest l = return l

      if isDirectory then do
        sub <- getDirectoryContents n 
        subRez <- searchIn [(s, n </> s) | s <- sub]
        findOrRest subRez
      else do
        font <- loader n
        findOrRest $ font >>= isMatching n