{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}

module Text.HTML.TagSoup.Navigate.Types.TagTreePos(
  TagTreePos(..)
, HasTagTreePos(..)
, AsTagTreePos(..)
, tagsoupTagTreePos
, fromTagTree
, toTagTree
) where

import Control.Applicative((<*>))
import Control.Category((.), id)
import Control.Lens(Each(each), Lens', Prism', Iso, iso, view, from)
import Data.Eq(Eq)
import Data.Eq.Deriving(deriveEq1)
import Data.Foldable(Foldable(foldMap))
import Data.Functor(Functor(fmap), (<$>))
import Data.Ord(Ord)
import Data.Ord.Deriving(deriveOrd1)
import Data.Monoid(mappend)
import Data.Traversable(Traversable(traverse))
import Prelude(Show)
import Text.HTML.TagSoup.Navigate.Types.Attribute(tagsoupAttribute)
import Text.HTML.TagSoup.Navigate.Types.TagTree(TagTree, tagsoupTagTree)
import Text.HTML.TagSoup.Navigate.Types.TagTreePosParent(TagTreePosParent(TagTreePosParent))
import qualified Text.HTML.TagSoup.Tree.Zipper as TagSoup(TagTreePos(TagTreePos), fromTagTree, root)
import Text.Show.Deriving(deriveShow1)

data TagTreePos str =
  TagTreePos
    (TagTree str)
    [TagTree str]
    [TagTree str]
    [TagTreePosParent str]
  deriving (Eq, Ord, Show)

instance Functor TagTreePos where
  fmap f (TagTreePos c b a p) =
    TagTreePos (fmap f c) (fmap (fmap f) b) (fmap (fmap f) a) (fmap (fmap f) p)

instance Foldable TagTreePos where
  foldMap f (TagTreePos c b a p) =
    foldMap f c `mappend` foldMap (foldMap f) b `mappend` foldMap (foldMap f) a `mappend` foldMap (foldMap f) p

instance Traversable TagTreePos where
  traverse f (TagTreePos c b a p) =
    TagTreePos <$> traverse f c <*> traverse (traverse f) b <*> traverse (traverse f) a <*> traverse (traverse f) p

instance Each (TagTreePos str) (TagTreePos str') str str' where
  each =
    traverse

class HasTagTreePos a str | a -> str where
  tagTreePos ::
    Lens' a (TagTreePos str)
  tagTreePosContent ::
    Lens' a (TagTree str)
  tagTreePosContent =
    tagTreePos . tagTreePosContent
  tagTreePosBefore ::
    Lens' a [TagTree str]
  tagTreePosBefore =
    tagTreePos . tagTreePosBefore
  tagTreePosAfter ::
    Lens' a [TagTree str]
  tagTreePosAfter =
    tagTreePos . tagTreePosAfter
  tagTreePosParents ::
    Lens' a [TagTreePosParent str]
  tagTreePosParents =
    tagTreePos . tagTreePosParents

instance HasTagTreePos (TagTreePos str) str where
  tagTreePos =
    id
  tagTreePosContent f (TagTreePos c b a p) =
    fmap (\c' -> TagTreePos c' b a p) (f c)
  tagTreePosBefore f (TagTreePos c b a p) =
    fmap (\b' -> TagTreePos c b' a p) (f b)
  tagTreePosAfter f (TagTreePos c b a p) =
    fmap (\a' -> TagTreePos c b a' p) (f a)
  tagTreePosParents f (TagTreePos c b a p) =
    fmap (\p' -> TagTreePos c b a p') (f p)

class AsTagTreePos a str | a -> str where
  _TagTreePos ::
    Prism' a (TagTreePos str)

instance AsTagTreePos (TagTreePos str) str where
  _TagTreePos =
    id

deriveEq1 ''TagTreePos
deriveOrd1 ''TagTreePos
deriveShow1 ''TagTreePos

tagsoupTagTreePos ::
  Iso (TagTreePos str) (TagTreePos str') (TagSoup.TagTreePos str) (TagSoup.TagTreePos str')
tagsoupTagTreePos =
  iso
    (\(TagTreePos c b a p) ->
      TagSoup.TagTreePos
        (view tagsoupTagTree c)
        (fmap (view tagsoupTagTree) b)
        (fmap (view tagsoupTagTree) a)
        (fmap (\(TagTreePosParent l' x' a' r') -> (fmap (view tagsoupTagTree) l', x', fmap (view tagsoupAttribute) a', fmap (view tagsoupTagTree) r')) p)
    )
    (\(TagSoup.TagTreePos c b a p) ->
      let tagsoupTagTree' =
            from tagsoupTagTree
          tagsoupAttribute' =
            from tagsoupAttribute
      in  TagTreePos
            (view tagsoupTagTree' c)
            (fmap (view tagsoupTagTree') b)
            (fmap (view tagsoupTagTree') a)
            (fmap (\(l', x', a', r') -> TagTreePosParent (fmap (view tagsoupTagTree') l') x' (fmap (view tagsoupAttribute') a') (fmap (view tagsoupTagTree') r')) p)
    )

instance AsTagTreePos (TagSoup.TagTreePos str) str where
  _TagTreePos =
    from tagsoupTagTreePos . _TagTreePos

instance HasTagTreePos (TagSoup.TagTreePos str) str where
  tagTreePos =
    from tagsoupTagTreePos . tagTreePos

fromTagTree ::
  TagTree str
  -> TagTreePos str
fromTagTree =
  view (from tagsoupTagTreePos) . TagSoup.fromTagTree . view tagsoupTagTree

toTagTree ::
  TagTreePos str
  -> TagTree str
toTagTree t =
  let TagSoup.TagTreePos x _ _ _ = TagSoup.root (view tagsoupTagTreePos t)
  in  view (from tagsoupTagTree) x