{-# LANGUAGE DeriveGeneric #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}

{- |
Module      :  $Header$
Description :  The ViewPort type and its operations.
Copyright   :  (c) plaimi 2015
License     :  AGPL-3

Maintainer  :  blubber@plaimi.net
-} module Blubber.Server.ViewPort where

import Data.Map (Map)
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Data.Maybe
  (
  fromJust,
  )
import Data.Serialize
  (
  Serialize,
  )
import GHC.Generics
  (
  Generic,
  )

import Blubber.Server.Entity
  (
  Entity,
  mass,
  position,
  radius,
  )
import Blubber.Server.Vector
  (
  Vector (Vec),
  (^-^),
  magVec,
  vx,
  vy,
  )
import Blubber.Server.World
  (
  World,
  neutrals,
  clamp,
  height,
  neutrals,
  players,
  width,
  )

-- | A 'ViewPort' is the part of the 'World' that a client is privy to.
data ViewPort = MkViewPort
              {viewPos         :: Vector
               -- ^ The absolute position of the 'ViewPort'
              ,viewWidth       :: Double
               -- ^ The width of the 'ViewPort'.
              ,viewHeight      :: Double
               -- ^ The height of the 'ViewPort'.
              ,visiblePlayers  :: Map String Entity
              -- ^ The visible 'PlayerBlob's in the 'ViewPort'.
              ,visibleNeutrals :: [Entity]
              -- ^ The visible 'NeutralBlob's in the 'ViewPort'.
              } deriving (Eq, Generic, Show)


-- | 'ViewPort' uses a 'Serialize' instance to encode its data.
instance Serialize ViewPort

mkViewPort :: World -> String -> ViewPort
-- | Make a 'ViewPort' for the passed in client's 'Entity'. If they don't have
--   an 'Entity', then we'll just give them the entire 'World' via
--   'worldViewPort'. The 'ViewPort' also includes the 'mass' of the player's
--   own 'Entity'.
mkViewPort w p =
  case M.lookup p (players w) of
    Just b  -> MkViewPort {viewPos         = position e
                          ,viewWidth       = wid
                          ,viewHeight      = hei
                          ,visiblePlayers  =
                            M.insert (p ++ ": " ++ show (floor (mass e))) b
                          . M.delete p $ getVisiblePlayers (players w)
                                                           (position e)
                                                           (radius e)
                          ,visibleNeutrals = S.toList
                                           $ getVisibles (neutrals w)
                                                         (position e)
                                                         (radius e)
                          }
    Nothing -> worldViewPort w
  where e   = fromJust (M.lookup p (players w)) -- Stooopid.
        wid = radius e * 16
        hei = radius e * 9

getVisiblePlayers :: Map String Entity -> Vector -> Double
                  -> Map String Entity
-- | Figure out which 'PlayerBlob's are visible to a client, based on their
--   'position' and 'radius'.
getVisiblePlayers v pos r = M.filter visibleP v
  where visibleP e = magVec (dev e ^-^ Vec (c 16 vx e) (c 9 vy e)) <= radius e
        dev        = (^-^ pos) . position
        c a d e    = clamp ((-(r * a)) / 2) (r * a / 2) (d (dev e))

getVisibles :: Set Entity -> Vector -> Double -> Set Entity
-- | Figure out which 'NeutralBlob's are visible to a client, based on their
--   'position' and 'radius'.
getVisibles v pos r = S.filter visibleP v
  where visibleP e = magVec (dev e ^-^ Vec (c 16 vx e) (c 9 vy e)) <= radius e
        dev        = (^-^ pos) . position
        c a d e    = clamp ((-(r * a)) / 2) (r * a / 2) (d (dev e))

worldViewPort :: World -> ViewPort
-- | Make a 'ViewPort' of essentieally the whole 'World' and all its
--   'Entity's.
worldViewPort w = MkViewPort
                {viewPos         = Vec 0 0
                ,viewWidth       = width w
                ,viewHeight      = height w
                ,visiblePlayers  = players w
                ,visibleNeutrals = S.elems (neutrals w)
                }

viewPort :: World -> String -> ViewPort
viewPort w p = w `mkViewPort` p