{-# LANGUAGE OverloadedStrings #-}

module Text.Authoring.Combinator.Ref where

import           Control.Lens
import           Control.Monad.State
import           Control.Monad.Writer
import           Data.Char (isAlphaNum)
import qualified Data.Map as Map
import qualified Data.Set as Set
import           Data.Text (Text, pack)

import Text.Authoring.Document
import Text.Authoring.State
import Text.Authoring.Label
import Text.Authoring.Combinator.Writer
import Text.Authoring.Combinator.Meta

-- | refer to a label.  
ref :: (MonadState s m, HasAuthorState s, MonadWriter w m, HasDocument w) => Label -> m ()
ref lab = do
  ret <- getReference lab
  command1 "ref" $ raw ret

-- | insert a label.
label :: (MonadState s m, HasAuthorState s, MonadWriter w m, HasDocument w) => Label -> m ()
label lab = do
  ret <- getReference lab
  command1 "label" $ raw ret

-- | an interface for mapping any Label to a unique, LaTeX-safe text.

getReference :: (MonadState s m, HasAuthorState s) => Label -> m Text
getReference lab = do
  map0 <- use labelMap
  case Map.lookup lab map0 of
    Just str -> return str
    Nothing  -> do
      let cands :: [String]
          cands = cand0 : [s ++ [c]| s <- cands, c <- ['0'..'9']]
          cand0 = map modify $ show lab

          modify :: Char -> Char
          modify c
            | isAlphaNum c = c
            | otherwise    = '.'
                             
          takens :: Set.Set Text
          takens = Set.fromList $ Map.elems map0
          
          isTaken x = Set.member (pack x) takens

          freeStr :: Text
          freeStr = pack $ head $ filter (not . isTaken) cands
      labelMap %= (Map.insert lab freeStr)
      return freeStr