{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DefaultSignatures #-}

module Data.Aviation.Aip.SHA1(
  SHA1(..)
, AsSHA1(..)
, FoldSHA1(..)
, GetSHA1(..)
, SetSHA1(..)
, ManySHA1(..)
, HasSHA1(..)
, IsSHA1(..)
, hash
, hashHex
, showsHash
, strHash
) where

import Control.Category((.), id)
import Control.Applicative(pure)
import Control.Lens
import Data.Aeson(FromJSON(parseJSON), ToJSON(toJSON))
import Data.Digest.SHA1(Word160(Word160))
import qualified Data.Digest.SHA1 as SHA1(hash, toInteger)
import Data.Eq(Eq)
import Data.Functor((<$>))
import Data.Maybe(Maybe(Nothing))
import Data.String(String)
import Data.Word(Word8, Word32)
import Numeric(showHex, readHex)
import Prelude(Show, ShowS, ReadS)

newtype SHA1 =
  SHA1
    Word160
  deriving (Eq, Show)

instance FromJSON SHA1 where
  parseJSON v =
    (\(b0, b1, b2, b3, b4) -> SHA1 (Word160 b0 b1 b2 b3 b4)) <$> parseJSON v

instance ToJSON SHA1 where
  toJSON (SHA1 (Word160 b0 b1 b2 b3 b4)) =
    toJSON (b0, b1, b2, b3, b4)

class ManySHA1 a => AsSHA1 a where
  _SHA1 ::
    Prism' a SHA1
  default _SHA1 ::
    IsSHA1 a =>
    Prism' a SHA1
  _SHA1 =
    _IsSHA1

instance AsSHA1 SHA1 where
  _SHA1 =
    id

class FoldSHA1 a where
  _FoldSHA1 ::
    Fold a SHA1

instance FoldSHA1 SHA1 where
  _FoldSHA1 =
    id

class FoldSHA1 a => GetSHA1 a where
  _GetSHA1 ::
    Getter a SHA1
  default _GetSHA1 ::
    HasSHA1 a =>
    Getter a SHA1
  _GetSHA1 =
    sha1

instance GetSHA1 SHA1 where
  _GetSHA1 =
    id

class SetSHA1 a where
  _SetSHA1 ::
    Setter' a SHA1
  default _SetSHA1 ::
    ManySHA1 a =>
    Setter' a SHA1
  _SetSHA1 =
    _ManySHA1

instance SetSHA1 SHA1 where
  _SetSHA1 =
    id

class (FoldSHA1 a, SetSHA1 a) => ManySHA1 a where
  _ManySHA1 ::
    Traversal' a SHA1

instance ManySHA1 SHA1 where
  _ManySHA1 =
    id

class (GetSHA1 a, ManySHA1 a) => HasSHA1 a where
  sha1 ::
    Lens' a SHA1
  default sha1 ::
    IsSHA1 a =>
    Lens' a SHA1
  sha1 =
    _IsSHA1

instance HasSHA1 SHA1 where
  sha1 =
    id

class (HasSHA1 a, AsSHA1 a) => IsSHA1 a where
  _IsSHA1 ::
    Iso' a SHA1

instance IsSHA1 SHA1 where
  _IsSHA1 =
    id

instance SetSHA1 () where
instance FoldSHA1 () where
  _FoldSHA1 =
    _ManySHA1
instance ManySHA1 () where
  _ManySHA1 _ x =
    pure x

hash ::
  [Word8]
  -> SHA1
hash =
  SHA1 . SHA1.hash

hashHex ::
  SHA1
  -> ShowS
hashHex (SHA1 x) =
  showHex (SHA1.toInteger x)

showsHash ::
  HasSHA1 s =>
  s
  -> ShowS
showsHash x =
  hashHex (x ^. sha1)

strHash ::
  Prism'
    String
    SHA1
strHash =
  prism'
    (\x -> showsHash x "")
    (\h ->  case h of
              (a0:a1:a2:a3:a4:a5:a6:a7:b0:b1:b2:b3:b4:b5:b6:b7:c0:c1:c2:c3:c4:c5:c6:c7:d0:d1:d2:d3:d4:d5:d6:d7:e0:e1:e2:e3:e4:e5:e6:e7:[]) ->
                let word32 w =
                      (readHex :: ReadS Word32) w ^? _head . _1
                in  do  w1 <- word32 [a0,a1,a2,a3,a4,a5,a6,a7]
                        w2 <- word32 [b0,b1,b2,b3,b4,b5,b6,b7]
                        w3 <- word32 [c0,c1,c2,c3,c4,c5,c6,c7]
                        w4 <- word32 [d0,d1,d2,d3,d4,d5,d6,d7]
                        w5 <- word32 [e0,e1,e2,e3,e4,e5,e6,e7]
                        pure (SHA1 (Word160 w1 w2 w3 w4 w5))
              _ ->
                Nothing)