{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fwarn-unused-imports #-}

-- | Verified backward compatibility of ASCII encoder changes in v0.3
module Test.Bc.ASCIISpec where

import qualified Data.TypedEncoding.Instances.Restriction.ASCII as ASCII
import           Data.TypedEncoding.Common.Class.Util.StringConstraints

import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.List as L

import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as BL8

import           Data.Char
import           Control.Arrow

import           Test.QuickCheck.Instances.ByteString ()
import           Test.QuickCheck.Instances.Text ()
import           Test.QuickCheck
import           Test.QuickCheck.Property
import           Test.Hspec


equivalProperty :: Eq str => (str -> Either NonAsciiChar str) -> (str -> Either NonAsciiChar str) -> str -> Property
equivalProperty alg1 alg2 str = property $ liftBool (alg1 str == alg2 str)

forwardCompatible :: (Char8Find str, Eq str) => (str -> Either NonAsciiChar str) -> str -> Property
forwardCompatible = equivalProperty newImpl


type NonAsciiChar = ASCII.NonAsciiChar


oldImplS = oldEncodeImpl L.partition L.head L.null
oldImplT = oldEncodeImpl T.partition T.head T.null
oldImplTL = oldEncodeImpl TL.partition TL.head TL.null
oldImplB = oldEncodeImpl (\p -> B8.filter p &&& B8.filter (not . p)) B8.head B8.null
oldImplBL = oldEncodeImpl (\p -> BL8.filter p &&& BL8.filter (not . p)) BL8.head BL8.null


newImpl :: Char8Find str => str -> Either NonAsciiChar str
newImpl = ASCII.encImpl

oldEncodeImpl :: 
   ((Char -> Bool) -> a -> (a, a))
   -> (a -> Char)
   -> (a -> Bool)
   -> a
   -> Either NonAsciiChar a
oldEncodeImpl partitionf headf nullf t = 
                 let (tascii, nonascii) = partitionf isAscii t 
                 in if nullf nonascii 
                    then Right tascii
                    else Left . ASCII.NonAsciiChar $ headf nonascii 

spec :: Spec 
spec = 
    describe "ASCII backward compapatibilty check" $ do
       it "String" $ property $ forwardCompatible oldImplS 
       it "Text" $ property $ forwardCompatible oldImplT
       it "Lazy.Text" $ property $ forwardCompatible oldImplTL
       it "ByteString" $ property $ forwardCompatible oldImplB
       it "Lazy.ByteString" $ property $ forwardCompatible oldImplBL

runSpec :: IO ()
runSpec = hspec spec       