{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE QuasiQuotes #-} -- for pun
{-# LANGUAGE TemplateHaskell #-}
module HListExample.Prism where


import Test.Hspec
import Properties.Common

import Data.HList.CommonMain
import Data.HList.Labelable (hLens')
import Control.Lens

-- generate left = Label :: Label "left"
makeLabels6 (words "left right up down")

--- define the Labelable labels manually
left_ = hLens' left
right_ = hLens' right
up_ = hLens' up
down_ = hLens' down

-- this definition is needed to decide what order
-- to put the fields in, as well as their initial types
r = [pun|right left up|] where
  left = 'a'
  right = 2 :: Int
  up = 2.3 :: Double

r2 = down_ .==. v .*. r

v = mkVariant left 'x' r

mainPrism = do
  it "inspect v with hPrism" $ do
    v ^? hPrism left `shouldShowTo` "Just 'x'"
    v ^? hPrism right `shouldBe` Nothing
    v ^? hPrism up `shouldBe` Nothing
    v2 ^? hPrism left `shouldShowTo` "Just ()"

  it "inspect v with hPrism through Labelable" $ do
    v ^? left_ `shouldShowTo` "Just 'x'"
    v ^? right_ `shouldBe` Nothing
    v ^? up_ `shouldBe` Nothing
    v2 ^? left_ `shouldShowTo` "Just ()"

  it "Setting the missing tag does nothing" $ do
    set right_ () v `shouldShowTo` "V{left='x'}"

    set _Right () (Left 'x') -- prisms for Either do the same thing
      `shouldShowTo` "Left 'x'"

  it "compose prism" $ do
    v3 ^? up_.up_ `shouldBe` Nothing
    v3 ^? left_ `shouldShowTo` "Just 'x'"

    v4 ^? left_.left_ `shouldShowTo` "Just \"leftleft\""

  it "compose lens.prism" $ do
    r2 ^? down_.left_ `shouldShowTo` "Just 'x'"
    r2 ^? down_.right_ `shouldBe` Nothing

    let du = down_.up_
    r2 ^? du `shouldBe` Nothing

  it "extension" $ do
    v5 ^? down_ `shouldBe` Just "hi"
    v6 ^? down_ `shouldBe` Just "hi"
    v7 ^? down_ `shouldBe` Nothing
    v7 ^? left_ `shouldBe` Just 'x'

  it "show" $ do
    vs `shouldShowTo`
        "Record{v=V{left='x'},\
        \v2=V{left=()},\
        \v2'=V{left=()},\
        \v3=V{left='x'},\
        \v4=V{left=V{left=\"leftleft\"}},\
        \v5=V{down=\"hi\"},\
        \v6=V{down=\"hi\"},\
        \v7=V{left='x'}}"

    -- works in ghci. Probably need -XExtendedDefaultRules
    -- wX `shouldShowTo` "V{x='a'}"
    -- wY `shouldShowTo` "V{y=2.5}"
    [wX,wY] `shouldShowTo` "[V{x='a'},V{y=2.5}]"

  -- :t wX
  -- > wX :: Variant '[Tagged "x" Char, Tagged "y" y]
  --
  -- > :t wY
  -- > wY :: Variant '[Tagged "x" x, Tagged "y" Double]
  --
  -- ghc doesn't need to decide on a type for values that
  -- have no influence on the final result
  it "type partly defined" $ do
    wX ^? hLens' (Label :: Label "x")
        `shouldShowTo` "Just 'a'"
    wY ^? hLens' (Label :: Label "y")
        `shouldShowTo` "Just 2.5"
  

wX = mkVariant (Label :: Label "x") 'a' wProto
wY = mkVariant (Label :: Label "y") (2.5 :: Double) wProto

wProto = undefined :: Record
  '[Tagged "x" x, Tagged "y" y]

vs = [pun| v v2 v2' v3 v4 v5 v6 v7 |]

-- note that we can change the type of the 'x' field
-- from Char to ()
v2 = set (hPrism left) () v


-- or with the "better" label
v2' = set left_ () v


v3 = v & up_ .~ v & up_.up_ .~ "upup"
v4 = v & left_ .~ v & left_.left_ .~ "leftleft"
v5 = down .=. Just "hi" .*. v
v6 = down_ .==. Just "hi" .*. v
v7 = down .=. (Nothing :: Maybe String) .*. v