module find where

open import Level
open import Size
open import Codata.IO
open import Codata.Thunk
open import Data.Bool.Base using (if_then_else_)
open import Data.List.Base as List hiding (_++_)
open import Data.Maybe.Base using (Maybe; nothing; just)
open import Data.Product
open import Data.String
open import Data.Sum.Base
open import Function
open import System.Environment
open import System.FilePath.Posix
open import System.Directory
open import System.Directory.Tree

-- @find str iots@ looks depth-first for a file with name @str@ in the
-- list of subdirectories @iots@ left to process.

find : String   {i}  List (IO′ 0ℓ RelativeTree i)  IO′ 0ℓ (Maybe (List RelativePath)) i
find str []           = pure nothing
find str (iot  iots) = iot >>=ᵗ λ where
  (dir  fs :< ds) .force  do
    case List.filter ((str ≟_)  getFilePath) fs of λ where
      -- if we have found a suitable file in the current directory
      -- then we return it immediately
      (fp  _)  pure (just (dir  fp  []))
      -- otherwise we look for it in its subdirectories. If we fail
      -- then we look at the next directory in the list.
      []        do
        let ds = List.map ((tree# <$>_) ∘′ proj₂) ds
        nothing  find str ds where (just fps)  pure (just (dir  fps))
        find str iots

usage : IO 0ℓ _
usage = putStrLn "Requires two arguments: root filepath and target file name"

main = run $ do
  -- the user should call e.g. @find src/ Clock.agda@
  (fp  str  [])  getArgs where _  usage
  let dtree = tree =<< makeAbsolute (mkFilePath fp)
  mfp  find str (dtree  [])
  case mfp of λ where
    nothing     putStrLn $ "Could not find file with name " ++ str
    (just fps)  putStrLn $ "Found: " ++ getFilePath (joinPath fps)