{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE CPP #-}

#if __GLASGOW_HASKELL__ > 710
{-# OPTIONS_GHC -Wno-missing-export-lists #-}
#endif

{- |

Internal bits and pieces. The actual 'Grid' data structure
is defined here, and various Vector operations on it.

Not intended for public consumption; use "Data.Grid.Storable"
instead.

-}

module Data.Grid.Storable.Internal
  where

import Control.DeepSeq ( NFData(rnf) )

import            Data.Data      (Data (..))
import            Text.Read      (Read(..), readListPrecDefault )
import            Data.Tuple (swap)
import            Data.Typeable  (Typeable)
import qualified  Data.Vector as V
import qualified  Data.Vector.Generic as G

import            Foreign

import Prelude hiding ( length, null,
                        replicate, (++), concat,
                        head, last,
                        init, tail, take, drop, splitAt, reverse,
                        map, concatMap,
                        zipWith, zipWith3, zip, zip3, unzip, unzip3,
                        filter, takeWhile, dropWhile, span, break,
                        elem, notElem,
                        foldl, foldl1, foldr, foldr1,
#if __GLASGOW_HASKELL__ >= 706
                        foldMap,
#endif
                        all, any, and, or, sum, product, minimum, maximum,
                        scanl, scanl1, scanr, scanr1,
                        enumFromTo, enumFromThenTo,
                        mapM, mapM_, sequence, sequence_ )

import Data.Grid.Storable.Mutable

{-# ANN module ("HLint: ignore Use camelCase"::String) #-}
{-# ANN module ("HLint: ignore Eta reduce"::String) #-}

-- | convert an offset to an (x,y) pair
offset_to_coord :: Integral a => (a, b) -> a -> (a, a)
offset_to_coord (w,_h) n = swap $ divMod n w
{-# INLINEABLE offset_to_coord #-}


-- | convert an (x,y) pair to an offset
coord_to_offset :: Num a => (a, b) -> (a, a) -> a
coord_to_offset (w,_h) (x,y) = y * w + x
{-# INLINEABLE coord_to_offset #-}

-- | internal grid implementation
--
data Grid el a =  Grid {-# UNPACK #-} !(V.Vector a)
                       {-# UNPACK #-} !(ForeignPtr el)
  deriving (Typeable, Data)

liftRnfV :: G.Vector a b => (b -> ()) -> a b -> ()
liftRnfV elemRnf =  G.foldl' (const elemRnf) ()

instance NFData (v el) => NFData (Grid el (v el)) where
  rnf = liftRnfV rnf
  {-# INLINEABLE rnf #-}

instance Show (v el) => Show (Grid el (v el)) where
  showsPrec = G.showsPrec

instance Read (v el) => Read (Grid el (v el)) where
  readPrec = G.readPrec
  readListPrec = readListPrecDefault



type instance G.Mutable (Grid el) = MGrid el

instance G.Vector (Grid el) (v el :: *) where
  {-# INLINE basicUnsafeFreeze #-}
  basicUnsafeFreeze _       = error "you can't possible have got hold of a mutable Grid"
  {-# INLINE basicUnsafeThaw #-}
  basicUnsafeThaw _         = error "you can't get thaw Grids"
  {-# INLINE basicLength #-}
  basicLength (Grid vec _) = G.basicLength vec

  {-# INLINE basicUnsafeSlice #-}
  basicUnsafeSlice j n (Grid vec ptr) = Grid (G.basicUnsafeSlice j n vec) ptr

  {-# INLINE basicUnsafeIndexM #-}
  basicUnsafeIndexM (Grid vec _ptr) j = G.basicUnsafeIndexM vec j




instance Eq (v el) => Eq ((Grid el) (v el)) where
  {-# INLINE (==) #-}
  (Grid v1 _) == (Grid v2 _) = v1 == v2

  {-# INLINE (/=) #-}
  (Grid v1 _) /= (Grid v2 _) = v1 /= v2

-- See http://trac.haskell.org/vector/ticket/12
instance Ord (v el) => Ord ((Grid el) (v el)) where
  {-# INLINE compare #-}
  compare (Grid v1 _) (Grid v2 _) = compare v1 v2

  {-# INLINE (<) #-}
  (Grid v1 _) < (Grid v2 _) = v1 < v2

  {-# INLINE (<=) #-}
  (Grid v1 _) <= (Grid v2 _) = v1 <= v2

  {-# INLINE (>) #-}
  (Grid v1 _) > (Grid v2 _) = v1 > v2

  {-# INLINE (>=) #-}
  (Grid v1 _) >= (Grid v2 _) = v1 >= v2

-- Length information
-- ------------------

-- | /O(1)/ Yield the length of the vector
length :: (Grid el) (v el) -> Int
{-# INLINE length #-}
length = G.length

-- | /O(1)/ Test whether a vector is empty
null :: (Grid el) (v el) -> Bool
{-# INLINE null #-}
null = G.null

-- Indexing
-- --------

-- | O(1) Indexing
(!) :: (Grid el) (v el) -> Int -> v el
{-# INLINE (!) #-}
(!) = (G.!)

-- | O(1) Safe indexing
(!?) :: (Grid el) (v el) -> Int -> Maybe (v el)
{-# INLINE (!?) #-}
(!?) = (G.!?)

-- | /O(1)/ First element
head :: (Grid el) (v el) -> v el
{-# INLINE head #-}
head = G.head

-- | /O(1)/ Last element
last :: (Grid el) (v el) -> v el
{-# INLINE last #-}
last = G.last

-- | /O(1)/ Unsafe indexing without bounds checking
unsafeIndex :: (Grid el) (v el) -> Int -> v el
{-# INLINE unsafeIndex #-}
unsafeIndex = G.unsafeIndex

-- | /O(1)/ First element without checking if the vector is empty
unsafeHead :: (Grid el) (v el) -> v el
{-# INLINE unsafeHead #-}
unsafeHead = G.unsafeHead

-- | /O(1)/ Last element without checking if the vector is empty
unsafeLast :: (Grid el) (v el) -> v el
{-# INLINE unsafeLast #-}
unsafeLast = G.unsafeLast

-- Extracting subvectors (slicing)
-- -------------------------------

-- | /O(1)/ Yield a slice of the vector without copying it. The vector must
-- contain at least @i+n@ elements.
slice :: Int   -- ^ @i@ starting index
                 -> Int   -- ^ @n@ length
                 -> (Grid el) (v el)
                 -> (Grid el) (v el)
{-# INLINE slice #-}
slice = G.slice

-- | /O(1)/ Yield all but the last element without copying. The vector may not
-- be empty.
init :: (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE init #-}
init = G.init


-- | /O(1)/ Yield all but the first element without copying. The vector may not
-- be empty.
tail :: (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE tail #-}
tail = G.tail

-- | /O(1)/ Yield at the first @n@ elements without copying. The vector may
-- contain less than @n@ elements in which case it is returned unchanged.
take :: Int -> (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE take #-}
take = G.take

-- | /O(1)/ Yield all but the first @n@ elements without copying. The vector may
-- contain less than @n@ elements in which case an empty vector is returned.
drop :: Int -> (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE drop #-}
drop = G.drop

-- | /O(1)/ Yield the first @n@ elements paired with the remainder without copying.
--
-- Note that @'splitAt' n v@ is equivalent to @('take' n v, 'drop' n v)@
-- but slightly more efficient.
splitAt :: Int -> (Grid el) (v el) -> ((Grid el) (v el), (Grid el) (v el))
{-# INLINE splitAt #-}
splitAt = G.splitAt


-- | /O(1)/ Yield a slice of the vector without copying. The vector must
-- contain at least @i+n@ elements but this is not checked.
unsafeSlice :: Int   -- ^ @i@ starting index
                       -> Int   -- ^ @n@ length
                       -> (Grid el) (v el)
                       -> (Grid el) (v el)
{-# INLINE unsafeSlice #-}
unsafeSlice = G.unsafeSlice

-- | /O(1)/ Yield all but the last element without copying. The vector may not
-- be empty but this is not checked.
unsafeInit :: (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE unsafeInit #-}
unsafeInit = G.unsafeInit

-- | /O(1)/ Yield all but the first element without copying. The vector may not
-- be empty but this is not checked.
unsafeTail :: (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE unsafeTail #-}
unsafeTail = G.unsafeTail

-- | /O(1)/ Yield the first @n@ elements without copying. The vector must
-- contain at least @n@ elements but this is not checked.
unsafeTake :: Int -> (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE unsafeTake #-}
unsafeTake = G.unsafeTake

-- | /O(1)/ Yield all but the first @n@ elements without copying. The vector
-- must contain at least @n@ elements but this is not checked.
unsafeDrop :: Int -> (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE unsafeDrop #-}
unsafeDrop = G.unsafeDrop


-- Permutations
-- ------------

-- | /O(n)/ Reverse a vector
reverse :: (Grid el) (v el) -> (Grid el) (v el)
{-# INLINE reverse #-}
reverse = G.reverse


-- Conversions - Lists
-- ------------------------

-- | /O(n)/ Convert a vector to a list.
--
-- Be very cautious about using this. If the underlying
-- 'ForeignPtr' goes out of scope and gets garbage-collected,
-- then the data it points to may get freed, meaning all
-- the data in the vectors returned by this function is
-- probably invalid. Probably you should use the version in "Data.Grid.Storable"
-- instead.
toList :: (Grid el) (v el) -> [v el]
{-# INLINE toList #-}
toList = G.toList