{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Instances where

import Control.Monad(replicateM)

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative((<$>), (<*>), pure)
#endif

import Test.Tasty.QuickCheck
import Text.Namelist.Types
import Data.Complex(Complex((:+)))
import Data.CaseInsensitive(mk)
import Data.Char(toUpper)
import Numeric(showFFloat)

arbitrarySafeDouble :: Gen Double
arbitrarySafeDouble = suchThat (arbitrary :: Gen Double) $ \d ->
    let (n, _:f) = break (== '.') $ showFFloat Nothing d ""
    in d == read (n ++ f) / 10 ** fromIntegral (length f)

arbitraryIndex :: Gen Index
arbitraryIndex = do
    i <- choose (1, maxBound)
    return $ Index i

arbitraryRange :: Gen Index
arbitraryRange = do
    a <- oneof [Just <$> choose (1, maxBound - 1),          pure Nothing]
    b <- oneof [Just <$> choose (maybe 1 succ a, maxBound), pure Nothing]
    s <- oneof [Just . unSmall <$> arbitrary,               pure Nothing]
    return $ Range a b s
  where
    unSmall (Small a) = a

instance Arbitrary Index where
    arbitrary = oneof
        [ arbitraryIndex
        , arbitraryRange
        ]

arbitraryName :: Gen String
arbitraryName = sized $ \i -> do
    a  <- elements alpha
    as <- replicateM (min i 30) (elements an_)
    return $ a : as
  where
    an_   = '_': ['0' .. '9'] ++ alpha
    alpha = map toUpper lower ++ lower
    lower = ['a'..'z']

arbitraryKeyName :: Gen Key
arbitraryKeyName = oneof
    [ Key . mk <$> arbitraryName
    , Indexed  <$> (mk <$> arbitraryName) <*> listOf1 arbitrary
    ]

arbitrarySub :: Gen Key
arbitrarySub = sized $ \depth ->
    if depth < 1
    then arbitraryKeyName
    else Sub <$> (mk <$> arbitraryName) <*> arbitrary

instance Arbitrary Key where
    arbitrary = oneof
        [ arbitraryKeyName
        , arbitrarySub
        ]

newtype Scalar = Scalar { unScalar :: Value }

instance Arbitrary Scalar where
    arbitrary = Scalar <$> oneof
        [ Integer <$> arbitrary
        , Real    <$> arbitrarySafeDouble
        , Complex <$> ((:+) <$> arbitrarySafeDouble <*> arbitrarySafeDouble)
        , Logical <$> arbitrary
        , String  <$> arbitrary
        ]

instance Arbitrary Value where
    arbitrary = oneof
        [ unScalar <$> arbitrary
        , pure Null
        , sized $ \i -> do
            as <- map unScalar <$> replicateM (max 1 i) arbitrary
            a  <- unScalar <$> arbitrary
            return $ Array (reverse $ a:as)
        , sized $ \i -> (:*) <$> pure i <*> (unScalar <$> arbitrary)
        ]

instance Arbitrary Pair where
    arbitrary = (:=) <$> arbitrary <*> arbitrary

scaleDouble :: (Double -> Double) -> Gen a -> Gen a
scaleDouble f = scale (round . f . fromIntegral)

instance Arbitrary Group where
    arbitrary = Group <$> (mk <$> arbitraryName) <*> arbitrary

newtype Namelist = Namelist { getNamelist :: [Group] }

instance Show Namelist where
    show (Namelist gs) = show gs

instance Arbitrary Namelist where
    arbitrary = Namelist <$> scaleDouble sqrt arbitrary
