diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,37 +1,58 @@
 # pgdl
 
-a terminal user interface program that downloads a video (or a file) from webpage and xdg-open it immediately.
-(Linux: xdg-open "video.mp4", MACOSX: open "video.mp4")
+## What can this program do?
 
-Client: Only for Linux and MACOSX.
-Server: Only for nginx's file download page.
+pgdl is a program for viewing and accessing directory listing webpage in terminal.
 
-The program will sort files and folders by date while files in nginx's download page just sort by name.
+Browsing files on directory listings like
+[this](https://www.kernel.org/pub/linux/) and
+[this](https://nixos.org/channels/)
+is often annoying and hard to find the files we want.
 
+pgdl provids a simple interface for browsing and downloading the files in web-engine-generated directory listings.
+
 ## Installing
 
+by cabal:
 ```shell
 cabal update  
 cabal install pgdl
 ```
 
-If there is an error occured when installing vty-ui, you may need 
+via nix:
 ```shell
-cabal install vty-ui -f no-tests 
+nix-env -iA nixos.haskellPackages.pgdl
 ```
 
-to skip build of testing executable.
+## example usage
 
+```shell
+pgdl https://www.kernel.org/pub/linux/
+```
+or simply type pgdl if you have set servpath attribute in the config file.
+
 ## Shortcut keys
 
-q - quit  
-s - list files that similar with the highlighted file
-Key Right - show the detailed information of the highlighted file
-Key Left - go back to the previous page  
-Enter - xdg-open the file  
-/ - open a input box to enter a keyword to search  
-n - sort the current list by name  
-v - show the filess already in the disk  
-u - show the filess not in the disk  
-h - shortcut keys' help page
+'/' for file searching
 
+press Enter to confirm or to download the selected file
+
+press 'q' to quit the program
+
+press 'd' to delete currently selected file (which have been downloaded)
+
+
+## Config file
+
+If you want to access webpage that uses basic authentication, you should at least set
+*username* attribute in config file.
+(if password is not set, you need to enter password manually when program launch)
+
+(~/.pgdl)
+```bash
+# example: 
+username = "jack"      # should be set if the webpage have basic authentication
+password = "mypassw"   # optional
+servpath = "example.org/videodir/" # default server location, optional
+localdir = "/home/jack/Downloads/" # where to store downloaded files, optional
+```
diff --git a/pgdl.cabal b/pgdl.cabal
--- a/pgdl.cabal
+++ b/pgdl.cabal
@@ -1,43 +1,52 @@
 
 name:                pgdl
-version:             8.5
+version:             9.0
 license:             PublicDomain
 license-file:        LICENSE
-author:              sifmelcara
-maintainer:          sifmelcara
+author:              mingchuan
+maintainer:          mingchuan
 category:            Network
 build-type:          Simple
 extra-source-files:  README.md
 cabal-version:       >=1.10
-description:         a terminal user interface program downloads video (or a file) from nginx file index page and xdg-open it.
-                     (Linux: xdg-open "video.mp4", MACOSX: open "video.mp4")
-                     .
-                     Client: Only for Linux and MACOSX.
-                     Server: Only for nginx's file download page.
+description:         pgdl is a program for viewing and accessing directory listing webpage in terminal.
                      .
-                     The program will sort the files and folders by date while files in nginx's download page just sort by name.
+                     Browsing files on directory listings like this and this is often annoying and hard to find the files we want.
                      .
-                     For shortcut keys, please visit <https://github.com/sifmelcara/pgdl> or press h in the program.
+                     pgdl provids a simple interface for browsing and downloading the files in web-engine-generated directory listings.
 
-synopsis:            simply download a video (or a file) from a webpage and xdg-open it.
 
+synopsis:            browse directory listing webpages and download files from them.
+
 source-repository head
   type:     git
   location: https://github.com/sifmelcara/pgdl
 
 executable pgdl
   hs-source-dirs:      src
-  main-is:             Pgdl.hs
-  other-modules:       NameAlgo, Chkcfg, FetchHtml, Getconfig,
-                       Log, RealWorld, PrsVid, Video, GenStat, 
-                       CrtInf, AskScene, UI
-  build-depends:       base == 4.*, process, 
-                       directory, bytestring,
-                       tagsoup, network-uri, 
-                       vty, text, vty-ui >= 1.9,
-                       configurator >= 0.3.0.0, HTTP,
-                       filepath, Cabal, array,
-                       http-conduit < 3.0.0
-  ghc-options:         -threaded -O2
+  main-is:             Main.hs
+  other-modules:       Cache, DownloadInterface, Local
+                       Networking, Utils, Configure,
+                       EntryAttrViewer, Types
+  build-depends:       base == 4.*,
+                       tagsoup,
+                       directory-listing-webpage-parser,
+                       brick, vty, data-default,
+                       vector,
+                       text, bytestring,
+                       unix,
+                       conduit, conduit-extra,
+                       http-conduit, resourcet,
+                       configurator >= 0.3,
+                       directory >= 1.2.3.0,
+                       Cabal,
+                       filepath,
+                       transformers,
+                       process,
+                       filepath,
+                       time,
+                       binary
+
+  ghc-options:         -threaded
   default-language:    Haskell2010
 
diff --git a/src/AskScene.hs b/src/AskScene.hs
deleted file mode 100644
--- a/src/AskScene.hs
+++ /dev/null
@@ -1,62 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module AskScene where
-
-import Graphics.Vty.Widgets.All
-import Graphics.Vty.Input.Events
-
-import Control.Monad
-
-data AskScene = AskScene { sceneWidget :: Widget (Bordered Padded)
-                         , playHand :: Handlers AskScene
-                         , downHand :: Handlers AskScene
-                         , quitHand :: Handlers AskScene
-                         , remvHand :: Handlers AskScene
-                         }
-
-newAskScene :: IO (AskScene, Widget FocusGroup)
-newAskScene = do
-    playB <- newButton "No"
-    downB <- newButton "Yes"
-    quitB <- newButton "Cancel"
-    remvB <- newButton "Remov"
-    buttonBox <- mkwIO playB <++> mkwIO downB <++> mkwIO quitB <++> mkwIO remvB
-    b <- do
-        bx <- plainText "    Download it again?" <--> return buttonBox
-        setBoxSpacing bx 1
-        withPadding (padAll 1) bx
-    fg <- newFocusGroup
-    mapM_ (addToFocusGroup fg . buttonWidget) [playB, downB, quitB, remvB]
-    ui <- withBorderedLabel "File Exists!" =<< bordered b
-    [phs, dhs, qhs, rhs] <- replicateM 4 newHandlers
-    let sce = AskScene { sceneWidget = ui
-                       , playHand = phs
-                       , downHand = dhs
-                       , quitHand = qhs
-                       , remvHand = rhs
-                       }
-    onButtonPressed playB $ \_ -> 
-        fireEvent sce (return . playHand) sce
-    onButtonPressed downB $ \_ -> 
-        fireEvent sce (return . downHand) sce
-    onButtonPressed quitB $ \_ -> 
-        fireEvent sce (return . quitHand) sce
-    onButtonPressed remvB $ \_ ->
-        fireEvent sce (return . remvHand) sce
-    setFocusGroupNextKey fg KRight []
-    setFocusGroupPrevKey fg KLeft []
-    return (sce, fg)
-    where mkwIO = return . buttonWidget
-
-onScePlay :: AskScene -> Handler AskScene -> IO ()
-onScePlay = addHandler (return . playHand)
-
-onSceDown :: AskScene -> Handler AskScene -> IO ()
-onSceDown = addHandler (return . downHand)
-
-onSceQuit :: AskScene -> Handler AskScene -> IO ()
-onSceQuit = addHandler (return . quitHand)
-
-onSceRemv :: AskScene -> Handler AskScene -> IO()
-onSceRemv = addHandler (return . remvHand)
-
diff --git a/src/Cache.hs b/src/Cache.hs
new file mode 100644
--- /dev/null
+++ b/src/Cache.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Cache
+where
+
+import qualified Data.Text as T
+import Data.Text (Text)
+import Data.Binary
+import Data.Maybe
+import Data.Time.LocalTime
+import Data.Time.Clock.POSIX
+import System.Directory
+import Text.HTML.DirectoryListing.Type
+
+import Configure
+import Types 
+import Local
+
+-- | this conversion may lose accuracy?
+instance Binary LocalTime where
+    put = put . toRational . utcTimeToPOSIXSeconds . localTimeToUTC utc
+    get = utcToLocalTime utc . posixSecondsToUTCTime . fromRational <$> get
+
+instance Binary Entry where
+    put e = do
+        put $ visibleName e
+        put $ href e
+        put $ lastModified e
+        put $ fileSize e
+    get = do
+        v <- get
+        h <- get
+        l <- get
+        f <- get
+        return Entry { visibleName = v
+                     , href = h
+                     , lastModified = l
+                     , fileSize = f
+                     }
+        
+writeCache :: [Entry] -> IO ()
+writeCache es = do
+    p <- getCacheFileLocation
+    encodeFile p es
+
+readCache :: IO (Maybe [DNode])
+readCache = do
+    p <- getCacheFileLocation
+    lcd <- T.unpack . fromMaybe "" <$> getLocaldir
+    doesFileExist p >>= \case
+        False -> return Nothing
+        True -> decodeFileOrFail p >>= \case
+                    Right d -> do
+                        dnodes <- mapM (toDNode lcd) d
+                        return $ Just dnodes
+                    Left _ -> return Nothing
+    where
+    toDNode :: String -> Entry -> IO DNode
+    toDNode lcd e
+        | isDirectory e = return $ Directory e noData
+        | otherwise = do
+            downloaded <- isFileDownloaded (decodedName e) lcd
+            return $ File e "offline mode" downloaded
+    noData = error "offline mode"
+    
diff --git a/src/Chkcfg.hs b/src/Chkcfg.hs
deleted file mode 100644
--- a/src/Chkcfg.hs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-module Chkcfg (chkcfg) where
-
-import System.Directory
-import System.FilePath
-import Control.Monad
-
-chkcfg :: IO ()
-chkcfg = do
-    flp <- fmap (</> ".pgdl") getHomeDirectory
-    fle <- doesFileExist flp
-    unless fle $ do
-              writeFile flp defcfg
-              error "please config the file \"~/.pgdl\" first!"
-    where defcfg = unlines [ ""
-                           , "# example: "
-                           , "# username = \"jack\" "
-                           , "# password = \"mypassw\" "
-                           , "# servpath = \"example.org/videodir/\" "
-                           , "# localdir = \"/home/jack/Downloads/\" "
-                           , ""
-                           , "# username and localdir is optional."
-                           , ""
-                           , "username = \"\""
-                           , "password = \"\""
-                           , "servpath = \"\""
-                           , "localdir = \"\""
-                           , ""
-                           ]
-
diff --git a/src/Configure.hs b/src/Configure.hs
new file mode 100644
--- /dev/null
+++ b/src/Configure.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Configure 
+where
+
+import qualified Data.Text as T
+import Data.Text (Text)
+
+import qualified Data.Configurator as C
+import qualified Data.Configurator.Types as C
+
+import System.FilePath.Posix
+import System.Directory
+
+getConfig :: IO (Maybe C.Config)
+getConfig = do
+    home <- getUserDocumentsDirectory
+    let cfgFile = home </> ".pgdl"
+    doesFileExist cfgFile >>= \case
+        False -> return Nothing
+        True -> Just <$> C.load [C.Required cfgFile]
+    
+getUsername :: IO (Maybe Text)
+getUsername =
+    getConfig >>= \case
+        Nothing -> return Nothing
+        Just cfg -> C.lookup cfg "username" 
+
+
+getPassword :: IO (Maybe Text)
+getPassword = 
+    getConfig >>= \case
+        Nothing -> return Nothing
+        Just cfg -> C.lookup cfg "password" 
+
+getLocaldir :: IO (Maybe Text)
+getLocaldir =
+    getConfig >>= \case
+        Nothing -> return Nothing
+        Just cfg -> C.lookup cfg "localdir" 
+
+-- | return Nothing if there are no servpath or config file do not exist
+getServpath :: IO (Maybe Text)
+getServpath = do
+    cfg <- getConfig
+    case cfg of
+        Nothing -> return Nothing
+        Just c -> fmap unify <$> C.lookup c "servpath"
+    where
+    -- this is not a good approach, improve it later
+    unify :: Text -> Text
+    unify p
+        | "http://" `T.isPrefixOf` p = p
+        | "https://" `T.isPrefixOf` p = p
+        | otherwise = "http://" `T.append` p
+
+-- | This function also creates the cache directory
+-- if it doesn't exists.
+getCacheFileLocation :: IO FilePath
+getCacheFileLocation = do
+    cdir <- getXdgDirectory XdgCache ""
+    createDirectoryIfMissing True cdir
+    let cacheFile = cdir </> "pgdl"
+    return cacheFile
+
diff --git a/src/CrtInf.hs b/src/CrtInf.hs
deleted file mode 100644
--- a/src/CrtInf.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module CrtInf where
-
-import Video
-
-import qualified Data.Text as T
-
-crtInfPg :: Video -> T.Text
-crtInfPg vid = T.unlines ["", "",
-                          "File name: " `app` (spl . getName) vid,
-                          "File size: " `app` getSize vid,
-                          "File date: " `app` getDate vid,
-                          "File link: " `app` (spl . getLink) vid
-                         ]
-    where app = T.append
-          spl = T.intercalate "\n" . T.chunksOf 60 
-          getSize v
-            | isVid v = vidSize v
-            | otherwise = "I'm a folder"
-          getDate v
-            | isVid v = vidDate v
-            | otherwise = fldDate v
-          getLink v
-            | isVid v = vidLink v
-            | otherwise = fldLink v
-
diff --git a/src/DownloadInterface.hs b/src/DownloadInterface.hs
new file mode 100644
--- /dev/null
+++ b/src/DownloadInterface.hs
@@ -0,0 +1,179 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase#-}
+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+
+module DownloadInterface (downloadInterface)
+where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Text.Encoding
+import Data.Conduit
+import Data.Conduit.Binary
+import Data.Default
+import Control.Monad
+import Control.Monad.Trans.Resource 
+import Control.Monad.IO.Class
+import Control.Concurrent
+import qualified Control.Concurrent.Chan as C
+import Network.HTTP.Conduit
+import System.Posix.Files
+import System.Process
+import Distribution.System
+
+import qualified Graphics.Vty as V
+import qualified Brick.Main as M
+import qualified Brick.Types as T
+import qualified Brick.Widgets.Edit as E
+import qualified Brick.Widgets.Border as B
+import qualified Brick.Widgets.ProgressBar as P
+import qualified Brick.Widgets.Center as C
+import qualified Brick.AttrMap as A
+import Brick.Types (Widget)
+import Brick.Widgets.Core
+import Brick.Util (on)
+
+import qualified Configure as Conf
+import Networking
+
+--                                 bytes already downloaded
+data DownloadState = DownloadState Integer | FinishedState
+                   | UserInput DownloadState E.Editor
+                   --          ^ download progress
+
+data DEvent = VtyEvent V.Event
+            | UpdateFinishedSize Integer
+            | DownloadFinish
+
+-- | Maybe we should try to get file size by other method,
+-- that would be more accurate and reliable.
+downloadInterface :: NetworkResource -> 
+                     Text -> -- ^ url (relative)
+                     Text -> -- ^ file path (may be a absolute path)
+                     Integer -> -- ^ filesize in bytes
+                     Bool -> -- ^ is the download already finished?
+                     IO ()
+downloadInterface nr url filepath filesize alreadyFinished = do
+    eventChan <- C.newChan 
+    unless alreadyFinished . void . forkIO $ download nr url filepath (C.writeChan eventChan)
+    let
+        initialState :: DownloadState 
+        initialState = if alreadyFinished
+                       then FinishedState
+                       else DownloadState 0
+        theApp =
+            M.App { M.appDraw = drawUI
+                  , M.appChooseCursor = M.neverShowCursor
+                  , M.appHandleEvent = appEvent
+                  , M.appStartEvent = return
+                  , M.appAttrMap = const theMap 
+                  , M.appLiftVtyEvent = VtyEvent
+                  }
+        appEvent :: DownloadState -> DEvent -> T.EventM (T.Next DownloadState)
+        appEvent ds@(DownloadState doneB) de = case de of
+            VtyEvent e -> case e of
+                V.EvKey V.KEsc [] -> M.halt ds
+                V.EvKey (V.KChar 'q') _ -> M.halt ds
+                V.EvKey (V.KChar 'o') [] -> M.continue $ UserInput ds (E.editor "command" (str.unlines) (Just 1) "")
+                V.EvKey V.KEnter [] -> do
+                    liftIO $ filepath `openBy` ""
+                    M.continue ds
+                ev -> M.continue ds
+            UpdateFinishedSize b -> M.continue . DownloadState $ b
+            DownloadFinish -> M.continue FinishedState
+        appEvent FinishedState de = case de of
+            VtyEvent e -> case e of
+                V.EvKey (V.KChar 'q') _ -> M.halt FinishedState
+                V.EvKey (V.KChar 'o') [] -> M.continue $ UserInput FinishedState (E.editor "command" (str.unlines) (Just 1) "")
+                V.EvKey V.KEnter [] -> do
+                    liftIO $ filepath `openBy` ""
+                    M.halt FinishedState
+                    -- ^ file opened, we can quit download interface now
+                ev -> M.continue FinishedState
+            _ -> error "received non vty event after FinishedState was reached."
+        appEvent (UserInput st ed) de = case de of
+            VtyEvent e -> case e of
+                V.EvKey V.KEsc [] -> M.continue st
+                V.EvKey V.KEnter [] -> do
+                    liftIO $ filepath `openBy` concat (E.getEditContents ed)
+                    case st of
+                        DownloadState _ -> M.continue st
+                        FinishedState -> M.halt FinishedState
+                        _ -> error "unexpected state in UserInput state."
+                ev -> do
+                    newEd <- T.handleEvent ev ed
+                    M.continue $ UserInput st newEd
+            UpdateFinishedSize b -> M.continue (UserInput (DownloadState b) ed)
+            DownloadFinish -> M.continue (UserInput FinishedState ed)
+        theMap = A.attrMap V.defAttr [ (P.progressCompleteAttr, V.black `on` V.cyan)
+                                     , (P.progressIncompleteAttr, V.black `on` V.white)
+                                     , ("input box", V.black `on` V.blue)
+                                     ]
+        drawUI :: DownloadState -> [Widget]
+        drawUI (DownloadState bytes) = [vBox [bar, note]]
+            where
+            bar = C.vCenter . C.hCenter $ P.progressBar Nothing (fromIntegral bytes / fromIntegral filesize)
+            note = C.vCenter . C.hCenter . str $ unlines [ "press Enter to open the file (even if its download has not finished)"
+                                                         , "press 'o' to specify which program should be used to open the file."
+                                                         ]
+        drawUI FinishedState = [ui]
+            where
+            ui = C.vCenter . C.hCenter . str $ unlines [ "The file is ready for open."
+                                                       , "press Enter to open the file, or press 'q' to return to file listing"
+                                                       , "press 'o' to specify which program should be used to open the file."
+                                                       ]
+        drawUI (UserInput (UserInput _ _) _) = error "unexpected state in UserInput state."
+        drawUI (UserInput s ed) = (padTop (T.Pad 1) . vLimit 5 $ ask) : drawUI s
+            where
+            ask = C.vCenter . C.hCenter . 
+                  hLimit 50 . vLimit 5 . 
+                  B.borderWithLabel (str "please input a program name") .
+                  forceAttr "input box" . 
+                  hLimit 40 $
+                  E.renderEditor ed
+    M.customMain (V.mkVty Data.Default.def) eventChan theApp initialState
+    return ()
+
+openBy :: Text -> String -> IO ()
+openBy file "" = case buildOS of
+                    OSX -> openBy file "open"
+                    Linux -> openBy file "xdg-open"
+                    _ -> error "don't know how to open file in this OS"
+openBy file cmd = void $
+    case buildOS of
+        OSX   -> runCommand $ cmd ++ " " ++ addq filepath ++ " "
+        Linux -> runCommand $ "nohup " ++ cmd ++ " " ++ addq filepath ++ " &>/dev/null &"
+        _     -> error "don't know how to open file in this OS"
+    where
+    filepath = T.unpack file
+    addq :: String -> String
+    addq s = "\"" ++ s ++ "\""
+    
+download :: NetworkResource -> 
+            Text -> -- ^ url
+            Text -> -- ^ filepath
+            (DEvent -> IO ()) -> IO ()
+download nr url tFilepath tell = do
+    -- this implementation needs to be change
+    -- since some filename contains characters that cannot be represented by String
+    let
+        filepath = T.unpack tFilepath
+        checkFile :: IO ()
+        checkFile = forever $ do
+            threadDelay $ 1000000 * 1
+            exist <- fileExist filepath
+            when exist $ do
+                s <- fileSize <$> getFileStatus filepath
+                tell . UpdateFinishedSize . fromIntegral $ s
+    checkerThreadID <- forkIO checkFile
+    let (req, manager) = nr url
+    -- use http and ResumableSource in conduit for constant memory usage
+    runResourceT $ do
+        response <- http req manager
+        let body = responseBody response
+        body $$+- sinkFile filepath
+        liftIO $ do
+            killThread checkerThreadID
+            tell DownloadFinish
+    return ()
+
diff --git a/src/EntryAttrViewer.hs b/src/EntryAttrViewer.hs
new file mode 100644
--- /dev/null
+++ b/src/EntryAttrViewer.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module EntryAttrViewer (entryAttrViewer)
+where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+import Control.Monad
+
+import qualified Graphics.Vty as V
+import qualified Brick.Main as M
+import qualified Brick.Types as T
+import qualified Brick.Widgets.ProgressBar as P
+import qualified Brick.Widgets.Center as C
+import qualified Brick.Widgets.Core as C
+import qualified Brick.AttrMap as A
+import Brick.Types (Widget)
+import Brick.Widgets.Core
+import Brick.Util (on)
+
+import Types
+import Text.HTML.DirectoryListing.Type
+
+import qualified Utils as U
+
+entryAttrViewer :: DNode -> IO ()
+-- | Temporary use File to process Directory...
+entryAttrViewer (Directory entry _) = entryAttrViewer (File entry "directory no link." False)
+entryAttrViewer (File entry url downloaded) = do
+    let
+        initialState :: () 
+        initialState = ()
+        theApp =
+            M.App { M.appDraw = drawUI
+                  , M.appChooseCursor = M.neverShowCursor
+                  , M.appHandleEvent = appEvent
+                  , M.appStartEvent = return
+                  , M.appAttrMap = const theMap 
+                  , M.appLiftVtyEvent = id
+                  }
+        appEvent :: () -> V.Event -> T.EventM (T.Next ())
+        appEvent () e = case e of
+            V.EvKey V.KEsc [] -> M.halt ()
+            V.EvKey (V.KChar 'q') [] -> M.halt ()
+            V.EvKey V.KLeft [] -> M.halt ()
+            ev -> M.continue ()
+        theMap = A.attrMap V.defAttr [ (P.progressCompleteAttr, V.black `on` V.cyan)
+                                     , (P.progressIncompleteAttr, V.black `on` V.white)
+                                     ]
+        drawUI :: () -> [Widget]
+        drawUI _ = [ui]
+            where
+            ui = C.vCenter . C.hCenter .
+                 C.hLimit U.terminalWidth . C.txt $ info
+    M.defaultMain theApp initialState
+    return ()
+    where
+    info = T.unlines $
+           zipWith (\describ s -> T.intercalate "\n" . 
+                                  U.cutTextByDisplayLength U.terminalWidth $
+                                  describ `T.append` ": " `T.append` s
+                   )
+           [ "visible name" 
+           , "decoded name" 
+           , "url" 
+           , "file size" 
+           , "downloaded"
+           ]
+           [ visibleName entry
+           , decodedName entry
+           , url
+           , maybe "directory" (T.pack . U.friendlySize) . fileSize $ entry
+           , if downloaded then "Yes" else "No"
+           ]
+
diff --git a/src/FetchHtml.hs b/src/FetchHtml.hs
deleted file mode 100644
--- a/src/FetchHtml.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module FetchHtml where
-  
-import Getconfig
-
-import Network.URI
-import Data.Text.Encoding
-import Data.Maybe
-import System.FilePath
-import Control.Applicative
-import qualified Data.Text as T
-import Network.HTTP.Conduit
-import Data.ByteString.Lazy
---import bytestring?
-
-fetchHtml :: IO T.Text
-fetchHtml = getTextContent =<< genURI
-
-fetchFld :: T.Text -> IO T.Text
-fetchFld fnt = do
-    uri <- (</> fn) <$> genURI
-    getTextContent uri
-    where fn = T.unpack fnt
-
-genURI :: IO String
-genURI = do
-    usrn <- getUsername
-    usrp <- getPassword
-    servp <- getServpath
-    return $ "http://" ++ usrn ++ ":" ++ usrp ++ "@" ++ servp
-
-getTextContent :: String -> IO T.Text
-getTextContent u = do
-    bs <- simpleHttp u
-    let t = decodeUtf8 . toStrict $ bs
-    return t
-    
-
diff --git a/src/GenStat.hs b/src/GenStat.hs
deleted file mode 100644
--- a/src/GenStat.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
-
-module GenStat (genStat, waitingBar) where
-
-import Video
-import RealWorld
-
-import qualified Data.Text as T
-
-genStat :: Video -> IO T.Text
-genStat vid@(Video _ _ size date) = do
-    st <- toStr $ downloaded vid
-    return $ T.concat [ spc 3 , date
-                      , spc 5 , size
-                      , spc 20, st
-                      , spc 30
-                      ]
-    where spc :: Int -> T.Text
-          spc i = T.replicate i " "
-
-genStat (Folder _ _ date) = 
-    return $ T.concat [ spc 3
-                      , date
-                      , spc 5
-                      , "I'm a folder"
-                      , spc 99
-                      ]
-    where spc i = T.replicate i " "
-
-waitingBar :: T.Text
-waitingBar = "   Waiting for data..." `T.append` T.replicate 99 " "
-
-toStr :: IO Bool -> IO T.Text
-toStr b = b >>= \case
-    True -> return "Downloaded"
-    _    -> return "Not Downloaded"
-
-
diff --git a/src/Getconfig.hs b/src/Getconfig.hs
deleted file mode 100644
--- a/src/Getconfig.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
-
-module Getconfig where
-
-import qualified Data.Configurator as C
-import qualified Data.Configurator.Types as C
-
-getConfig :: IO C.Config
-getConfig = C.load [C.Required "$(HOME)/.pgdl"]
-
-getUsername :: IO String
-getUsername = do
-    cfg <- getConfig
-    C.lookup cfg "username" >>= \case
-        Nothing -> noArg
-        Just s -> return s
-
-getPassword :: IO String
-getPassword = do 
-    cfg <- getConfig
-    C.lookup cfg "password" >>= \case
-        Nothing -> noArg
-        Just s -> return s
-
-getServpath :: IO String
-getServpath = do
-    cfg <- getConfig
-    C.lookup cfg "servpath" >>= \case
-        Nothing -> noArg
-        Just s -> return s
-
-getLocaldir :: IO String
-getLocaldir = do
-    cfg <- getConfig
-    C.lookup cfg "localdir" >>= \case
-        Nothing -> noArg
-        Just s -> return s
-
-noArg :: IO String
-noArg = error "please correct the config file (~/.pgdl)."
-
diff --git a/src/Local.hs b/src/Local.hs
new file mode 100644
--- /dev/null
+++ b/src/Local.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Local
+where
+
+import System.Directory
+import System.FilePath.Posix
+import Data.Text (Text)
+import qualified Data.Text as T
+
+import Configure
+
+-- | determine whether a file is in specified directory
+isFileDownloaded :: Text -> -- ^ file name
+                    String -> -- ^ local directory
+                    IO Bool
+isFileDownloaded fn path = doesFileExist $ path </> T.unpack fn
+    
+deleteFile :: Text -> -- ^ file name (may be a absolute path)
+              IO ()
+deleteFile = removeFile . T.unpack
+
diff --git a/src/Log.hs b/src/Log.hs
deleted file mode 100644
--- a/src/Log.hs
+++ /dev/null
@@ -1,44 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
-
-module Log where
-
-import Video
-
-import System.Directory
-import System.FilePath
-
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
-
-encVid :: [Video] -> T.Text
-encVid vs = T.intercalate "\n" $ map genName vs
-    where genName v
-            | isVid v = vidName v
-            | otherwise = fldName v
-
-decVid :: T.Text -> [Video]
-decVid = map (\t -> Video t "" "" "") . T.lines
-
-logname :: FilePath
-logname = ".pgdl.cache"
-
-writeVid :: [Video] -> IO ()
-writeVid vs = do
-    hdir <- getHomeDirectory
-    T.writeFile (hdir </> logname) $ encVid vs 
-
-readVid :: IO [Video]
-readVid = do
-    hdir <- getHomeDirectory
-    let absdir = hdir </> logname
-    doesFileExist absdir >>= \case
-        True -> do
-            dat <- T.readFile (hdir </> logname)
-            return $ decVid dat
-        False -> return dlnVid
-
-dlnVid :: [Video]
-dlnVid = [Video "Downloading..." "" "" ""]
-
-
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,214 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+
+module Main
+where
+
+import qualified Data.Text as T
+import qualified Data.Vector as V
+import Data.Text (Text)
+import Data.Maybe
+import DownloadInterface
+import Control.Monad
+import Control.Monad.IO.Class
+import System.FilePath
+import System.Environment
+import System.IO
+import Text.HTML.DirectoryListing.Type
+
+import qualified Graphics.Vty as V
+import qualified Brick.Main as M
+import qualified Brick.Types as T
+import qualified Brick.Widgets.List as L
+import qualified Brick.Widgets.Center as C
+import qualified Brick.Widgets.Edit as E
+import qualified Brick.AttrMap as A
+import Brick.Types (Widget)
+import Brick.Widgets.Core
+import Brick.Util (on)
+
+import EntryAttrViewer
+import Utils
+import qualified Configure as Conf
+import Cache
+import Types
+import Local
+import Networking
+import qualified Utils as U
+
+-- |                    father            contents                 
+data MainState = LState (Maybe MainState) (L.List DNode)
+               | SearchState MainState (L.List DNode) E.Editor
+
+
+main :: IO ()
+main = do
+    let askPassword = do
+            putStr "please input password: "
+            hFlush stdout
+            hSetEcho stdin False
+            pass <- getLine
+            hSetEcho stdin True
+            putChar '\n'
+            return $ T.pack pass
+    (dNodes, nr) <- getArgs >>= \case
+                        ["--offline"] -> readCache >>= \case
+                            Nothing -> error "no offline data or data corrupted."
+                            Just dlst -> return (dlst, error "no network resource, offline mode.")
+                        online -> do
+                            (rootUrl, up) <- case online of
+                                                [] -> Conf.getServpath >>= \case
+                                                    Nothing -> error "example usage: pgdl https://www.kernel.org/pub/"
+                                                    Just ru -> Conf.getUsername >>= \case
+                                                                 Nothing -> return (ru, Nothing)
+                                                                 Just user -> Conf.getPassword >>= \case
+                                                                    Nothing -> do   
+                                                                        pass <- askPassword
+                                                                        return (ru, Just (user, pass))
+                                                                    Just pass -> return (ru, Just (user, pass))
+                                                [r] -> return (T.pack r, Nothing)
+                                                _ -> error "too many arguments."
+                            putStrLn "loading webpage..."
+                            putStrLn "(you can use 'pgdl --offline' to browse the webpage you load last time)"
+                            nr <- genNetworkResource rootUrl up
+                            dNodes <- fetch nr 
+                            return (dNodes, nr)
+    let
+        initialState :: MainState
+        initialState = LState Nothing lst
+            where
+            lst = L.list (T.Name "root") (V.fromList dNodes) 3
+        theApp =
+            M.App { M.appDraw = drawUI
+                  , M.appChooseCursor = M.neverShowCursor
+                  , M.appHandleEvent = appEvent
+                  , M.appStartEvent = return
+                  , M.appAttrMap = const theMap 
+                  , M.appLiftVtyEvent = id
+                  }
+        appEvent :: MainState -> V.Event -> T.EventM (T.Next MainState)
+        appEvent ls@(LState father lst) e = case e of
+            V.EvKey V.KEsc [] -> M.halt ls
+            V.EvKey (V.KChar 'q') [] -> M.halt ls
+            V.EvKey V.KEnter [] -> case L.listSelectedElement lst of
+                                    Nothing -> M.continue ls
+                                    Just (rowNum, child) -> case child of
+                                        Directory entry dnsOp -> do
+                                            dns <- liftIO dnsOp -- grab the subdirectory
+                                            M.continue $ LState (Just ls) $ L.list (T.Name "root") (V.fromList dns) 3
+                                        File entry url False -> do
+                                            let fn = decodedName entry
+                                            path <- liftIO $ Conf.getLocaldir >>= \case
+                                                                Nothing -> return fn 
+                                                                Just pre -> return $ T.pack (T.unpack pre </> T.unpack fn)
+                                            let
+                                                dui = downloadInterface nr url path (fromJust $ fileSize entry) False
+                                                --                                ^ not good, unsafe
+                                                -- | construct a newList, modify the downloaded
+                                                -- file's *downloaded state* to True.
+                                                -- Maybe this is not a good approach?
+                                                newList = L.listMoveTo rowNum . L.listInsert rowNum (File entry url True) . L.listRemove rowNum $ lst
+                                            M.suspendAndResume $ dui >> return (LState father newList)
+                                        File entry url True -> do -- already downloaded file
+                                            let fn = decodedName entry
+                                            path <- liftIO $ Conf.getLocaldir >>= \case
+                                                                Nothing -> return fn 
+                                                                Just pre -> return $ T.pack (T.unpack pre </> T.unpack fn)
+                                            let dui = downloadInterface nr url path (fromJust $ fileSize entry) True
+                                            --                                    ^ not good, unsafe
+                                            M.suspendAndResume $ dui >> return ls
+            V.EvKey V.KLeft [] -> M.continue $ fromMaybe ls father
+            V.EvKey V.KRight [] -> case L.listSelectedElement lst of
+                                    Nothing -> M.continue ls
+                                    Just (_, sel) -> M.suspendAndResume $ entryAttrViewer sel >> return ls
+            V.EvKey (V.KChar '/') [] -> M.continue $ SearchState ls lst (E.editor "searchBar" (str.unlines) (Just 1) "")
+            V.EvKey (V.KChar 'd') [] -> case L.listSelectedElement lst of
+                                            Nothing -> M.continue ls
+                                            Just (rowNum, child) -> case child of
+                                                Directory _ _ -> M.continue ls
+                                                File entry url False -> M.continue ls
+                                                File entry url True -> do -- already downloaded file
+                                                    let fn = decodedName entry
+                                                    path <- liftIO $ Conf.getLocaldir >>= \case
+                                                                        Nothing -> return fn 
+                                                                        Just pre -> return $ T.pack (T.unpack pre </> T.unpack fn)
+                                                    liftIO $ deleteFile path
+                                                    -- | very bad approach. File deletion may fail.
+                                                    let newList = L.listMoveTo rowNum . L.listInsert rowNum (File entry url False) . L.listRemove rowNum $ lst
+                                                    M.continue $ LState father newList
+                                                    
+            --                                                      ^ current list which reactively change with editor
+            ev -> M.continue =<< (LState father <$> T.handleEvent ev lst)
+        appEvent ss@(SearchState ms@(LState _ origLst) lst ed) e = case e of
+            V.EvKey V.KEsc [] -> M.halt ss
+            V.EvKey V.KEnter [] -> case E.getEditContents ed of
+                [""] -> M.continue ms -- ^ do nothing if the editor is empty
+                _ -> M.continue $ LState (Just ms) lst
+            ev -> do
+                newEd <- T.handleEvent ev ed
+                -- | update the list, lst
+                let 
+                    linesToALine [l] = l
+                    linesToALine _ = error "not one line of words in the search bar, why?"
+                    -- | Why does L.listReplace require an Eq instance of Vector elements...?
+                    applyFilter kw lst = replaceList newElms lst
+                        where
+                        newElms = V.filter (\dn -> kw `isKeyWordOf` getDNText dn) $ L.listElements lst
+                        -- | sometime this will crash? how?
+                        replaceList es l = l {L.listElements = es, L.listSelected = Just 0}
+                    getDNText (Directory e _) = decodedName e
+                    getDNText (File e _ _) = decodedName e 
+                    isKeyWordOf t1 t2 = T.toCaseFold t1 `T.isInfixOf` T.toCaseFold t2
+                M.continue $ SearchState ms (applyFilter (T.pack . linesToALine $ E.getEditContents newEd) origLst) newEd
+        appEvent SearchState {} _ = error "unexpected prev state in SearchState."
+        theMap = A.attrMap V.defAttr [ (L.listAttr, V.white `on` V.black)
+                                     , ("directory", V.black `on` V.magenta)
+                                     , ("file", V.black `on` V.cyan)
+                                     , ("downloaded file", V.black `on` V.red)
+                                     , ("statusBar", V.black `on` V.green)
+                                     , ("searchBar", V.black `on` V.blue)
+                                     ]
+    M.defaultMain theApp initialState
+    return ()
+
+
+-- | use cropping to draw UI in the future?
+drawUI :: MainState -> [Widget]
+drawUI mainState = case mainState of
+        (LState _ l) -> [ C.hCenter . hLimit U.terminalWidth $
+                          vBox [entryList l, statusBar l]
+                        ]
+        (SearchState _ l e) -> [ C.hCenter . hLimit U.terminalWidth $
+                                 vBox [entryList l, searchBar e]
+                               ]
+    where
+    -- fixme (brick bug?): the vertical size of the list 
+    -- is somewhat strange when the hroizontal size limit
+    -- is not the multiple of its element size (it is 3)
+    -- This strange behavior do not occur in vty-ui
+    entryList lst = L.renderList lst listDrawElement
+    listDrawElement False (Directory a _) = C.hCenter . txt . mid . stripWidth $ decodedName a 
+    listDrawElement False (File a _ _) = C.hCenter . txt . mid . stripWidth $ decodedName a 
+    listDrawElement True d@(Directory _ _) = withAttr "directory" $ listDrawElement False d
+    listDrawElement True f@(File _ _ False) = withAttr "file" $ listDrawElement False f
+    listDrawElement True f@(File _ _ True) = withAttr "downloaded file" $ listDrawElement False f
+    mid s = T.unlines ["", s, ""]
+    stripWidth :: Text -> Text
+    stripWidth t = case U.cutTextByDisplayLength (U.terminalWidth-5) t of
+                    [] -> ""
+                    [singleLine] -> singleLine
+                    (x:_) -> x `T.append` "..."
+
+    searchBar ed = forceAttr "searchBar" $ hBox [txt "search: ", E.renderEditor ed]
+
+    statusBar lst = withAttr "statusBar" . str . expand $ info lst
+    expand s = s ++ replicate 88 ' '
+    info lst = case L.listSelectedElement lst of
+            Nothing -> "Nothing selected by user"
+            Just (_, sel) -> "  " ++ show (lastModified entry) ++ "    " ++ maybe "Nothing" friendlySize (fileSize entry)
+                where
+                entry = case sel of
+                            Directory e _ -> e
+                            File e _ _ -> e
+
diff --git a/src/NameAlgo.hs b/src/NameAlgo.hs
deleted file mode 100644
--- a/src/NameAlgo.hs
+++ /dev/null
@@ -1,115 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module NameAlgo where
-
-import Video
-
-import Data.Function
-import Data.Array
-import Data.List
-import Data.Ord
-import Data.Text (Text)
-import qualified Data.Text as T
-
--- | the video name parsing is only for specific type of videos...( [subtitle][video name][episode].... )
--- for most filename, the parsing function will just return the video name itself.
-beaut :: Video -> T.Text
-beaut (Video str _ _ _) = beautT str
-beaut (Folder str _ _)  = beautT str
-
-beautT :: T.Text -> T.Text
-beautT str = case cutName str of
-    Left x -> "\n" `apd` x
-    Right (sbt:nam:eps:_) -> T.unlines  [ ws 4  `apd` sbt
-                                        , ws 10 `apd` nam
-                                        , ws 30 `apd` eps
-                                        ]
-    _ -> error "something wrong in video name parsing function"
-    where apd = T.append
-          ws t = T.replicate t " "
-
--- | cut the name into pisces
-cutName :: T.Text -> Either T.Text [T.Text]
-cutName str
-    | length dat < 3 = Left str
-      -- parsing failure
-    | otherwise      = Right [sbtitle, name, episode]
-      -- ok.
-    where (sbtitle:name:episode:_) = dat
-          dat = filter (not . T.null) . getTags ["[", "]"] $ [str]
-          getTags :: [T.Text] -> -- ^ the delimiters
-                     [T.Text] -> -- ^ initial patterns
-                     [T.Text]
-          getTags dl s = foldl (\ls d -> concatMap (T.splitOn d) ls) s dl
-
--- | return the edit distance of two string
-editDis :: T.Text -> T.Text -> Int
-editDis t1 t2 = dp!(len1, len2)
-    where (s1, s2) = (T.unpack t1, T.unpack t2)
-          (len1, len2) = (length s1, length s2)
-          a1 = listArray (1, len1) s1
-          a2 = listArray (1, len2) s2
-          bnd = ((0, 0), (len1, len2))
-          dp = listArray bnd [go i j | (i, j) <- range bnd]
-          go i 0 = i
-          go 0 j = j
-          go i j = minimum [ dp!(i, j-1) + 1
-                           , dp!(i-1, j) + 1
-                           , dp!(i-1, j-1) + cost
-                           ]
-            where cost = if a1!i == a2!j then 0 else 1
-
--- | caculate whether the edit distance of two video
--- name is less than half of the first video's length
-isAlike :: Video -> Video -> Bool
-isAlike vf1 vf2 = dis <= lim
-    where (n1, n2) = (getName vf1, getName vf2)
-          (e1, e2) = (cutName n1, cutName n2)
-          vn1 = either id (!!1) e1
-          vn2 = either id (!!1) e2
-          -- vn1 and vn2 are the video name without subtitle or episode if being parsed.
-          lim = length (T.unpack vn1) `div` 2
-          dis = editDis vn1 vn2
-
-{-
-search :: [Video] ->  -- ^ video list
-          [String] -> -- ^ a list of target string
-          [Video]
-search vids par = filter (\v -> vidInfx v (map T.pack par)) vids
--}
-
-vidInfx :: Video -> [Text] -> Bool
-vidInfx _ [] = True
-vidInfx v (x:xs) = (x `ifx` getName v) && vidInfx v xs
-    where ifx = T.isInfixOf `on` T.toCaseFold
-
-sortByName :: [Video] -> [Video]
-sortByName = sortBy (comparing getName)
-
-sortVid :: [Video] -> [Video]
-sortVid = sortBy cmpv
-    where cmpv :: Video -> Video -> Ordering
-          cmpv = cmp `on` dt
-            where dt v
-                    | isVid v = vidDate v
-                    | otherwise = fldDate v
-          cmp :: T.Text -> T.Text -> Ordering
-          cmp = flip compare `on` prs
-            where prs tx = [year, tranMon mon, day, time] 
-                    where [day, mon, year, time] = T.splitOn "-" tx
-                          tranMon :: T.Text -> T.Text
-                          tranMon m = case m of
-                            "Jan" -> "A"
-                            "Feb" -> "B"
-                            "Mar" -> "C"
-                            "Apr" -> "D"
-                            "May" -> "E"
-                            "Jun" -> "F"
-                            "Jul" -> "G"
-                            "Aug" -> "H"
-                            "Sep" -> "I"
-                            "Oct" -> "J"
-                            "Nov" -> "K"
-                            "Dec" -> "L"
-                            _     -> " "
-
diff --git a/src/Networking.hs b/src/Networking.hs
new file mode 100644
--- /dev/null
+++ b/src/Networking.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+
+module Networking
+where
+
+import Network.HTTP.Conduit
+import Data.Text.Encoding
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import Data.Ord
+import Data.List
+import Data.Maybe
+import Text.HTML.DirectoryListing.Type
+import Text.HTML.DirectoryListing.Parser
+import System.FilePath.Posix
+import Control.Concurrent
+
+import Cache
+import qualified Configure as Conf
+import Types
+import Local
+
+type NetworkResource = Text -> (Request, Manager) 
+--                    ^ relative path
+
+genNetworkResource :: Text -> -- ^ webpage url (root)
+                      Maybe (Text, Text) -> -- ^ (username, password)
+                      IO NetworkResource
+genNetworkResource url up = do
+    let genReq :: Text -> Request
+        genReq rp = case parseUrl path of
+                        Nothing -> error $ "invalid url: " ++ path
+                        Just r -> auth r
+            where
+            path = T.unpack url ++ T.unpack rp
+            auth = case up of
+                    Nothing -> id
+                    Just (u, p) -> applyBasicAuth (encodeUtf8 u) (encodeUtf8 p)
+    manager <- newManager tlsManagerSettings
+    return $ \t -> (genReq t, manager)
+    
+
+getWebpage :: NetworkResource -> 
+              Text -> -- relative path
+              IO Text
+getWebpage nr rp = do
+    let (req, mgr) = nr rp
+    response <- httpLbs req mgr
+    let body = responseBody response
+    case decodeUtf8' . B.concat . BL.toChunks $ body of
+        Left unicodeException -> error . show $ unicodeException
+        Right t -> return t
+
+fetch :: NetworkResource ->
+         IO [DNode]
+fetch nr = do
+    html <- getWebpage nr ""
+    lcd <- T.unpack . fromMaybe "" <$> Conf.getLocaldir
+    let
+        entries = sortBy (flip $ comparing lastModified) $ parseDirectoryListing html
+        toDNode :: Text -> Entry -> IO DNode
+        toDNode url e
+            | isDirectory e = return $ Directory e childs
+            | otherwise = do
+                downloaded <- isFileDownloaded (decodedName e) lcd
+                return $ File e (url `T.append` href e) downloaded
+            where
+            childs :: IO [DNode]
+            childs = do
+                html' <- getWebpage nr newUrl
+                mapM (toDNode newUrl) . sortBy (flip $ comparing lastModified) $ parseDirectoryListing html'
+            newUrl = url `T.append` href e 
+    forkIO $ writeCache entries
+    mapM (toDNode "") entries
diff --git a/src/Pgdl.hs b/src/Pgdl.hs
deleted file mode 100644
--- a/src/Pgdl.hs
+++ /dev/null
@@ -1,235 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
-{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
-
-import FetchHtml
-import PrsVid
-import NameAlgo 
-import RealWorld
-import Chkcfg
-import Video
-import Log
-import GenStat
-import CrtInf
-import AskScene
-import UI
-
-import qualified Data.Text as T
-import Data.Ord
-import Control.Applicative
-import Control.Concurrent
-import Control.Monad
-
-import Graphics.Vty
-import Graphics.Vty.Widgets.All
-
-main :: IO ()
-main = do
-    chkcfg 
-    -- check if there exists a configuration file, if not, then create one
-
-    c <- newCollection
-
-    tmpLst <- newList 3
-    -- the main list will display on screen
-
-    diskV <- readVid
-    -- read cache in the disk, if the cache do not exists, return "downloading"
-
-    forM_ diskV $ \v -> addToList tmpLst v =<< plainText (beaut v)
-    -- add disk videos to list
-
-    tmpLfg <- newFocusGroup
-    addToFocusGroup tmpLfg tmpLst
-
-    statBar <- plainText waitingBar
-    -- the state bar show in the bottom
-    setNormalAttribute statBar (black `on` green)
-
-    tmpUi <- centered =<< hFixed 80 =<< vBox tmpLst statBar
-    -- ui consists of a list and a state bar
-    addToCollection c tmpUi tmpLfg
-    
-
-    (dlg, dfcg) <- newAskScene
-    dui <- centered =<< hFixed 50 (sceneWidget dlg)
-    chgdl <- addToCollection c dui dfcg
-
-    ifsfg <- newFocusGroup
-    -- a focus group for information page
-    
-    helpfg <- newFocusGroup
-    -- a focus group of help page
-
-    -- this function will show the focused item's information to user
-
-    tmpLfg `onKeyPressed` tryExit
-    dfcg `onKeyPressed` tryExit
-    ifsfg `onKeyPressed` tryExit
-    helpfg `onKeyPressed` tryExit
-
-    -- colorDecide tmpLst
-
-    -- let's download video from internet !
-    schedule $ do
-        forkIO $ do
-            vdlst <- prsHtm <$> fetchHtml
-            -- prsHtm :: Text -> [Video (whether video or folder)]
-            forkIO . writeVid $ vdlst
-            -- write cache to the disk
-            
-            vlst <- vidsVList vdlst
-            let lst = listW vlst
-            
-            -- move to the selected old item
-            Just (_, (oldSeleItm, _)) <- getSelected tmpLst
-            listFindFirst lst oldSeleItm >>= \case
-                Just idx -> setSelected lst idx
-                Nothing -> return ()
-
-            -- show new list
-            lfg <- newFocusGroup
-            lfg `onKeyPressed` tryExit
-            addToFocusGroup lfg lst
-            lui <- centered =<< hFixed 80 =<< vBox lst statBar
-            chgls <- addToCollection c lui lfg 
-            chgls
-            ew <- editWidget
-            -- a widget to enter keyword
-            kfg <- newFocusGroup
-            addToFocusGroup kfg ew
-            kui <- centered =<< hFixed 80 =<< vBox lst ew
-            chgky <- addToCollection c kui kfg
-            -- create a scene presents a input box
-
-            colorDecide lst
-
-            -- four button in ask scene's action
-            onScePlay dlg $ \_ -> do
-                Just (_, (itm, _)) <- getSelected lst
-                justPlay itm
-                chgls
-            onSceDown dlg $ \_ -> do
-                Just (_, (itm, _)) <- getSelected lst
-                playVid itm
-                chgls
-            onSceQuit dlg $ const chgls
-            onSceRemv dlg $ \_ -> do
-                Just (_, (itm, _)) <- getSelected lst
-                removeVid itm
-                chgls
-
-          --  onSelectionChange lst $ \sle -> case sle of
-           --     SelectionOn _ itm _ -> setText statBar =<< genStat itm
-            --    -- refresh state bar after any scroll
-            --    _ -> return ()
-            refreshStatusBar lst statBar
-
-            let chgInf = do
-                  Just (_, (vid, _)) <- getSelected lst
-                  inf <- centered =<< plainText (crtInfPg vid)
-                  addToFocusGroup ifsfg inf
-                  join $ addToCollection c inf ifsfg
-
-            -- the information scene
-            onKeyPressed ifsfg $ \_ key _ -> case key of
-                KLeft -> do
-                    chgls -- return to the list
-                    return True
-                KUp   -> do
-                    scrollUp lst
-                    chgInf
-                    return True
-                KDown -> do
-                    scrollDown lst
-                    chgInf
-                    return True
-                _     -> return False
-
-            let chgHelp = do
-                  hp <- centered =<< plainText helpPage
-                  addToFocusGroup helpfg hp
-                  join $ addToCollection c hp helpfg
-            onKeyPressed helpfg $ \_ _ _ -> (chgls >> return True)
-
-            onKeyPressed lst $ \_ key _ -> case key of
-                KRight -> do
-                    chgInf
-                    return True
-                _ -> return False
-
-            onActivate ew $ \e -> do
-                t <- getEditText e
-                setEditText e ""
-                filterVList vlst $ \v -> vidInfx v (T.words t)
-                chgls
-                -- return to the list
-
-            let openFld lnk = do
-                  ctnt <- (map (attcLink lnk) . prsHtm) <$> fetchFld lnk 
-                  -- attach folder link to the videos in the folder
-                  when (null ctnt) $ error "there is no video in the folder!" 
-                  setVList vlst ctnt
-                  return ()
-
-            onKeyPressed lst $ \_ key _ -> case key of
-                KEnter -> do
-                -- User can choose a folder or a video !
-                    Just (_, (itm, _)) <- getSelected lst
-                    case itm of
-                        Folder _ lnk _ -> openFld lnk
-                        v -> fex v >>= \case
-                            True -> chgdl
-                            False -> playVid itm
-                    return True
-                KLeft -> do
-                -- return to the old list
-                    backVList vlst
-                    return True
-                KChar 's' -> do
-                -- filter the videos by the focused video
-                    Just (_, (itm, _)) <- getSelected lst
-                    filterVList vlst (isAlike itm)
-                    return True
-                KChar 'n' -> do
-                -- sort videos by their name
-                    sortVList vlst (comparing getName) 
-                    return True
-                KChar 'v' -> do
-                    filterVListIO vlst $ \obj -> if isVid obj then downloaded obj
-                                                 else return False 
-                    return True
-                KChar 'u' -> do
-                    filterVListIO vlst $ \obj -> if isVid obj then not <$> downloaded obj
-                                                 else return False 
-                    return True
-                KChar '/' -> do
-                    chgky
-                    return True
-                KChar 'h' -> do
-                    chgHelp
-                    return True
-
-                _   -> return False
-
-            return ()
-        return ()
-
-    runUi c $ defaultContext { normalAttr = white `on` black 
-                             , focusAttr  = black `on` blue
-                             }
-
-helpPage :: T.Text
-helpPage = T.unlines [ 
-                      "q - quit"
-                     ,"s - list files that similar with the highlighted file"
-                     ,"Key Right - show the detailed information of the highlighted file"
-                     ,"Key Left - go back to the previous page"
-                     ,"Enter - xdg-open the file"
-                     ,"/ - open a input box to enter a keyword to search"
-                     ,"n - sort the current list by name"
-                     ,"v - show all the files already in the disk"
-                     ,"u - show all the files not in the disk"
-                     ]
-
-
diff --git a/src/PrsVid.hs b/src/PrsVid.hs
deleted file mode 100644
--- a/src/PrsVid.hs
+++ /dev/null
@@ -1,54 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module PrsVid where
-
-import Video
-import NameAlgo
-
-import Network.HTTP.Base
-import Text.HTML.TagSoup
-import qualified Data.Text as T
-
-prsHtm :: T.Text -> [Video]
-prsHtm t = if null vs then [noVideo] else vs
-    where vs = sortVid $ prsVid t ++ prsFld t
-          noVideo = Video "No video in this folder." "" "" ""
-
-prsVid :: T.Text -> [Video]
-prsVid = map (genVid . filter (not . T.null) . map pullText) .
-         filter isVidLn . map parseTags . T.lines
-    where isVidLn = any isVidLnk
-          isVidLnk tg
-            | isTagOpen tg = not ("/" `T.isSuffixOf` fromAttrib "href" tg) && (not . T.null $ fromAttrib "href" tg)
-            | otherwise = False
-          pullText tg
-            | isTagOpen tg = fromAttrib "href" tg
-            | isTagText tg = fromTagText tg
-            | otherwise = ""
-          genVid [lnk, _, tx] = Video {vidLink = lnk, vidName = nm, vidDate = dt, vidSize = sz}
-            where dt = T.intercalate "-" . take 2 $ T.words tx
-                  sz = T.pack . hsz . read . last . words . T.unpack $ tx
-                  hsz :: Int -> String
-                  hsz i = show (i `div` (1024*1024)) ++ "M"
-                  nm = genName lnk
-          genVid _ = Video "Parse Fail" "" "" ""
-          fmts = [".mp4", ".avi", ".mkv"]
-
-prsFld :: T.Text -> [Video]
-prsFld = map (mkFld . filter (not . T.null) . map pullText) . filter isFldLn . map parseTags . T.lines
-    where isFldLn = isFldLnk . head
-          isFldLnk tg
-            | isTagOpen tg = (`T.isSuffixOf` fromAttrib "href" tg) "/"
-            | otherwise = False
-          mkFld :: [T.Text] -> Video
-          mkFld [lnk, _, tx] = Folder {fldName = nm, fldLink = lnk, fldDate = dt}
-            where nm = T.init . genName $ lnk
-                  dt = T.intercalate "-" . take 2 $ T.words tx
-          mkFld _ = Folder "Folder Parse Fail" "" ""
-          pullText tg
-            | isTagOpen tg = fromAttrib "href" tg
-            | isTagText tg = fromTagText tg
-            | otherwise = ""
-
-genName :: T.Text -> T.Text
-genName = T.pack . urlDecode . T.unpack
diff --git a/src/RealWorld.hs b/src/RealWorld.hs
deleted file mode 100644
--- a/src/RealWorld.hs
+++ /dev/null
@@ -1,82 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
-
-module RealWorld where
-
-import Getconfig
-import Video
-
-import Control.Monad
-import Distribution.System
-import System.Process
-import System.Directory
-import System.FilePath.Posix
-import qualified Data.Text as T
-
--- play a video
-playVid :: Video -> IO ()
-playVid vid = do
-    when (isFld vid) $ error "give playVid a folder."
-
-    -- don't download a video whoose name is empty
-    when ("/" `T.isSuffixOf` vidLink vid) $ error "maybe this is not a video?"
-
-    username <- getUsername
-    password <- getPassword
-    servpath <- getServpath
-    localdir <- getLocaldir
-
-    let localloc = localdir </> vn
-    fex <- doesFileExist localdir
-    when fex $ removeFile localloc
-
-    let purepath = reverse . dropWhile (/= '/') . reverse $ servpath
-    let url = "http://" ++ purepath ++ vu
-    case username of
-        ""  -> runCommand $ "nohup curl -L "                                          ++ addq url ++ " -o " ++ addq localloc ++ "&>/dev/null &"
-        _   -> runCommand $ "nohup curl -L -u " ++ username ++ ":" ++ password ++ " " ++ addq url ++ " -o " ++ addq localloc ++ "&>/dev/null &"
-    let checkFile = doesFileExist localloc >>= \ready -> unless ready checkFile
-    checkFile
-    case buildOS of
-        OSX   -> runCommand $ "open " ++ addq localloc ++ " "
-        Linux -> runCommand $ "nohup xdg-open " ++ addq localloc ++ " &>/dev/null &"
-        _     -> error "OS unsupported!"
-    return ()
-  where vn = T.unpack . vidName $ vid
-        vu = T.unpack . vidLink $ vid
-        addq :: String -> String
-        addq s = "\"" ++ s ++ "\""
-
-justPlay :: Video -> IO ()
-justPlay vid = do
-    when (isFld vid) $ error "give playVid a folder."
-
-    localdir <- getLocaldir
-    let localloc = localdir </> vn
-    case buildOS of
-        OSX   -> runCommand $ "open " ++ addq localloc ++ " "
-        Linux -> runCommand $ "nohup xdg-open " ++ addq localloc ++ " &>/dev/null &"
-        _     -> error "OS unsupported!"
-    return ()
-    -- exitSuccess
-  where vn = T.unpack . vidName $ vid
-        addq :: String -> String
-        addq s = "\"" ++ s ++ "\""
-    
--- | remove a video 
-removeVid :: Video -> IO ()
-removeVid vid = do
-    localDir <- getLocaldir
-    removeFile $ localDir </> vn
-    return ()
-    -- exitSuccess
-    where vn = T.unpack . vidName $ vid
-
--- | determine whether a video is downloaded
-downloaded :: Video -> IO Bool
-downloaded v = do
-    lcd <- getLocaldir
-    doesFileExist $ lcd </> T.unpack t
-    where t = if isVid v then vidName v
-              else error "try to consider a folder downloaded or not" 
-
diff --git a/src/Types.hs b/src/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Types.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Types
+where
+
+import Text.HTML.DirectoryListing.Type
+import Data.Text (Text)
+
+data DNode = Directory Entry (IO [DNode]) | File Entry Text Bool
+--                                                          downloaded?
+
diff --git a/src/UI.hs b/src/UI.hs
deleted file mode 100644
--- a/src/UI.hs
+++ /dev/null
@@ -1,152 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
-{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
-
-module UI where
-
-import Video
-import RealWorld
-import NameAlgo
-import GenStat
-
-import Control.Monad
-import System.Exit
-import Graphics.Vty
-import Graphics.Vty.Widgets.All
-import Data.IORef
-import Data.List
-import Control.Concurrent
-
-type WL = Widget (List Video FormattedText)
-type VidStat = ([Video], Video)
---              videos   last select video
-
-data VList = VList WL (IORef [VidStat])
-
-listW :: VList -> WL
-listW (VList lw _) = lw
-
-vidsVList :: [Video] -> IO VList
-vidsVList vs = do
-    lst <- newList 3
-    forM_ vs $ \v -> addToList lst v =<< plainText (beaut v)
-    ior <- newIORef []
-    return $ VList lst ior
-
-filterVList :: VList -> (Video -> Bool) -> IO ()
-filterVList vl@(VList wl _) ok = do
-    origVids <- getListVideos wl
-    case filter ok origVids of
-        [] -> return ()
-        res -> do
-            storeState vl
-            setListVideos wl res
-
-filterVListIO :: VList -> (Video -> IO Bool) -> IO ()
-filterVListIO vl@(VList wl _) ok = do
-    origVids <- getListVideos wl
-    newVids <- filterM ok origVids
-    case newVids of
-        [] -> return ()
-        res -> do
-            storeState vl
-            setListVideos wl res
-
-sortVList :: VList -> (Video -> Video -> Ordering) -> IO ()
-sortVList vl@(VList wl _) cmp = do
-    storeState vl
-    setListVideos wl . sortBy cmp =<< getListVideos wl
-
-storeState :: VList -> IO ()
-storeState (VList wl st) = do
-    vs <- getListVideos wl
-    idx <- getWLSelected wl
-    modifyIORef st ((vs, idx):)
-
-setVList :: VList -> [Video] -> IO ()
-setVList (VList wl st) vs = do
-    oldVs <- getListVideos wl
-    idx <- getWLSelected wl
-    modifyIORef st ((oldVs, idx):)
-    setListVideos wl vs
-
-backVList :: VList -> IO ()
-backVList (VList wl st) = 
-    readIORef st >>= \case
-        [] -> 
-            return ()
-        ((bvs, bloc):_) -> do
---            putStrLn "something >////<"
-            setListVideosB wl bvs bloc
-            modifyIORef st tail
-
-tryExit :: Widget FocusGroup -> Key -> [Modifier] -> IO Bool
-tryExit _ key _ = case key of
-    KChar 'q' -> exitSuccess
-    KEsc      -> exitSuccess
-    _         -> return False
-
-fex :: Video -> IO Bool
-fex itm
-    | isVid itm = downloaded itm
-    | otherwise = return False
-
--- return the videos in the list
-getListVideos :: WL -> IO [Video]
-getListVideos lst = do
-    sz <- getListSize lst
-    forM [0..sz-1] $ \idx -> do
-        Just (itm, _) <- getListItem lst idx
-        return itm
-
-getWLSelected :: WL -> IO Video
-getWLSelected ls = do
-    Just (_, (idx, _)) <- getSelected ls
-    return idx
-
--- set lst content to vs
-setListVideos :: WL -> [Video] -> IO ()
-setListVideos lst vs = do
-    oldItm <- getWLSelected lst
-    clearList lst 
-    forM_ vs $ \v -> addToList lst v =<< plainText (beaut v)
-    listFindFirst lst oldItm >>= \case
-        Just ind -> setSelected lst ind
-        Nothing  -> return ()
-
-setListVideosB :: WL -> [Video] -> Video -> IO ()
-setListVideosB lst vs bvid = do
-    Just (_, (oldItm, _)) <- getSelected lst
-    clearList lst 
-    forM_ vs $ \v -> addToList lst v =<< plainText (beaut v)
-    listFindFirst lst oldItm >>= \case
-        Just ind -> setSelected lst ind
-        Nothing  -> listFindFirst lst bvid >>= \case
-            Just ind2 -> setSelected lst ind2
-            Nothing -> return ()
-
-colorDecide :: WL -> IO ()
-colorDecide lst = do
-    forkIO . forever $ do
-        schedule $ do
-            Just (_, (itm, _)) <- getSelected lst
-            if isFld itm then
-                setFocusAttribute lst (black `on` magenta)
-                        -- folder's color
-            else fex itm >>= \case
-                    True  -> setFocusAttribute lst (black `on` red) 
-                             -- if video have been download, use red color
-                    False -> setFocusAttribute lst (black `on` cyan)
-                             -- if video haven't been download, use cyan
-        threadDelay 50000 -- 0.05 second
-    return ()
-
-refreshStatusBar :: WL -> Widget FormattedText-> IO ()
-refreshStatusBar lst statBar = do
-    forkIO . forever $ do
-        schedule $ do
-            Just (_, (itm, _)) <- getSelected lst
-            setText statBar =<< genStat itm
-        threadDelay 50000 -- 0.05 second
-    return ()
-
diff --git a/src/Utils.hs b/src/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Utils.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Utils 
+where
+
+import qualified Data.ByteString as B
+import qualified Data.Text as T
+import Data.Text (Text)
+import Text.Printf
+import Data.Text.Encoding
+
+-- | give a file size in bytes, return pretty file size 
+-- represent in KB, MB, GB or TB
+friendlySize :: Integer -> String
+friendlySize b
+    | fb < 1024 = s2s fb ++ " B "
+    | kb < 1024 = s2s kb ++ " KB"
+    | mb < 1024 = s2s mb ++ " MB"
+    | gb < 1024 = s2s gb ++ " GB"
+    | otherwise = s2s tb ++ " TB"
+    where
+    fb :: Double
+    fb = fromIntegral b
+    kb = fb / 1024.0
+    mb = kb / 1024.0
+    gb = mb / 1024.0
+    tb = gb / 1024.0
+    s2s :: Double -> String
+    s2s = printf "%6s" . (printf "%.1f" :: Double -> String)
+
+terminalWidth :: Num a => a
+terminalWidth = 80
+
+-- | return the estimated display length of a Text
+displayLength :: Text -> Integer
+displayLength = sum . map charDisplayLen . T.unpack
+
+-- | charDisplayLen determine a unicode character is a wide character or not
+-- (a wide character occupy 2 space in the terminal)
+-- this method may seem unreliable, but have no better idea.
+charDisplayLen :: Char -> Integer
+charDisplayLen c
+    | (B.length . encodeUtf8 . T.pack $ [c]) > 1 = 2
+    | otherwise = 1
+
+-- | given a line of Text, cut it to a group of Text
+-- which have 'len' display length
+cutTextByDisplayLength :: Integer -> Text -> [Text]
+cutTextByDisplayLength len = map T.pack . gp . T.unpack
+    where
+    gp :: String -> [String]
+    gp = go 0 "" . map (\c -> (c, charDisplayLen c))
+        where
+        go :: Integer -> String -> [(Char, Integer)] -> [String]
+        go _ acc []
+            | null acc = []
+            | otherwise = [reverse acc]
+        go lsum acc str@(x:xs) 
+            | lsum + snd x <= len = go (lsum + snd x) (fst x : acc) xs
+            | otherwise = reverse acc : go 0 "" str
+
+
diff --git a/src/Video.hs b/src/Video.hs
deleted file mode 100644
--- a/src/Video.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Video where
-
-import System.FilePath
-import qualified Data.Text as T
-
-data Video = Video { vidName :: T.Text
-                   , vidLink :: T.Text
-                   , vidSize :: T.Text
-                   , vidDate :: T.Text
-                   } |
-             Folder { fldName :: T.Text
-                    , fldLink :: T.Text
-                    , fldDate :: T.Text
-                    }
-
-instance Eq Video where
-    x == y 
-        | isVid x && isVid y = vidName x == vidName y
-        | isFld x && isFld y = fldName x == fldName y
-        | otherwise = False
-
-getName :: Video -> T.Text
-getName (Video n _ _ _) = n
-getName (Folder n _ _) = n
-
-isVid :: Video -> Bool
-isVid Video {} = True
-isVid _ = False
-
-isFld :: Video -> Bool
-isFld v = not $ isVid v
-
-
-attcLink :: T.Text -> Video -> Video
-attcLink fnt (Video name linkt size date) = Video 
-                         { vidName = name
-                         , vidLink = T.pack $ fn </> link
-                         , vidSize = size
-                         , vidDate = date
-                         }
-    where fn = T.unpack fnt
-          link = T.unpack linkt
-attcLink fnt (Folder name linkt date) = Folder
-                         { fldName = name
-                         , fldLink = T.pack $ fn </> link
-                         , fldDate = date
-                         }
-    where fn = T.unpack fnt
-          link = T.unpack linkt
-
