module Data.HMemDb.TableVarId (TableVarId, idToVar, tableIds, tvIdTarget, varToId) where
import Control.Compose (Id(Id), unId)
import Control.Concurrent.STM (readTVar)
import Control.Monad.Trans.Class (lift)
import Data.Function (on)
import qualified Data.Map as M (lookup)
import Data.HMemDb.Binary (MS)
import Data.HMemDb.ForeignKeys (ForeignKey(ForeignKey))
import Data.HMemDb.References (Ref(Ref, refContent, refIndex))
import Data.HMemDb.Tables (Table(Table), tabContent)
import Data.HMemDb.TableVars (TableVar, TableVarS(TableVar))
import Data.HMemDb.Utils (liftMaybe)
data TableVarId a =
    TableVarId
    {
      unTVId :: Integer,
      tvIdTarget :: Table a -- ^ 'Table' this id points to.
    }
-- ^ This type can be used for columns in a table.
-- It's sort of a 'ForeignKey' accompanied with a specific index.
instance Eq (TableVarId a) where (==) = (==) `on` unTVId
-- ^ 'TableVarId's pointing to different 'Table's may accidentally appear equal.
instance Ord (TableVarId a) where compare = compare `on` unTVId
varToId :: TableVar a -> TableVarId a
-- ^ This function gets an Id of the variable.
varToId (TableVar iref pt) =
    TableVarId {unTVId = refIndex (unId iref), tvIdTarget = Table pt}
idToVar :: TableVarId a -> MS (TableVar a)
-- ^ This function looks up the specific Id in the 'Table'.
idToVar tvId =
    case tvIdTarget tvId of
      Table pt ->
          do mp <- lift $ readTVar $ tabContent pt
             let i = unTVId tvId
             tv <- liftMaybe $ M.lookup i mp
             return $ TableVar (Id Ref {refContent = tv, refIndex = i}) pt
tableIds :: Table a -> ForeignKey Id (TableVarId a) a
-- ^ If you want to use 'TableVarId' as a column type in a table,
-- you'd need a 'ForeignKey'. This function provides one.
tableIds (Table pt) =
    ForeignKey pt $ \tvId -> do
      mp <- lift (readTVar $ tabContent pt)
      let i = unTVId tvId
      tv <- liftMaybe $ M.lookup i mp
      return $ Id Ref {refContent = tv, refIndex = i}