{-# language
     Rank2Types
  , TemplateHaskell
  , OverloadedStrings
  , ExistentialQuantification
  , ScopedTypeVariables
  , GeneralizedNewtypeDeriving
  , FlexibleInstances
  , StandaloneDeriving
  #-}

module Rasa.Internal.Buffer
  ( Buffer
  , BufAction
  , HasBuffer(..)
  , BufRef(..)
  , text
  , mkBuffer
  , ref

  , buffers
  , nextBufId

  ) where


import Eve

import qualified Yi.Rope as Y
import Control.Lens hiding (matching)
import qualified Data.Map as M
import qualified Data.IntMap as IM
import Data.List
import Data.Default

-- | An opaque reference to a buffer.
-- When operating over a BufRef Rasa checks if the 'Rasa.Internal.Buffer.Buffer' still
-- exists and simply ignores any operations over non-existent buffers; typically returning 'Nothing'
newtype BufRef =
  BufRef Int
  deriving (Show, Eq, Ord)

newtype NextBufId = NextBufId
  { _nextBufId' :: Int 
  } deriving Show

instance Default NextBufId where
  def = NextBufId 0

makeLenses ''NextBufId
nextBufId :: HasStates s => Lens' s Int
nextBufId = stateLens.nextBufId'

-- | A buffer, holds the text in the buffer and any extension states that are set on the buffer.
data Buffer = Buffer
  { _text' :: Y.YiString
  , _bufStates' :: States
  , _ref :: BufRef
  }
makeLenses ''Buffer

instance HasStates Buffer where
  states = bufStates'

instance HasEvents Buffer where

-- | This allows creation of polymorphic lenses over any type which has access to a Buffer
class HasBuffer a where
  buffer :: Lens' a Buffer

instance HasBuffer Buffer where
  buffer = lens id (flip const)

-- | This lens focuses the text of the in-scope buffer.
text :: HasBuffer b => Lens' b Y.YiString
text = buffer.text'

instance Show Buffer where
  show b = "text:" ++ (Y.toString . Y.take 30 $ (b^.text)) ++ "...,\n"
           ++ "exts: " ++ extText ++ "}>\n"
    where
      extText = intercalate "\n" $ show <$> b^.states.to M.toList

type BufAction a = Action Buffer a

newtype Buffers = Buffers
  { _buffers' :: IM.IntMap Buffer
  } deriving Show
makeLenses '' Buffers

instance Default Buffers where
  def = Buffers mempty

-- | A lens over the map of available buffers
buffers :: HasStates s => Lens' s (IM.IntMap Buffer)
buffers = stateLens.buffers'

-- | Creates a new buffer from the given text.
mkBuffer :: Y.YiString -> BufRef -> Buffer
mkBuffer txt bRef =
  Buffer
    { _text' = txt
    , _bufStates' = mempty
    , _ref = bRef
    }