{-|
Base API for Point serialisation for G1, G2 and GT
-}

module Pairing.Serialize.Types (
  MkCompressedForm(..),
  MkUncompressedForm(..),
  FromSerialisedForm(..),
  FromUncompressedForm(..),
  minReprLength,
  buildPoint,
  parseBS
) where

import Protolude hiding (putByteString)
import Pairing.Point
import Pairing.Fq
import Data.ByteString.Builder
import Data.ByteString as B hiding (length)
import qualified Data.ByteString as B
import Data.Binary.Get
import Data.Binary.Put (Put, putWord8, putWord16le, runPut, putByteString)
import Control.Error
import Pairing.ByteRepr
import Pairing.CyclicGroup

class MkCompressedForm a where
  -- | The serialisation may fail if y cannot be obtained from x
  serializeCompressed :: (ByteRepr b, FromX b, Ord b) => a -> Point b -> Maybe LByteString

class MkUncompressedForm a where
  serializePointUncompressed :: (ByteRepr b, FromX b, Eq b) => a -> Point b -> Maybe LByteString
  serializeUncompressed :: (ByteRepr c) => a -> c -> Maybe LByteString

class FromSerialisedForm a where
  unserializePoint :: (ByteRepr b, FromX b, Ord b, Show b, Validate (Point b)) => a -> Point b -> LByteString -> Either Text (Point b)

class FromUncompressedForm a where
  unserialize :: (ByteRepr b, Validate b, Eq b, Show b) => a -> b -> LByteString -> Either Text b

minReprLength :: Int
minReprLength = B.length $ toBytes p
  where
    p = natVal (witness :: Fq)

buildPoint :: ByteRepr a => a -> ByteOrderLength -> ByteString -> ByteOrderLength -> ByteString -> Maybe (Point a)
buildPoint one xlen xbs ylen ybs = do
  x <- fromRepr xlen one xbs
  y <- fromRepr ylen one ybs
  pure (Point x y)

parseBS :: (Validate a, Show a) => Get (Maybe a) -> LByteString -> Either Text a
parseBS f bs = do
  (_, _, mpt) <- first (\(_,_,err) -> toS err) (runGetOrFail f bs)
  case mpt of
    Just pt -> if isValidElement pt then (Right pt) else Left ("Element was not valid after deserialisation: " <> show pt)
    Nothing -> Left "Point could not be parsed"