module Rasa.Ext.Cursors.Internal.Actions
  ( delete
  , insertText
  , findNext
  , findPrev
  , findNextFrom
  , findPrevFrom
  , moveRangesByN
  , moveRangesByC
  ) where

import qualified Yi.Rope as Y

import Control.Lens
import Control.Lens.Text
import Rasa.Ext
import Rasa.Ext.Cursors.Internal.Base

-- | Moves all Ranges that are on the same END row as the given range by the coord's row and column
-- This is used to adjust cursors when things have been inserted/deleted before them in the row.
moveSameLineRangesBy :: CrdRange -> Coord -> BufAction ()
moveSameLineRangesBy (Range _ (Coord endRow endCol)) amt = do
  let moveInLine r@(Range (Coord startRow startCol) _) = return $
        if endRow == startRow && startCol > endCol
           then moveRange amt r
           else r
  rangeDo moveInLine >>= setRanges

-- | Delete the text of all ranges in a buffer
delete :: BufAction ()
delete = rangeDo_ $ \r -> do
  deleteRange r
  moveSameLineRangesBy r (negate $ sizeOfR r)

-- | Insert text at the beginning of all ranges in the buffer.
insertText :: Y.YiString -> BufAction ()
insertText txt = rangeDo_ $ \r@(Range s _) -> do
  insertAt s txt
  moveSameLineRangesBy r (Coord 0 (Y.length txt))

-- | Move all ranges to the location of the next occurence of the given text.
findNext :: Y.YiString -> BufAction ()
findNext pat = do
  res <- rangeDo $ \(Range _ e) -> do
    off <- findNextFrom pat e
    let end = moveCursorByN 1 off
    return $ Range off end
  setRanges res

-- | Get the 'Coord' of the next occurence of the given text after the given 'Coord'
findNextFrom :: Y.YiString -> Coord -> BufAction Coord
findNextFrom pat c = do
  txt <- getText
  let distance = txt ^. afterC c . asText . tillNext (Y.toText pat) . from asText . to sizeOf
  return (distance + c)

-- | Move all ranges to the location of the previous occurence of the given text.
findPrev :: Y.YiString -> BufAction ()
findPrev pat = do
  res <- rangeDo $ \(Range s _) -> do
    off <- findPrevFrom pat s
    let end = moveCursorByN 1 off
    return $ Range off end
  setRanges res

-- | Get the 'Coord' of the previous occurence of the given text before the given 'Coord'
findPrevFrom :: Y.YiString -> Coord -> BufAction Coord
findPrevFrom pat c = do
  txt <- getText
  let distance = txt ^. beforeC c . asText . tillPrev (Y.toText pat) . from asText . to sizeOf
  return (c - distance)

  -- let Offset o = c^.from (asCoord txt)
      -- distance = txt ^. asText . before o . tillPrev (Y.toText pat) . to T.length .to negate
  -- return ((Offset $ distance + o)^.asCoord txt)

-- | Move all ranges by the given number of columns
moveRangesByN :: Int -> BufAction ()
moveRangesByN n = overEachRange $ return . moveRangeByN n

-- | Move all ranges by the given number of rows and columns
moveRangesByC :: Coord -> BufAction ()
moveRangesByC c = overEachRange $ return . moveRange c