{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

module Data.Aviation.Aip.PerHref(
  PerHref(..)
, ioPerHref
, nothingPerHref
, hrefPerHref
, basedirPerHref
, downloaddirPerHref
, logPerHref
, logeachPerHref
, logShowPerHref
, PerHrefAipCon
, PerHrefIO
) where

import Control.Category((.))
import Control.Applicative(Applicative(pure, (<*>)), Alternative((<|>), empty))
import Control.Lens hiding ((<.>))
import Control.Monad(Monad(return, (>>=)))
import Control.Monad.IO.Class(MonadIO(liftIO))
import Control.Monad.Trans.Class(MonadTrans(lift))
import Data.Aviation.Aip.AipCon(AipCon)
import Data.Aviation.Aip.Href(Href)
import Data.Functor(Functor(fmap))
import Data.Functor.Alt(Alt((<!>)))
import Data.Functor.Apply(Apply((<.>)))
import Data.Functor.Bind(Bind((>>-)))
import Data.String(String)
import Prelude(Show(show))
import System.FilePath(FilePath)
import System.IO(IO)

newtype PerHref f a =
  PerHref
    (Href -> FilePath -> FilePath -> (String -> AipCon ()) -> f a)

instance Functor f => Functor (PerHref f) where
  fmap f (PerHref x) =
    PerHref (\h d d' l -> fmap f (x h d d' l))

instance Apply f => Apply (PerHref f) where
  PerHref f <.> PerHref a =
    PerHref (\h d d' l -> f h d d' l <.> a h d d' l)

instance Applicative f => Applicative (PerHref f) where
  pure =
    PerHref . pure . pure . pure . pure . pure

  PerHref f <*> PerHref a =
    PerHref (\h d d' l -> f h d d' l <*> a h d d' l)

instance Bind f => Bind (PerHref f) where
  PerHref x >>- f =
    PerHref (\h d d' l -> x h d d' l >>- \a -> let g = f a ^. _Wrapped in g h d d' l)

instance Monad f => Monad (PerHref f) where
  return =
    pure
  PerHref x >>= f =
    PerHref (\h d d' l -> x h d d' l >>= \a -> let g = f a ^. _Wrapped in g h d d' l)

instance Alt f => Alt (PerHref f) where
  PerHref x <!> PerHref y =
    PerHref (\h d d' l -> x h d d' l <!> y h d d' l)

instance Alternative f => Alternative (PerHref f) where
  PerHref x <|> PerHref y =
    PerHref (\h d d' l -> x h d d' l <|> y h d d' l)
  empty =
    (PerHref . pure . pure . pure . pure) empty

instance MonadTrans PerHref where
  lift =
    PerHref . pure . pure . pure . pure

instance MonadIO f => MonadIO (PerHref f) where
  liftIO =
    PerHref . pure . pure . pure . pure . liftIO

instance PerHref f a ~ x =>
  Rewrapped (PerHref g k) x

instance Wrapped (PerHref f k) where
  type Unwrapped (PerHref f k) =
      Href
      -> FilePath
      -> FilePath
      -> (String -> AipCon ())
      -> f k
  _Wrapped' =
    iso
      (\(PerHref x) -> x)
      PerHref

ioPerHref ::
  MonadIO f =>
  (Href -> FilePath -> FilePath -> (String -> AipCon ()) -> IO a)
  -> PerHref f a
ioPerHref k =
  PerHref (\h d d' l -> liftIO (k h d d' l))

nothingPerHref ::
  Applicative f =>
  PerHref f ()
nothingPerHref =
  pure ()

hrefPerHref ::
  Applicative f =>
  PerHref f Href
hrefPerHref =
  PerHref (\h _ _ _ -> pure h)

basedirPerHref ::
  Applicative f =>
  PerHref f FilePath
basedirPerHref =
  PerHref (\_ d _ _ -> pure d)

downloaddirPerHref ::
  Applicative f =>
  PerHref f FilePath
downloaddirPerHref =
  PerHref (\_ _ d' _ -> pure d')

logPerHref ::
  Applicative f =>
  PerHref f (String -> AipCon ())
logPerHref =
  PerHref (\_ _ _ l -> pure l)

type PerHrefAipCon a =
  PerHref AipCon a

type PerHrefIO a =
  PerHref IO a

logeachPerHref ::
  PerHrefAipCon ()
logeachPerHref =
  PerHref (\h d d' l ->
    let l' ::
          Show a =>
          a
          -> AipCon ()
        l' =
          l . show
    in  do  l' h
            l' d
            l' d')

logShowPerHref ::
  Show a =>
  a
  -> PerHrefAipCon ()
logShowPerHref z =
  do  l <- logPerHref
      lift (l (show z))