-- |
--   Copyright   :  (c) Sam Truzjan 2013
--   License     :  BSD3
--   Maintainer  :  pxqr.sta@gmail.com
--   Stability   :  experimental
--   Portability :  portable
--
--   Directory tree can be used to easily manipulate file layout info.
--
{-# LANGUAGE FlexibleInstances  #-}
{-# LANGUAGE TemplateHaskell    #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Data.Torrent.Tree
       ( -- * Directory tree
         DirTree (..)

         -- * Construction
       , build

         -- * Query
       , Data.Torrent.Tree.lookup
       , lookupDir
       , fileNumber
       , dirNumber
       ) where

import Data.ByteString as BS
import Data.ByteString.Char8 as BC
import Data.Foldable
import Data.List as L
import Data.Map  as M
import Data.Monoid

import Data.Torrent.Layout


-- | 'DirTree' is more convenient form of 'LayoutInfo'.
data DirTree a = Dir  { children :: Map ByteString (DirTree a) }
               | File { node     :: FileInfo a                 }
                 deriving Show

-- | Build directory tree from a list of files.
build :: LayoutInfo -> DirTree ()
build SingleFile {liFile = FileInfo {..}} = Dir
    { children = M.singleton fiName (File fi) }
  where
    fi = FileInfo fiLength fiMD5Sum ()
build MultiFile {..} = Dir $ M.singleton liDirName files
  where
    files = Dir $ M.fromList $ L.map mkFileEntry liFiles
    mkFileEntry FileInfo {..} = (L.head fiName, ent) -- TODO FIXME
      where
        ent = File $ FileInfo fiLength fiMD5Sum ()

--decompress :: DirTree () -> [FileInfo ()]
--decompress = undefined

-- TODO pretty print

-- | Lookup file by path.
lookup :: [FilePath] -> DirTree a -> Maybe (DirTree a)
lookup []        t      = Just t
lookup (p : ps) (Dir m) | Just subTree <- M.lookup (BC.pack p) m
                        = Data.Torrent.Tree.lookup ps subTree
lookup _         _      = Nothing

-- | Lookup directory by path.
lookupDir :: [FilePath] -> DirTree a -> Maybe [(ByteString, DirTree a)]
lookupDir ps d = do
  subTree <- Data.Torrent.Tree.lookup ps d
  case subTree of
    File _  -> Nothing
    Dir  es -> Just $ M.toList es

-- | Get total count of files in directory and subdirectories.
fileNumber :: DirTree a -> Sum Int
fileNumber File {..} = Sum 1
fileNumber Dir  {..} = foldMap fileNumber children

-- | Get total count of directories in the directory and subdirectories.
dirNumber :: DirTree a -> Sum Int
dirNumber File {..} = Sum 0
dirNumber Dir  {..} = Sum 1 <> foldMap dirNumber children