module Recents where
import Control.Monad (filterM)
import Data.List (sort)
import Settings
import Stack (Stack)
import System.Environment (lookupEnv)
import System.FilePath ((</>), splitFileName, takeExtension, dropExtension, splitPath, joinPath)
import qualified Stack as S
import qualified System.Directory as D
import qualified System.IO.Strict as IOS (readFile)

getRecents :: IO (Stack FilePath)
getRecents = do
  rf <- getRecentsFile
  exists <- D.doesFileExist rf
  if exists
    then removeDeletedFiles rf *> clampRecents rf
    else return S.empty

removeDeletedFiles :: FilePath -> IO (Stack FilePath)
removeDeletedFiles fp = do
  contents <- IOS.readFile fp
  existing <- S.fromList <$> filterM D.doesFileExist (lines contents)
  writeRecents existing
  return existing

parseRecents :: String -> Stack FilePath
parseRecents = S.fromList . lines

clampRecents :: FilePath -> IO (Stack FilePath)
clampRecents fp = do
  rs <- parseRecents <$> IOS.readFile fp
  maxRs <- getMaxRecents
  let clamped = S.takeStack maxRs rs
  writeRecents clamped
  return clamped

addRecent :: FilePath -> IO ()
addRecent fp = do
  rs <- getRecents
  maxRecents <- getMaxRecents
  let rs'  = fp `S.insert` rs
      rs'' = if S.size rs' <= maxRecents
              then rs'
              else S.removeLast rs'
  writeRecents rs''

writeRecents :: Stack FilePath -> IO ()
writeRecents stack = do
  file <- getRecentsFile
  writeFile file $ unlines (S.toList stack)

getRecentsFile :: IO FilePath
getRecentsFile = do
  maybeSnap <- lookupEnv "SNAP_USER_DATA"
  xdg <- D.getXdgDirectory D.XdgData "hascard"

  let dir = case maybeSnap of
                Just path | not (null path) -> path
                          | otherwise       -> xdg
                Nothing                     -> xdg
  D.createDirectoryIfMissing True dir

  return (dir </> "recents")

initLast :: [a] -> ([a], a)
initLast [x] = ([], x)
initLast (x:xs) = let (xs', y) = initLast xs
                   in (x:xs', y)

prep :: [FilePath] -> ([String], [FilePath])
prep [] = ([], [])
prep fps@(fp:_) = if all ((== takeExtension fp) . takeExtension) fps
  then unzip (map ((\(pre, fn) -> (pre, dropExtension fn)) . splitFileName) fps)
  else unzip (map splitFileName fps)

shortenFilepaths :: [FilePath] -> [FilePath]
shortenFilepaths = uncurry shortenFilepaths' . prep
  where
    shortenFilepaths' prefixes abbreviations =
      let ds = duplicates abbreviations in
        if null ds then abbreviations else
          shortenFilepaths'
            (flip map (zip [0..] prefixes) (
              \(i, pre) -> if i `elem` ds then
                joinPath (init (splitPath pre)) else pre
            ))
            (flip map (zip [0..] abbreviations) (
              \(i, abbr) -> if i `elem` ds then
                last (splitPath (prefixes !! i)) ++ abbr
                else abbr) )


duplicates :: Eq a => [a] -> [Int]
duplicates = sort . map fst . duplicates' 0 [] []
  where duplicates' _ _    acc []     = acc
        duplicates' i seen acc (x:xs) = duplicates' (i+1) ((i, x) : seen) acc' xs
          where acc' = case (getPairsWithValue x acc, getPairsWithValue x seen) of
                  ([], []) -> acc
                  ([], ys) -> (i, x) : ys ++ acc
                  (_, _)   -> (i, x) : acc
                -- acc' = if getPairsWithValue x seen then (i, x) : acc else acc 

getPairsWithValue :: Eq a => a -> [(Int, a)] -> [(Int, a)]
getPairsWithValue _ []       = []
getPairsWithValue y ((i, x):xs)
  | x == y    = (i, x) : getPairsWithValue y xs
  | otherwise = getPairsWithValue y xs