{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric  #-}

module CellularFluid.Grid.Types where

import           RIO
import qualified RIO.List              as L
import qualified RIO.Vector            as V

import qualified Data.Serialize        as S
import           Data.Vector.Serialize()

import           Numeric

import           Linear

-- | Time
type T = Double

type DT = Double

-- | Length
type L = Double

-- | Position
type X = V2 Double

-- | Velocity
type U = V2 Double

-- | Acceleration
type DU = V2 Double

-- | ∇·(∂u/∂t)
type DIV_DU = Double

-- | Pressure
type P = Double

type DP = Double

-- | Density
type Rho = Double

-- | Kinematic Viscosity
type Mu = Double

type Nu = Double

-- | Next state logic
type NSL = Cell -> AdjCells -> Cell

{-|
   Data type for one simulation cell
-}
data Cell
  = FluidD P -- ^ Diffusive fluid
  | Wall -- ^ Perfect wall cell
  | Edge -- ^ Edge
  deriving (Generic, NFData)

instance S.Serialize Cell

-- | Adjacent cells
data Adj a =
  Adj
    { adjEdge   :: Vector a
    , adjCorner :: Vector a
    }
  deriving (Show, Functor, Generic, NFData)

type AdjCells = Adj Cell

data Grid =
  Grid
    { gridCells  :: !(Vector Cell) -- ^ Row major, top-left is (0, 0)
    , gridWidth  :: !Int
    , gridHeight :: !Int
    }
  deriving (Generic, NFData)

instance S.Serialize Grid

instance Show Grid where
  show = showGrid

data PhysCfg =
  PhysCfg
    { phyGridSize :: Double
    , phyFDRho    :: Double
    , phyFDMu     :: Double
    }
  deriving (Show)

showGrid :: Grid -> String
showGrid g = go cells
  where
    rowSize = gridWidth g
    cells = gridCells g
    go :: Vector Cell -> String
    go cs
      | null cs = ""
      | otherwise =
        let (h, t) = V.splitAt rowSize cs
            hstr = showRow h ++ "\n"
         in hstr `seq` (hstr ++ go t)

cloneGrid :: Grid -> Grid
cloneGrid g = g {gridCells = cells'}
  where
    cells = gridCells g
    cells' = (V.new . V.clone) cells

instance Show Cell where
  show = showCell

showCell :: Cell -> String
showCell (FluidD p) = L.intercalate " " ["FD", sg p]
  where
    sg a = showGFloat Nothing a ""
showCell Wall = "W"
showCell Edge = "E"

showRow :: Vector Cell -> String
showRow = L.intercalate "," . toList . fmap show

{-|
   Grid configuration
-}
data SimulationCFG =
  SimulationCFG
    { cfgTimeStep :: Double
    , cfgPhysics  :: PhysCfg
    }
