module Data.Woot.WString
    ( WString
    -- Construction
    , emptyWString
    , fromList
    , toList

    -- General WString operations
    , lengthWS
    , (!?)
    , indexOf
    , insert
    , subsection
    , contains
    , isEmpty
    , nthVisible

    -- Special utilities
    , hideChar
    ) where


import           Data.Maybe  (fromMaybe, isJust)
import qualified Data.Vector as V

import Data.Woot.WChar


newtype WString = WString { wStringChars :: V.Vector WChar } deriving (Eq)


instance Show WString where
    -- get the visible characters, then remove any special characters
    -- TODO: this could be more efficient by composing (init . tail) in vector form
    -- (we know that the only special characters are at the beginning and end of the string)
    show = fmap wCharAlpha . toList . visibleChars


emptyWString :: WString
emptyWString = fromList [wCharBeginning, wCharEnding]


fromList :: [WChar] -> WString
fromList = WString . V.fromList


toList :: WString -> [WChar]
toList = V.toList . wStringChars


lengthWS :: WString -> Int
lengthWS = V.length . wStringChars


-- unsafe
(!) :: WString -> Int -> WChar
(!) ws n = wStringChars ws V.! n


(!?) :: WString -> Int -> Maybe WChar
(!?) ws n = wStringChars ws V.!? n


indexOf :: WCharId -> WString -> Maybe Int
indexOf wcid = V.findIndex ((==) wcid . wCharId) . wStringChars


-- insert before index i
-- insert 2 'x' "abc" -> abxc
insert :: WChar -> Int -> WString -> WString
insert wc i (WString wcs) = WString $ V.concat [V.take i wcs, V.singleton wc, V.drop i wcs]


-- returns the subsequence between the two provided elements, both not included
subsection :: WCharId -> WCharId -> WString -> WString
subsection prev next ws = WString . fromMaybe V.empty $ do
    i <- indexOf prev ws
    j <- indexOf next ws
    return $ slice' i (j - i) (wStringChars ws)
  where
    -- safe version of slice - returns empty when passed illegal indices
    slice' i n = V.take n . V.drop i


contains :: WCharId -> WString -> Bool
contains wcid ws = isJust $ indexOf wcid ws


visibleChars :: WString -> WString
visibleChars = WString . V.filter wCharVisible . wStringChars


nthVisible :: Int -> WString -> Maybe WChar
nthVisible n = (!? n) . visibleChars


isEmpty :: WString -> Bool
isEmpty = V.null . wStringChars


hideChar :: WCharId -> WString -> WString
hideChar wid ws@(WString wcs) = WString $
    maybe wcs (\i -> wcs V.// [(i, hide $ ws ! i)]) mindex
  where
    mindex = indexOf wid ws