{-#LANGUAGE GADTs         #-}
{-#LANGUAGE TypeOperators #-}
{-#LANGUAGE KindSignatures #-}
{-#LANGUAGE FlexibleInstances #-}
{-#LANGUAGE FlexibleContexts #-}
{-#LANGUAGE ScopedTypeVariables #-} 
{-#LANGUAGE InstanceSigs #-}
{-#LANGUAGE PartialTypeSignatures #-}
{-#LANGUAGE UndecidableInstances  #-}

{-#LANGUAGE DataKinds #-}
module GenericType (
    BasicType(..),
    GenericType(..),
    NestedType(..),
    NestedToType(..),
    ok_vector
    ) where
-- Test modules
import Test.QuickCheck hiding ((.&.))
import Test.QuickCheck.Modifiers (NonEmptyList(..))
-- Tested modules
import Foreign.Storable.Generic.Internal

-- Test data
import Foreign.Storable.Generic.Tools
import Foreign.Storable.Generic.Tools.TypeFuns
import Foreign.Storable.Generic.Instances
import Foreign.Ptr (Ptr, nullPtr, plusPtr)
import Foreign.C.Types
import Foreign.Storable
import GHC.Generics  
import Data.Int
import Data.Word
import Data.Bits
import Data.Proxy
import Debug.Trace
import GHC.TypeLits

import Unsafe.Coerce

-- | TestType - the basic building blocks from which
-- GStorable instances are built.
class (Arbitrary a,Eq a,GStorable a, Show a) => TestType a

-- Generating random pointers.
instance Arbitrary (Ptr a) where
    arbitrary = do
        plus <- choose (0, 10000)
        return $ plusPtr nullPtr plus

instance TestType Int
instance TestType Int8
instance TestType Int16
instance TestType Int32
instance TestType Int64

instance TestType Word
instance TestType Word8
instance TestType Word16
instance TestType Word32
instance TestType Word64

instance TestType Double
instance TestType Float
instance TestType (Ptr a)
instance TestType Char

instance TestType CFloat

instance TestType CDouble

-- | The wrappable type class. Wraps the type in generics
-- and then into GenericType data type.
class (Show a) => Wrappable a where
    wrapType :: a -> GenericType


-------------------
-- | Contains the basic building blocks that generate GStorable type classes.
data BasicType where
   BasicType :: (TestType a) => a -> BasicType

instance Show BasicType where
    show (BasicType val) = show val

instance Arbitrary BasicType where
    arbitrary = do
        valInt     <- arbitrary  :: Gen Int 
        valInt8    <- arbitrary  :: Gen Int8
        valInt16   <- arbitrary  :: Gen Int16 
        valInt32   <- arbitrary  :: Gen Int32 
        valInt64   <- arbitrary  :: Gen Int64
        valWord     <- arbitrary :: Gen Word 
        valWord8    <- arbitrary :: Gen Word8
        valWord16   <- arbitrary :: Gen Word16 
        valWord32   <- arbitrary :: Gen Word32 
        valWord64   <- arbitrary :: Gen Word64
        valDouble  <- arbitrary  :: Gen Double
        valFloat   <- arbitrary  :: Gen Float
        valPtr     <- arbitrary  :: Gen (Ptr a)
        valChar    <- arbitrary  :: Gen Char

        valCDouble  <- arbitrary :: Gen CDouble
        valCFloat   <- arbitrary :: Gen CFloat

        elements [BasicType valInt,    BasicType valInt8, BasicType valInt16
                 ,BasicType valInt32 , BasicType valInt64, BasicType valCDouble
                 ,BasicType valCFloat, BasicType valPtr, BasicType valChar
                 ,BasicType valWord,   BasicType valWord8, BasicType valWord16
                 ,BasicType valWord32, BasicType valWord64]

-- | Wraps the basic type with 'M1' and 'K1' type constructors. 
-- The result is usable by the testing algorithms.
instance Wrappable BasicType where
    wrapType (BasicType val) = GenericType $ M1 $ K1 val

-- Some tricks for generics:
instance Arbitrary c     => Arbitrary (K1 i c p) where
    arbitrary = K1 <$> arbitrary
instance Arbitrary (f p) => Arbitrary (M1 i c f p) where
    arbitrary = M1 <$> arbitrary
instance (Arbitrary (f p), Arbitrary (g p)) => Arbitrary ((:*:) f g p) where
    arbitrary = (:*:) <$> (arbitrary :: Gen (f p)) <*> (arbitrary :: Gen (g p))


-- | Used withing GenericType to enforce that the types can be joined with :*:.
data MyPhantom

-- | Constains generic representations of arbitrary data-types.
-- The Show constraint is used so we can print out the badly working cases.
-- The Eq and Arbitrary one are for generating different values for the same types.
data GenericType where
   GenericType  :: (p ~ MyPhantom, Eq (f p), Arbitrary (f p),KnownNat (NoFields f),  GStorable' f, Show (f p)) => f p -> GenericType

instance Arbitrary GenericType where
    arbitrary = do
        n <- choose (0,100) :: Gen Int
        genType n

-- | Generates a random generic representation.
-- Does not resemble the generic representation from real types.
genType :: Int  -- ^ Number of randomness. 
        -> Gen GenericType
genType 0 = wrapType <$> (arbitrary :: Gen BasicType)
genType n = do
    -- Choose what kind of element this layer is.
    step <- arbitrary :: Gen GenTree
    case step of
        -- A product - a tree
        Producted -> do
            -- Generate two subtrees with the same function
            div <- choose  (0,n) 
            gt1 <- genType  div
            gt2 <- genType (n-div)
            return $ (\(GenericType t1) (GenericType t2) -> GenericType $ t1 :*: t2) gt1 gt2 
        -- Wrap in meta-data anything that's generated later
        M1ed      -> (\(GenericType t) -> GenericType $ M1 t) <$> genType (n-1) 
        -- Wrap in K1 anything that's generated later.
        K1ed      -> (\(GenericType t) -> GenericType $ K1 t) <$> genType (n-1) 

-- | Enums for generating the generic representations.
data GenTree = Producted | M1ed | K1ed

instance Arbitrary GenTree where
    arbitrary = elements [Producted, M1ed , K1ed]

-- | Show the generic metadata. Could use some pretty printing later. 
instance Show GenericType where
    show (GenericType    val) = show val

-- | Wrap the generic representation as it were derived from a proper type.
instance Wrappable GenericType where
    wrapType    (GenericType  val) = GenericType $ M1 $ K1 $ val

-- | Wrapper for creating nested types.
data NestedType (n :: Nat) = NestedType GenericType

instance (KnownNat n) => Show (NestedType n) where
    show (NestedType (GenericType val)) = type_info ++ show val
        where type_info = "NestedType " ++ (show $ natVal (Proxy :: Proxy n)) ++ " "

instance (KnownNat n) => Arbitrary (NestedType n) where
    arbitrary = NestedType <$> nestedType (fromIntegral $ natVal (Proxy :: Proxy n))

-- | Wrapper for creating nested types up to a certain level.
data NestedToType (n :: Nat) = NestedToType GenericType

instance (KnownNat n) => Show (NestedToType n) where
    show (NestedToType (GenericType val)) = type_info ++ show val
        where type_info = "NestedToType " ++ (show $ natVal (Proxy :: Proxy n)) ++ " "

instance (KnownNat n) => Arbitrary (NestedToType n) where
    arbitrary = NestedToType <$> nestedToType (fromIntegral $ natVal (Proxy :: Proxy n))

-- | Generate a nested type.
nestedType :: Int             -- ^ Depth of the generated type. 
           -> Gen GenericType -- ^ Resulting generator.
nestedType n  = nestedType' n gen
    where gen = wrapType <$> (arbitrary :: Gen BasicType) 

nestedType' :: Int             -- ^ Depth of the generated type.
            -> Gen GenericType -- ^ Accumulator
            -> Gen GenericType -- ^ Resulting generator
nestedType' n gen 
    | n <  0 = error "GenericType.nestedType': n is less than 0"
    | n == 0 = gen
    | n > 0  = do 
        fields <- choose (1, 4*n)
        nestedType' (n-1) (wrapType <$> toGenericType <$> vectorOf fields gen) 

-- | For generating nested types with components from levels below.
nestedToType :: Int -> Gen (GenericType)
nestedToType n =do
    sublist <- suchThat (sublistOf [0..n]) (\x -> length x > 0)
    wrapType <$> toGenericType <$> mapM nestedType sublist

-- | Uses the :*: operator to construct a representation of a product type.
typeProduct :: GenericType -> GenericType -> GenericType
typeProduct (GenericType val1) (GenericType val2) = GenericType $ val1 :*: val2

-- | Creates a tree for type product.
toGenericType :: [GenericType] -> GenericType
toGenericType []  = error "toGenericType requires at least one type"
toGenericType [v] = v
toGenericType types = foldl1 typeProduct types

-- | Simulates the K1 step for generic representations. 
instance {-#OVERLAPS#-} (KnownNat (NoFields f), GStorable' f) => GStorable' (K1 i (f p)) where
    glistSizeOf'    _ = [internalSizeOf (undefined :: f p)]
    glistAlignment' _ = [internalAlignment (undefined :: f p)]
    gpeekByteOff' offs n ptr off   = K1 <$> internalPeekByteOff ptr (off + f_off)
        where f_off = offs !! n
    gpokeByteOff' offs n ptr off (K1 v) = internalPokeByteOff ptr (off + f_off) v
        where f_off = offs !! n

-- | Helps with avoiding NaN problem. 
ok_vector :: Int -> Gen [Word8]
ok_vector n = vectorOf n (suchThat arbitrary $ (\x-> (x .&. 127) /= 127) )

