{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}

module Text.HTML.TagSoup.Navigate.Types.Attribute(
  Attribute(..)
, AsAttribute(..)
, HasAttribute(..)
, Row
, Column
, bothAttributes
, tagsoupAttribute
) where

import Control.Applicative(Applicative((<*>), pure), liftA2)
import Control.Category((.), id)
import Control.Lens(Each(each), Reversing(reversing), Rewrapped, Wrapped(Unwrapped), _Wrapped', _Wrapped, Field1(_1), Field2(_2), Prism', Lens', Traversal, Iso, iso, from)
import Control.Monad(Monad((>>=), return))
import Control.Monad.Zip(MonadZip(mzipWith))
import Data.Bool((&&))
import Data.Eq(Eq)
import Data.Foldable(Foldable(foldMap))
import Data.Functor(Functor(fmap), (<$>))
import Data.Functor.Apply(Apply((<.>)))
import Data.Functor.Bind(Bind((>>-)))
import Data.Functor.Classes(Eq1(liftEq), Ord1(liftCompare), Show1(liftShowsPrec), showsBinaryWith)
import Data.Monoid(Monoid(mappend, mempty))
import Data.Ord(Ord)
import Data.Semigroup.Foldable(Foldable1(foldMap1))
import Data.Semigroup.Traversable(Traversable1(traverse1))
import Data.Semigroup(Semigroup((<>)))
import Data.Traversable(Traversable(traverse))
import Prelude(Show)
import Text.HTML.TagSoup(Row, Column)
import qualified Text.HTML.TagSoup as TagSoup(Attribute)

data Attribute str =
  Attribute
    str
    str
  deriving (Eq, Ord, Show)

instance Functor Attribute where
  fmap f (Attribute s1 s2) =
    Attribute (f s1) (f s2)

instance Apply Attribute where
  Attribute s1 s2 <.> Attribute s3 s4 =
    Attribute (s1 s3) (s2 s4)

instance Applicative Attribute where
  pure s =
    Attribute s s
  (<*>) =
    (<.>)

instance Bind Attribute where
  Attribute a b >>- f =
    let Attribute a' _ = f a
        Attribute _ b' = f b
    in  Attribute a' b'

instance Monad Attribute where
  return =
    pure
  (>>=) =
    (>>-)

instance Foldable Attribute where
  foldMap f (Attribute s1 s2) =
    f s1 `mappend` f s2

instance Foldable1 Attribute where
  foldMap1 f (Attribute a b) =
    f a <> f b

instance Traversable Attribute where
  traverse f (Attribute s1 s2) =
    Attribute <$> f s1 <*> f s2

instance Traversable1 Attribute where
  traverse1 f (Attribute s1 s2) =
    Attribute <$> f s1 <.> f s2

instance MonadZip Attribute where
  mzipWith =
    liftA2

instance Semigroup str => Semigroup (Attribute str) where
  Attribute s1 s2 <> Attribute s3 s4 =
    Attribute (s1 <> s3) (s2 <> s4)

instance Monoid str => Monoid (Attribute str) where
  Attribute s1 s2 `mappend` Attribute s3 s4 =
    Attribute (s1 `mappend` s3) (s2 `mappend` s4)
  mempty =
    Attribute mempty mempty

instance Each (Attribute str) (Attribute str) str str where
  each f (Attribute s1 s2) =
    Attribute <$> f s1 <*> f s2

instance Reversing (Attribute str) where
  reversing (Attribute s1 s2) =
    Attribute s2 s1

instance Attribute s ~ str =>
  Rewrapped (Attribute x) str

instance Wrapped (Attribute str) where
  type Unwrapped (Attribute str) =
    (str, str)
  _Wrapped' =
    iso
      (\(Attribute s1 s2) -> (s1, s2))
      (\(s1, s2) -> Attribute s1 s2)

instance Eq1 Attribute where
  liftEq f (Attribute s1 s2) (Attribute s3 s4) =
    f s1 s3 && f s2 s4

instance Ord1 Attribute where
  liftCompare f (Attribute s1 s2) (Attribute s3 s4) =
    f s1 s3 `mappend` f s2 s4

instance Show1 Attribute where
  liftShowsPrec f _ k (Attribute s1 s2) =
    showsBinaryWith f f "Attribute" k s1 s2

instance Field1 (Attribute str) (Attribute str) str str where
  _1 =
    attributeName

instance Field2 (Attribute str) (Attribute str) str str where
  _2 =
    attributeValue

class AsAttribute s str | s -> str where
  _Attribute ::
    Prism' s (Attribute str)

instance AsAttribute (Attribute str) str where
  _Attribute = id

instance AsAttribute (str, str) str where
  _Attribute =
    from tagsoupAttribute . _Attribute

class HasAttribute s str | s -> str where
  attribute ::
    Lens' s (Attribute str)
  attributeName ::
    Lens' s str
  attributeName =
    attribute . attributeName
  attributeValue ::
    Lens' s str
  attributeValue =
    attribute . attributeValue

instance HasAttribute (Attribute str) str where
  attribute =
    id
  attributeName f (Attribute s1 s2) =
    fmap (\s1' -> Attribute s1' s2) (f s1)
  attributeValue f (Attribute s1 s2) =
    fmap (\s2' -> Attribute s1 s2') (f s2)

instance HasAttribute (str, str) str where
  attribute =
    from tagsoupAttribute . attribute

bothAttributes ::
  Traversal (Attribute str) (Attribute str') str str'
bothAttributes f (Attribute s1 s2) =
  Attribute <$> f s1 <*> f s2

tagsoupAttribute ::
  Iso (Attribute str) (Attribute str') (TagSoup.Attribute str) (TagSoup.Attribute str')
tagsoupAttribute =
  _Wrapped