{-# LANGUAGE NamedFieldPuns #-}

module FortyTwo.Utils where

import System.Console.ANSI (cursorUpLine, clearFromCursorToScreenEnd)
import System.IO (hSetBuffering, hFlush, hSetEcho, hReady, stdin, stdout, BufferMode(..))
import Data.List (findIndex, findIndices, elemIndex, intercalate)
import Control.Monad (when)
import Data.Maybe (fromJust)
import FortyTwo.Types(Option(..), Options)
import FortyTwo.Constants (emptyString)

noBuffering :: IO()
noBuffering = do
  hSetBuffering stdin NoBuffering
  hSetBuffering stdout NoBuffering

restoreBuffering :: IO()
restoreBuffering = do
  hSetBuffering stdin LineBuffering
  hSetBuffering stdout LineBuffering

noEcho :: IO ()
noEcho = hSetEcho stdin False

restoreEcho :: IO ()
restoreEcho = hSetEcho stdin True

clearLines :: Int -> IO()
clearLines lines' = do
  -- move up of 1 line...
  cursorUpLine lines'
  -- and clear them
  clearFromCursorToScreenEnd

-- | Map a collection with an index
map' :: (Int -> a -> b) -> [a] -> [b]
map' f = zipWith f [0..]

-- | Filter a collection with index
filter' :: Eq a => (Int -> a -> Bool) -> [a] -> [a]
filter' f xs = [x | x <- xs, f (fromJust (elemIndex x xs)) x]

getKey = reverse <$> getKey' emptyString
  where
    getKey' chars = do
      char <- getChar
      more <- hReady stdin
      (if more then getKey' else return) (char:chars)

-- | Get useful informations from the options collection, like minVal, maxVal, activeIndex
getOptionsMeta :: Options -> (Int, Int, Maybe Int)
getOptionsMeta options = (0, length options - 1, getFocusedOptionIndex options)

-- | Convert a string array to
stringsToOptions :: [String] -> Options
stringsToOptions options = [
    Option { value = o, isFocused = False, isSelected = False } | o <- options
  ]

-- | Give the focus to a single option in the collection
focusOption :: Int -> Options -> Options
focusOption focusedIndex = map' $ \ i o ->
  Option {
    value = getOptionValue o,
    isSelected = getOptionIsSelected o,
    isFocused = focusedIndex == i
  }
-- | Toggle the isSelected flag for a single option
toggleFocusedOption :: Int -> Options -> Options
toggleFocusedOption focusedIndex = map' $ \ i o ->
  Option {
    value = getOptionValue o,
    isFocused = focusedIndex == i,
    isSelected = if focusedIndex == i then
      not $ getOptionIsSelected o
      else getOptionIsSelected o
  }

-- | Print a list to comma separated
toCommaSeparatedString :: [String] -> String
toCommaSeparatedString = intercalate ", "

-- | Flush the output buffer
flush :: IO()
flush = hFlush stdout

-- | Get the value of any option
getOptionValue :: Option -> String
getOptionValue Option { value } = value

-- | Get the is focused attribute of any option
getOptionIsFocused :: Option -> Bool
getOptionIsFocused Option { isFocused } = isFocused

-- | Get the is selected attribute of any option
getOptionIsSelected :: Option -> Bool
getOptionIsSelected Option { isSelected } = isSelected

-- | Get the index of the option selected
getFocusedOptionIndex :: Options -> Maybe Int
getFocusedOptionIndex = findIndex getOptionIsFocused

-- | Filter the indexes of the options selected
getSelecteOptionsIndexes :: Options -> [Int]
getSelecteOptionsIndexes = findIndices getOptionIsSelected