{-# OPTIONS_GHC -fno-warn-orphans #-}
module Voting.Protocol.Utils where

import Control.Applicative (Applicative(..))
import Control.Arrow (first)
import Data.Bool
import Data.Either (Either(..), either)
import Data.Eq (Eq(..))
import Data.Foldable (sequenceA_)
import Data.Function (($), (.))
import Data.Functor ((<$))
import Data.Maybe (Maybe(..), maybe, listToMaybe)
import Data.String (String)
import Data.Traversable (Traversable(..))
import Data.Tuple (uncurry)
import Numeric.Natural (Natural)
import Prelude (Integer, fromIntegral)
import qualified Data.Aeson.Internal as JSON
import qualified Data.List as List
import qualified System.Random as Random
import qualified Text.ParserCombinators.ReadP as Read
import qualified Text.Read as Read

-- | Like ('.') but with two arguments.
o2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
o2 f g = \x y -> f (g x y)
infixr 9 `o2`
{-# INLINE o2 #-}

-- | NOTE: check the lengths before applying @f@.
isoZipWith :: (a->b->c) -> [a]->[b]->Maybe [c]
isoZipWith f as bs
 | List.length as /= List.length bs = Nothing
 | otherwise = Just (List.zipWith f as bs)

-- | NOTE: check the lengths before applying @f@.
isoZipWith3 :: (a->b->c->d) -> [a]->[b]->[c]->Maybe [d]
isoZipWith3 f as bs cs
 | al /= List.length bs = Nothing
 | al /= List.length cs = Nothing
 | otherwise = Just (List.zipWith3 f as bs cs)
 where al = List.length as

isoZipWithM ::
 Applicative f =>
 f () -> (a->b->f c) -> [a]->[b]->f [c]
isoZipWithM err f as bs =
        maybe ([] <$ err) sequenceA $
                isoZipWith f as bs

isoZipWithM_ ::
 Applicative f =>
 f () -> (a->b->f c) -> [a]->[b]->f ()
isoZipWithM_ err f as bs =
        maybe err sequenceA_ $
                isoZipWith f as bs

isoZipWith3M ::
 Applicative f =>
 f () -> (a->b->c->f d) -> [a]->[b]->[c]->f [d]
isoZipWith3M err f as bs cs =
        maybe ([] <$ err) sequenceA $
                isoZipWith3 f as bs cs

isoZipWith3M_ ::
 Applicative f =>
 f () -> (a->b->c->f d) -> [a]->[b]->[c]->f ()
isoZipWith3M_ err f as bs cs =
        maybe err sequenceA_ $
                isoZipWith3 f as bs cs

-- * JSON utils

-- | Copied from 'Data.Aeson''s 'eitherFormatError'
-- which is not exported.
jsonEitherFormatError :: Either (JSON.JSONPath, String) a -> Either String a
jsonEitherFormatError = either (Left . uncurry JSON.formatError) Right
{-# INLINE jsonEitherFormatError #-}

instance Random.Random Natural where
        randomR (mini,maxi) =
                first (fromIntegral::Integer -> Natural) .
                Random.randomR (fromIntegral mini, fromIntegral maxi)
        random = first (fromIntegral::Integer -> Natural) . Random.random

-- * Parsing utils

parseReadP :: Read.ReadP a -> String -> Maybe a
parseReadP p s =
        let p' = Read.readP_to_S p in
        listToMaybe $ do
                (x, "") <- p' s
                pure x