{-# LANGUAGE ScopedTypeVariables #-}

-- | 'JSSelector' are used to access fields of Javascript objects.
module Language.Sunroof.Selector
  ( JSSelector
  , label, index
  , unboxSelector
  , (!)
  ) where

import Data.String ( IsString(..) )
import Data.Proxy ( Proxy(Proxy) )

import Language.Sunroof.JavaScript ( Expr, E(Dot), ExprE(ExprE) )
import Language.Sunroof.Classes ( Sunroof(..) )
import Language.Sunroof.JS.String ( JSString, string )
import Language.Sunroof.JS.Number ( JSNumber )

-- | A 'JSSelector' selects a field or attribute from a Javascript object.
--   The phantom type is the type of the selected value. Note the selected 
--   field or attributes may also array entries ('index').
data JSSelector a = JSSelector Expr

-- | Selectors can be created from the name of their attribute.
instance IsString (JSSelector a) where
  fromString = JSSelector . unbox . string

instance Show (JSSelector a) where
  show (JSSelector ids) = show ids

-- | Create a selector for a named field or attribute.
--   For type safty it is adivsed to use this with an 
--   accompanying type signature. Example:
--   
-- > array ! label "length"
--   
--   See '!' for further information on usage.
label :: JSString -> JSSelector a
label = JSSelector . unbox

-- | Create a selector for an indexed value (e.g. array access).
--   For type safty it is adivsed to use this with an 
--   accompanying type signature. Example:
--   
-- > array ! index 4
--   
--   See '!' for further information on usage.
index :: JSNumber -> JSSelector a
index = JSSelector . unbox

-- | Provided for internal usage by the compiler. Unwraps the 
--   selector.
unboxSelector :: JSSelector a -> Expr
unboxSelector (JSSelector e) = e

---------------------------------------------------------------

infixl 1 !

-- | Operator to use a selector on a Javascript object. Examples:
--   
-- > array ! label "length"
-- > array ! index 4
(!) :: forall o a . (Sunroof o, Sunroof a) => o -> JSSelector a -> a
(!) arr (JSSelector idx) = box $ Dot (ExprE $ unbox arr) 
                                     (ExprE $ idx) 
                                     (typeOf (Proxy :: Proxy a))