content: module Cornelis.Subscripts where
  
  import           Cornelis.Offsets
  import           Cornelis.Vim (getWindowCursor, getBufferLine, replaceInterval, setWindowCursor, reportError)
  import           Data.Foldable (asum, foldl', for_)
  import           Data.Map (Map)
  import qualified Data.Map as M
  import           Data.Maybe (fromMaybe)
  import           Data.Proxy
  import qualified Data.Text as T
  import           Data.Void (Void)
  import           Neovim (Neovim)
  import           Neovim.API.Text (vim_get_current_window, window_get_buffer)
  import           Text.Megaparsec
  
  type Parser = Parsec Void T.Text
  
  
  data Flavor a
    = Digits a
    | Subscript a
    | Superscript a
    deriving (Eq, Ord, Show, Functor)
  
  extract :: Flavor a -> a
  extract (Digits a) = a
  extract (Subscript a) = a
  extract (Superscript a) = a
  
  parseNum :: Num a => Flavor (Char, String) -> Parser (Flavor a)
  parseNum f = do
    r <- option id (negate <$ satisfy (== fst (extract f)) ) <*> parseDigits (fmap snd f)
    pure $ r <$ f
  
  
  parseDigits :: Num a => Flavor String -> Parser a
  parseDigits fv = mkNum <$> takeWhile1P (Just "digit") (flip elem $ extract fv)
    where
      mkNum = foldl' step 0 . chunkToTokens (Proxy :: Proxy T.Text)
      step a c = a * 10 + fromIntegral (digitToInt fv c)
  
  
  digitToInt :: Flavor String -> Char -> Int
  digitToInt fv
    = fromMaybe (error "digitToInt: not a digit")
    . flip lookup (zip (extract fv) [0..])
  
  
  subscripts :: Flavor (Char, String)
  subscripts = Subscript ('₋', "₀₁₂₃₄₅₆₇₈₉")
  
  
  superscripts :: Flavor (Char, String)
  superscripts = Superscript ('⁻', "⁰¹²³⁴⁵⁶⁷⁸⁹")
  
  
  digits :: Flavor (Char, String)
  digits = Digits ('-', "0123456789")
  
  
  mkReplacement :: (Char, String) -> (Char, String) -> Map Char Char
  mkReplacement (m1, s1) (m2, s2) = M.fromList $ zip (m1 : s1) (m2 : s2)
  
  
  replace :: Map Char Char -> String -> String
  replace m = fmap (\c -> fromMaybe c $ M.lookup c m)
  
  
  parseFlavor :: Parser (Flavor Int)
  parseFlavor = asum
    [ parseNum digits
    , parseNum superscripts
    , parseNum subscripts
    ]
  
  parseLine :: Parser (String, Flavor Int)
  parseLine = manyTill_ anySingle $ try parseFlavor
  
  
  unparse :: Flavor Int -> String
  unparse (Digits n) = unparseWith (extract digits) n
  unparse (Subscript n) = unparseWith (extract subscripts) n
  unparse (Superscript n) = unparseWith (extract superscripts) n
  
  unparseWith :: (Char, String) -> Int -> String
  unparseWith to n =
    let from = extract digits
        rep = mkReplacement from to
     in replace rep $ show n
  
  
  applyOver :: (Int -> Int) -> Parser (T.Text, (Int, Int))
  applyOver f = do
    (start_str, fv) <- parseLine
    let n = unparse $ fmap f fv
        start = T.pack start_str
    pure
      ( T.pack n
      , (T.length start, length $ show $ extract fv)
      )
  
  
  incNextDigitSeq :: Neovim env ()
  incNextDigitSeq = overNextDigitSeq (+1)
  
  
  decNextDigitSeq :: Neovim env ()
  decNextDigitSeq = overNextDigitSeq (subtract 1)
  
  
  overNextDigitSeq ::  (Int -> Int) -> Neovim env ()
  overNextDigitSeq f = do
    w <- vim_get_current_window
    b <- window_get_buffer w
    Pos line col <- getWindowCursor w
    txt <- getBufferLine b (zeroIndex line)
    let later = T.drop (fromZeroIndexed (zeroIndex col)) txt
  
    reportError $ T.pack $ show later
    for_ (parse (applyOver f) "" later) $ \(result, (before, target)) ->  do
      reportError result
      let start_col = col .+ Offset before
          end_col = start_col .+ Offset target
          start_pos = Pos line start_col
          end_pos = Pos line end_col
  
      replaceInterval b (Interval start_pos end_pos) result
  
      setWindowCursor w start_pos
  
  