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

module Text.HTML.TagSoup.Navigate.Types.TagTree(
  TagTree(..)
, HasTagTree(..)
, AsTagTree(..)
, tagsoupTagTree
, _TagBranch_
, _TagBranchAttributeList_
, _TagBranchAttributes_
, _TagBranchChildrenList_
, _TagBranchChildren_
, _TagBranchAttributeNames_
, _TagBranchAttributeValues_
, tagTree'
, flattenTree
) where

import Control.Applicative((<*>))
import Control.Category((.), id)
import Control.Lens(Plated(plate), Each(each), Lens', Prism', Iso, Traversal', prism', iso, from, (^.), ( # ), _1, _2, _3)
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.Maybe(Maybe(Just, Nothing))
import Data.Monoid(mappend)
import Data.Traversable(Traversable(traverse))
import Prelude(Show)
import Text.HTML.TagSoup.Navigate.Types.Attribute(Attribute, tagsoupAttribute, attributeName, attributeValue)
import Text.HTML.TagSoup.Navigate.Types.Tag(Tag, AsTag(_Tag), tagsoupTag)
import qualified Text.HTML.TagSoup.Tree as TagSoup(TagTree(TagBranch, TagLeaf), tagTree, flattenTree)
import Text.Show.Deriving(deriveShow1)

data TagTree str =
  TagBranch
    str
    [Attribute str]
    [TagTree str]
  | TagLeaf (Tag str)
  deriving (Eq, Ord, Show)

instance Functor TagTree where
  fmap f (TagBranch s as ts) =
    TagBranch (f s) (fmap (fmap f) as) (fmap (fmap f) ts)
  fmap f (TagLeaf t) =
    TagLeaf (fmap f t)

instance Foldable TagTree where
  foldMap f (TagBranch s as ts) =
    f s `mappend` foldMap (foldMap f) as `mappend` foldMap (foldMap f) ts
  foldMap f (TagLeaf t) =
    foldMap f t

instance Traversable TagTree where
  traverse f (TagBranch s as ts) =
    TagBranch <$> f s <*> traverse (traverse f) as <*> traverse (traverse f) ts
  traverse f (TagLeaf t) =
    TagLeaf <$> traverse f t

instance Plated (TagTree str) where
  plate =
    _TagBranchChildren_

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

class HasTagTree a str | a -> str where
  tagTree ::
    Lens' a (TagTree str)

instance HasTagTree (TagTree str) str where
  tagTree =
    id

instance HasTagTree (TagSoup.TagTree str) str where
  tagTree =
    from tagsoupTagTree . tagTree

class AsTagTree a str | a -> str where
  _TagTree ::
    Prism' a (TagTree str)
  _TagBranch ::
    Prism' a (str, [Attribute str], [TagTree str])
  _TagBranch =
    _TagTree . _TagBranch
  _TagLeaf ::
    Prism' a (Tag str)
  _TagLeaf =
    _TagTree . _TagLeaf

instance AsTagTree (TagTree str) str where
  _TagTree =
    id
  _TagBranch =
    prism'
      (\(s, as, t) -> TagBranch s as t)
      (\tr ->  case tr of
                TagBranch s as t ->
                  Just (s, as, t)
                TagLeaf _ ->
                  Nothing)
  _TagLeaf =
    prism'
      TagLeaf
      (\tr ->  case tr of
                TagBranch _ _ _ ->
                  Nothing
                TagLeaf x ->
                  Just x)

instance AsTagTree (TagSoup.TagTree str) str where
  _TagTree =
    from tagsoupTagTree . _TagTree

instance AsTag (TagTree str) str where
  _Tag =
    _TagLeaf . _Tag

tagsoupTagTree ::
  Iso (TagTree str) (TagTree str') (TagSoup.TagTree str) (TagSoup.TagTree str')
tagsoupTagTree =
  iso
    (\tr ->
      case tr of
        TagBranch s as t ->
          TagSoup.TagBranch s (fmap (^. tagsoupAttribute) as) (fmap (^. tagsoupTagTree) t)
        TagLeaf x ->
          TagSoup.TagLeaf (x ^. tagsoupTag))
    (\tr ->
      case tr of
        TagSoup.TagBranch s as t ->
          TagBranch s (fmap (tagsoupAttribute #) as) (fmap (tagsoupTagTree #) t)
        TagSoup.TagLeaf x ->
          TagLeaf (tagsoupTag # x))

_TagBranch_ ::
  AsTagTree a str =>
  Traversal' a str
_TagBranch_ =
  _TagBranch . _1

_TagBranchAttributeList_ ::
  AsTagTree a str =>
  Traversal' a [Attribute str]
_TagBranchAttributeList_ =
  _TagBranch . _2

_TagBranchAttributes_ ::
  AsTagTree a str =>
  Traversal' a (Attribute str)
_TagBranchAttributes_ =
  _TagBranchAttributeList_ . traverse

_TagBranchChildrenList_ ::
  AsTagTree a str =>
  Traversal' a [TagTree str]
_TagBranchChildrenList_ =
  _TagBranch . _3

_TagBranchChildren_ ::
  AsTagTree a str =>
  Traversal' a (TagTree str)
_TagBranchChildren_ =
  _TagBranchChildrenList_ . traverse

_TagBranchAttributeNames_ ::
  AsTagTree a str =>
  Traversal' a str
_TagBranchAttributeNames_ =
  _TagBranchAttributes_ . attributeName

_TagBranchAttributeValues_ ::
  AsTagTree a str =>
  Traversal' a str
_TagBranchAttributeValues_ =
  _TagBranchAttributes_ . attributeValue

deriveEq1 ''TagTree
deriveOrd1 ''TagTree
deriveShow1 ''TagTree

tagTree' ::
  Eq str =>
  [Tag str]
  -> [TagTree str]
tagTree' =
  fmap (tagsoupTagTree #) . TagSoup.tagTree . fmap (^. tagsoupTag)

flattenTree ::
  [TagTree str]
  -> [Tag str]
flattenTree =
  fmap (tagsoupTag #) . TagSoup.flattenTree . fmap (^. tagsoupTagTree)