module Model.Types.File
  ( File(..), _fileTitle, _fileContent, _fileModified
  , FileReference(..), _fileReferenceTitle, _fileReferenceContent, _fileReferenceModified
  , HasFileReference(..), IsFileReference(..), FileReferenceResidual(..)
  ) where

import Import.NoModel
import Model.Types.Common (FileContentReference)

import Utils.Lens.TH


data File = File
  { fileTitle    :: FilePath
  , fileContent  :: Maybe ByteString
  , fileModified :: UTCTime
  } deriving (Eq, Ord, Read, Show, Generic, Typeable)

makeLenses_ ''File

data FileReference = FileReference
  { fileReferenceTitle    :: FilePath
  , fileReferenceContent  :: Maybe FileContentReference
  , fileReferenceModified :: UTCTime
  } deriving (Eq, Ord, Read, Show, Generic, Typeable)

makeLenses_ ''FileReference


class HasFileReference record where
  data FileReferenceResidual record :: Type

  _FileReference :: Iso' record (FileReference, FileReferenceResidual record)

instance HasFileReference FileReference where
  data FileReferenceResidual FileReference = FileReferenceResidual
  _FileReference = iso (, FileReferenceResidual) $ view _1

instance (HasFileReference a, HasFileReference b) => HasFileReference (Either a b) where
  newtype FileReferenceResidual (Either a b) = FileReferenceResidualEither { unFileReferenceResidualEither :: Either (FileReferenceResidual a) (FileReferenceResidual b) }
  _FileReference = iso doSplit doJoin
    where doSplit (Right r) = over _2 (FileReferenceResidualEither . Right) $ r ^. _FileReference
          doSplit (Left  r) = over _2 (FileReferenceResidualEither . Left ) $ r ^. _FileReference
          doJoin (fRef, FileReferenceResidualEither (Right res)) = Right $ _FileReference # (fRef, res)
          doJoin (fRef, FileReferenceResidualEither (Left  res)) = Left  $ _FileReference # (fRef, res)

instance HasFileReference record => HasFileReference (Entity record) where
  data FileReferenceResidual (Entity record) = FileReferenceResidualEntity
    { fileReferenceResidualEntityKey :: Key record
    , fileReferenceResidualEntityResidual :: FileReferenceResidual record
    }
  _FileReference = iso doSplit doJoin
    where doSplit Entity{..} = (fRef, FileReferenceResidualEntity entityKey res)
            where (fRef, res) = entityVal ^. _FileReference
          doJoin (fRef, FileReferenceResidualEntity entityKey res) = Entity{ entityVal = _FileReference # (fRef, res), .. }

class (PersistEntity record, HasFileReference record) => IsFileReference record where
  fileReferenceTitleField    :: EntityField record FilePath
  fileReferenceContentField  :: EntityField record (Maybe FileContentReference)
  fileReferenceModifiedField :: EntityField record UTCTime
