-- SPDX-FileCopyrightText: 2024 David Mosbach <david.mosbach@uniworx.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later

{-# Language OverloadedStrings, LambdaCase, TypeApplications #-}

import Data.Text (Text)
import qualified Data.Text as T
import System.Directory
import System.Environment
import System.IO

main :: IO ()
main = getArgs >>= \case
  ["--assign", offsetFile] -> parseOffsets offsetFile >>= uncurry nextOffset
  ["--remove", offset] -> removeOffset offset
  _ -> fail "unsupported args"

parseOffsets :: FilePath -> IO (Int,Int)
parseOffsets offsetFile = do
  user <- T.pack <$> getEnv "USER"
  let pred x = "//" `T.isPrefixOf` x || T.null (T.strip x)
      tokenise = map (filter (not . pred) . T.lines) . T.split (=='#')
      extract = map tail . filter (\u -> not (null u) && user == (T.strip $ head u))
  ((extract . tokenise . T.pack) <$> readFile offsetFile) >>= \case
    [[min,max]] -> return (read $ T.unpack min, read $ T.unpack max)
    x -> print x >> fail "malformed offset file"

nextOffset :: Int -> Int -> IO ()
nextOffset min max
  | min > max = nextOffset max min
  | otherwise = do
      home <- getEnv "HOME"
      offset <- findFile [home] ".port-offsets" >>= \case
        Nothing   -> writeFile (home ++ "/.port-offsets") (show min) >> return min
        Just path -> do
          used <- (map (read @Int) . filter (not . null) . lines) <$> readFile path
          o <- next min max used
          appendFile path ('\n' : show o)
          return o
      print offset
        where
          next :: Int -> Int -> [Int] -> IO Int
          next min max used
            | min > max = fail "all offsets currently in use"
            | min `elem` used = next (min+1) max used
            | otherwise = return min

removeOffset :: String -> IO ()
removeOffset offset = do
  home <- getEnv "HOME"
  findFile [home] ".port-offsets" >>= \case
    Nothing   -> fail "offset file does not exist"
    Just path -> do
      remaining <- (filter (/= offset) . lines) <$> readFile path
      run <- getEnv "XDG_RUNTIME_DIR"
      (tempPath, fh) <- openTempFile run ".port-offsets"
      let out = unlines remaining
      hPutStr fh $ out
      case T.null (T.strip $ T.pack out) of
        True -> removeFile path
        False -> writeFile path $ out
      removeFile tempPath

