{-# LANGUAGE ForeignFunctionInterface #-}
module Math.Noise.NoiseGen where
import Data.Int
import Data.Bits
import Data.Array.Unboxed
import Math.Noise.Interpolation
import Math.Noise.VectorTable
import Data.Fixed (mod')
import qualified Data.Vector.Unboxed as Vector
import Foreign
import Foreign.C.Types

foreign import ccall "noisegen.h gradientCoherentNoise"
  c_gradientNoise :: CDouble -> CDouble -> CDouble -> CInt -> CDouble

-- CONSTANTS
--xNoiseGen :: Int32
xNoiseGen = 1619

--yNoiseGen :: Int32
yNoiseGen = 31337

--zNoiseGen :: Int32
zNoiseGen = 6971

--seedNoiseGen :: Int32
seedNoiseGen = 1013

shiftNoiseGen :: Int
shiftNoiseGen = 8 

-- | Generates a gradient-coherent-noise value from the coordinates of a 3D value
{-
gradientCoherentNoise3D :: Double -> Double -> Double -> Int -> NoiseQuality -> Double
gradientCoherentNoise3D x y z seed quality = noiseCube x y z quality gradNoise
  where gradNoise i0 i1 i2 = gradientNoise3D x y z (toInt i0) (toInt i1) (toInt i2) seed
        toInt = fromIntegral . floor 
-}

gradientCoherentNoise3D :: Double -> Double -> Double -> Int -> Double
gradientCoherentNoise3D x y z seed = realToFrac $ c_gradientNoise (realToFrac x) (realToFrac y) (realToFrac z) (fromIntegral seed)

{-
  where x0 = if x > 0.0 then floor x
                        else round (x - 1)
        x1 = x0 + 1
        y0 = if y > 0.0 then floor y
                        else round (y -1)
        y1 = y0 + 1
        z0 = if z > 1.0 then floor z
                        else round (z - 1)
        z1 = z0 + 1

        (xs,ys,zs) = case quality of
                           Fast -> (x - fromIntegral x0 
                                   ,y - fromIntegral y0
                                   ,z - fromIntegral z0)
                           Standard -> (scurve3 (x - fromIntegral x0)
                                       ,scurve3 (y - fromIntegral y0)
                                       ,scurve3 (z - fromIntegral z0) )
                           Best -> (scurve5 (x - fromIntegral x0)
                                   ,scurve5 (y - fromIntegral y0)
                                   ,scurve5 (z - fromIntegral z0) )

        noiseFunc = gradientNoise3D x y z seed
        n0a = noiseFunc x0 y0 z0 
        n1a = noiseFunc x1 y0 z0 
        ix0a = linear n0a n1a xs
        n0b = noiseFunc x0 y1 z0 
        n1b = noiseFunc x1 y1 z0 
        ix1a = linear n0b n1b xs
        iy0 = linear ix0a ix1a ys
        n0c = noiseFunc x0 y0 z1 
        n1c = noiseFunc x1 y0 z1 
        ix0b = linear n0c n1c xs
        n0d = noiseFunc x0 y1 z1 
        n1d = noiseFunc x1 y1 z1 
        ix1b = linear n0d n1d xs
        iy1 = linear ix0b ix1b ys

        final = linear iy0 iy1 zs
-}

{-
        (a0,a1,b0,b1,c0,c1,d0,d1) = fastGradientNoise x y z seed x0 y0 z0
        ix0a = linear a0 a1 xs
        ix1a = linear b0 b1 xs
        iy0 = linear ix0a ix1a ys
        ix0b = linear c0 c1 xs
        ix1b = linear d0 d1 xs
        iy1 = linear ix0b ix1b ys
        final = linear iy0 iy1 zs
-}



{- | Generates a gradient-noise value from the coordinates of a 3D input value and
the integer coordinates of a nearby 3D value -}
gradientNoise3D :: Double -> Double -> Double -> Int -> Int -> Int -> Int -> Double
gradientNoise3D !fx !fy !fz !seed ix iy iz = 
  (xvGrad * xvPoint) +
  (yvGrad * yvPoint) +
  (zvGrad * zvPoint) * 2.12
  where icalc = xNoiseGen * ix
              + yNoiseGen * iy
              + zNoiseGen * iz
              + seedNoiseGen * seed
                                       
        index = shiftL ( (xor (fromIntegral icalc) (shiftR icalc shiftNoiseGen)) .&. 0xff) 2 -- gen some quick random shiznit

        xvGrad :: Double
        xvGrad = staticTable Vector.! index 
        yvGrad :: Double
        yvGrad = staticTable Vector.! (index + 1)
        zvGrad :: Double
        zvGrad = staticTable Vector.! (index + 2)

        xvPoint :: Double
        xvPoint = fx - (fromIntegral ix)
        yvPoint :: Double
        yvPoint = fy - (fromIntegral iy)
        zvPoint :: Double
        zvPoint = fz - (fromIntegral iz)

{-
intValueNoise3D :: Int -> Int -> Int -> Int -> Int
intValueNoise3D x y z seed = 
  fromIntegral $ (n * (n * n * 60493 + 19990303) + 1376312589) .&. 0x7fffffff
  where nx :: Int32
        nx = fromIntegral x
        ny :: Int32
        ny = fromIntegral y
        nz :: Int32
        nz = fromIntegral z
        nseed :: Int32
        nseed = fromIntegral seed
        baseVal = (xNoiseGen * nx
                 + yNoiseGen * ny
                 + zNoiseGen * nz
                 + seedNoiseGen * nseed) .&. 0x7fffffff
        n = xor (shiftR baseVal 13) baseVal


-- | Generates a value-coherent-noise value from the coordinates of a 3D input
valueCoherentNoise3D :: Double -> Double -> Double -> Int -> NoiseQuality -> Double
valueCoherentNoise3D x y z seed quality = noiseCube x y z quality valNoise
  where valNoise i0 i1 i2 = valueNoise3D (toInt i0) (toInt i1) (toInt i2) seed
        toInt = fromIntegral . floor 


-- | Generates a value-noise value from the coordinates of a 3D input value
valueNoise3D :: Int -> Int -> Int -> Int -> Double
valueNoise3D x y z seed = 
  let noiseCalc = (fromIntegral $ intValueNoise3D x y z seed) in
    1.0 - ( noiseCalc / 1073741824.0 )
    -}