{-# LANGUAGE FlexibleContexts, TypeFamilies, TypeOperators #-}
module Data.Lens.Edit.Container where

import Data.Container
import Data.Default
import Data.Iso
import Data.Lens.Bidirectional
import Data.Lens.Edit.Stateful (C) -- needed for GHC 7.2
import Data.Module.Class
import Data.Module.Container
import qualified Data.Lens.Edit.Stateful  as F -- state_f_ul
import qualified Data.Lens.Edit.Stateless as L -- state_l_ess
import qualified Data.Set                 as S

data Map shape l = Map l deriving (Eq, Ord, Show, Read)

instance Bidirectional l => Bidirectional (Map shape l) where
	type L (Map shape l) = [ContainerAtom shape (L l)]
	type R (Map shape l) = [ContainerAtom shape (R l)]

instance (ContainerType shape, F.Lens l) => F.Lens (Map shape l) where
	type C (Map shape l) = Container shape (F.C l)
	missing (Map l) = Container def (const (F.missing l))
	dputr (Map l) = F.foldState (dputMapF F.dputr l)
	dputl (Map l) = F.foldState (dputMapF F.dputl l)

instance (ContainerType shape, L.Lens l) => L.Lens (Map shape l) where
	dputr (Map l) = map (dputMapL L.dputr l)
	dputl (Map l) = map (dputMapL L.dputl l)

dputMapF dput l FailContainer c = ([FailContainer], c)
dputMapF dput l (Modify p dx) c
	| S.member p (live (currentShape c)) = ([Modify p dy], replace p c' c)
	| otherwise = ([FailContainer], c)
	where (dy, c') = dput l (dx, containedValues c p)
dputMapF dput l (Insert ds) c = case apply ds (currentShape c) of
	Nothing -> ([FailContainer], c)
	Just s  -> ([Insert ds], expand s (F.missing l) c)
dputMapF dput l (Delete ds) c = case apply ds (currentShape c) of
	Nothing -> ([FailContainer], c)
	Just s  -> ([Delete ds], setShape s c)
dputMapF dput l (Rearrange ds f) c = case apply ds (currentShape c) of
	Nothing -> ([FailContainer], c)
	Just s  -> ([Rearrange ds f], reorder f s c)

dputMapL dput l (FailContainer ) = FailContainer
dputMapL dput l (Modify    p dx) = Modify p (dput l dx)
dputMapL dput l (Insert    ds  ) = Insert ds
dputMapL dput l (Delete    ds  ) = Delete ds
dputMapL dput l (Rearrange ds f) = Rearrange ds f

{-
data Reshape l x = Reshape (L l -> F.C l -> R l -> Iso (P (L l)) (P (R l))) l

instance Bidirectional (Reshape l x) where
	type L (Reshape l x) = [ContainerAtom (L l) x]
	type R (Reshape l x) = [ContainerAtom (R l) x]

instance
	( ContainerType (L l)
	, ContainerType (R l)
	, F.Lens l
	) => F.Lens (Reshape l x) where
	type C (Reshape l x) = (L l, F.C l, R l) -- these will be consistent according to the underlying lens l
	missing (Reshape iso l) = (def, F.missing l, def)
	-- TODO: the paper doesn't seem to actually finish defining dputr/dputl... (?)
-}