--------------------------------------------------------------------------------
--  See end of this file for licence information.
--------------------------------------------------------------------------------
-- |
--  Module      :  Datatype
--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012, 2018 Douglas Burke
--  License     :  GPL V2
--
--  Maintainer  :  Douglas Burke
--  Stability   :  experimental
--  Portability :  H98
--
--  This module defines the structures used by Swish to represent and
--  manipulate RDF datatypes.
--
--------------------------------------------------------------------------------

module Swish.RDF.Datatype
    ( RDFDatatype
    , RDFDatatypeVal
    , RDFDatatypeMod
    , RDFModifierFn, RDFApplyModifier
    , makeRdfDtOpenVarBindingModify, makeRdfDtOpenVarBindingModifiers
    , applyRDFDatatypeMod
    , RDFDatatypeSub
    , fromRDFLabel, toRDFLabel, makeDatatypedLiteral
    )
where

import Swish.Datatype
    ( Datatype
    , DatatypeVal(..)
    , DatatypeMap(..)
    , DatatypeMod(..), ModifierFn
    , ApplyModifier
    , DatatypeSub(..)
    )
import Swish.Namespace (ScopedName)
import Swish.VarBinding (VarBindingModify(..))

import Swish.RDF.Graph
    ( RDFLabel(..)
    , isDatatyped
    , getLiteralText
    , RDFGraph
    )

import Swish.RDF.VarBinding (RDFVarBinding, RDFOpenVarBindingModify)

import Data.Maybe (fromMaybe, isJust, fromJust)

import qualified Data.Text as T

------------------------------------------------------------
--  Specialize datatype framework types for use with RDF
------------------------------------------------------------

-- |RDF datatype wrapper used with RDF graph values
--
type RDFDatatype = Datatype RDFGraph RDFLabel RDFLabel

-- |RDF datatype value used with RDF graph values
--
type RDFDatatypeVal vt = DatatypeVal RDFGraph vt RDFLabel RDFLabel

-- |RDF datatype modifier used with RDF graph values
--
type RDFDatatypeMod vt = DatatypeMod vt RDFLabel RDFLabel

-- |Describe a subtype/supertype relationship between a pair
--  of RDF datatypes.
--
type RDFDatatypeSub supvt subvt = DatatypeSub RDFGraph RDFLabel RDFLabel supvt subvt

-- |RDF value modifier function type
--
--  This indicates a modifier function that operates on 'RDFLabel' values.
--
type RDFModifierFn = ModifierFn RDFLabel

-- |RDF value modifier application function type
--
--  This indicates a function that applies RDFModifierFn functions.
--
type RDFApplyModifier = ApplyModifier RDFLabel RDFLabel

--------------------------------------------------------------
--  Functions for creating datatype variable binding modifiers
--------------------------------------------------------------

-- |Create an 'RDFOpenVarBindingModify' value.
--
--  The key purpose of this function is to lift the supplied
--  variable constraint functions from operating on data values directly
--  to a corresponding list of functions that operate on values contained
--  in RDF graph labels (i.e. RDF literal nodes).  It also applies
--  node type checking, such that if the actual RDF nodes supplied do
--  not contain appropriate values then the variable binding is not
--  accepted.
--
makeRdfDtOpenVarBindingModify ::
    RDFDatatypeVal vt
    -- ^ is an 'RDFDatatype' value containing details of the datatype
    --   for which a variable binding modifier is created.
    -> RDFDatatypeMod vt
    -- ^ is the data value modifier value that defines the calculations
    --   that are used to implement a variable binding modifier.
    -> RDFOpenVarBindingModify
makeRdfDtOpenVarBindingModify dtval dtmod =
    dmAppf dtmod (dmName dtmod) $ map (makeRDFModifierFn dtval) (dmModf dtmod)

-- |Create all RDFOpenVarBindingModify values for a given datatype value.
--  See 'makeRdfDtOpenVarBindingModify'.
--
makeRdfDtOpenVarBindingModifiers ::
    RDFDatatypeVal vt
    -- ^  is an 'RDFDatatype' value containing details of the datatype
    --    for which variable binding modifiers are created.
    -> [RDFOpenVarBindingModify]
makeRdfDtOpenVarBindingModifiers dtval =
    map (makeRdfDtOpenVarBindingModify dtval) (tvalMod dtval)

-- |Apply a datatype modifier using supplied RDF labels to a supplied
--  RDF variable binding.
--
applyRDFDatatypeMod ::
    RDFDatatypeVal vt -> RDFDatatypeMod vt -> [RDFLabel] -> [RDFVarBinding]
    -> [RDFVarBinding]
applyRDFDatatypeMod dtval dtmod lbs =
    vbmApply (makeRdfDtOpenVarBindingModify dtval dtmod lbs)

-- |Given details of a datatype and a single value constraint function,
--  return a new constraint function that operates on 'RDFLabel' values.
--
--  The returned constraint function incorporates checks for appropriately
--  typed literal nodes, and returns similarly typed literal nodes.
--
makeRDFModifierFn ::
    RDFDatatypeVal vt -> ModifierFn vt -> RDFModifierFn
makeRDFModifierFn dtval fn ivs =
    let
        ivals = mapM (fromRDFLabel dtval) ivs
        ovals | isJust ivals = fn (fromJust ivals)
              | otherwise    = []
    in
        fromMaybe [] $ mapM (toRDFLabel dtval) ovals

------------------------------------------------------------
--  Helpers to map between datatype values and RDFLabels
------------------------------------------------------------

-- | Convert from a typed literal to a Haskell value,
-- with the possibility of failure.
fromRDFLabel ::
    RDFDatatypeVal vt -> RDFLabel -> Maybe vt
fromRDFLabel dtv lab
    | isDatatyped dtnam lab = mapL2V dtmap $ getLiteralText lab
    | otherwise             = Nothing
    where
        dtnam = tvalName dtv
        dtmap = tvalMap dtv

-- | Convert a Haskell value to a typed literal (label),
-- with the possibility of failure.
toRDFLabel :: RDFDatatypeVal vt -> vt -> Maybe RDFLabel
toRDFLabel dtv =
    fmap (makeDatatypedLiteral dtnam) . mapV2L dtmap
    where
        dtnam = tvalName dtv
        dtmap = tvalMap dtv

-- | Create a typed literal. No conversion is made to the
-- string representation.
makeDatatypedLiteral ::
    ScopedName   -- ^ data type
    -> T.Text    -- ^ string form of the value
    -> RDFLabel
makeDatatypedLiteral = flip TypedLit

--------------------------------------------------------------------------------
--
--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,
--    2011, 2012, 2018 Douglas Burke
--  All rights reserved.
--
--  This file is part of Swish.
--
--  Swish is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  Swish is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with Swish; if not, write to:
--    The Free Software Foundation, Inc.,
--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--
--------------------------------------------------------------------------------