{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
module Object.Computer where

import Affection as A

import Control.Monad (when)
import Control.Monad.IO.Class (MonadIO(..))

import Data.Ecstasy
import Data.Maybe
import Data.String

import Linear

-- internal imports

import Types

import Object.ActionTime

computerObjectAction
  :: (Monad m, MonadIO m)
  => Double
  -> ObjType
  -> ObjState
  -> Ent
  -> SystemT Entity m ()
computerObjectAction dt t@ObjComputer s@"on" ent = do
  pent <- efor (anEnt ent) $ do
    with objUsedBy
    query objUsedBy
  vls <- efor (anEnt $ head pent) $ do
    with player
    with vel
    query vel
  emap (anEnt ent) $ do
    mtime <- queryMaybe objStateTime
    case mtime of
      Nothing -> do
        liftIO $ logIO Verbose ("Computer " <> fromString (show ent) <> ": on!")
        let dur = actionTime t s
        return unchanged
          { objStateTime = Set dur
          }
      Just ttl ->
        return unchanged
          { objStateTime = Set (ttl - dt)
          }
  trans <- efor (anEnt ent) $ do
    mttl <- queryMaybe objStateTime
    case mttl of
      Nothing -> return Nothing
      Just ttl -> do
        pa <- query objPlayerActivated
        if ttl < 0
        then
          return (Just pa)
        else if not (null vls) && head vls `dot` head vls > 0
          then return (Just pa)
          else return Nothing
  maybe
    (return ())
    (\tpa -> setEntity ent =<< computerObjectTransition t s tpa ent Nothing)
    (head trans)

computerObjectAction dt t@ObjComputer s@"hack" ent = do
  aent <- efor (anEnt ent) $ do
    with objUsedBy
    query objUsedBy
  vls <- efor (anEnt $ head aent) $ do
    with player
    with vel
    query vel
  emap (anEnt ent) $ do
    mtime <- queryMaybe objStateTime
    case mtime of
      Nothing -> do
        liftIO $ logIO
          Verbose
          ("Computer " <> fromString (show ent) <> ": hacking!")
        return unchanged
          { objStateTime = Set (actionTime t s)
          }
      Just ttl ->
        return unchanged
          { objStateTime = Set (ttl - dt)
          }
  trans <- efor (anEnt ent) $ do
    mttl <- queryMaybe objStateTime
    case mttl of
      Nothing -> return Nothing
      Just ttl -> do
        if (ttl < 0) || (not (null vls) && head vls `dot` head vls > 0)
        then do
          tpa <- query objPlayerActivated
          return (Just tpa)
        else return Nothing
  maybe
    (return ())
    (\tpa -> setEntity ent =<< computerObjectTransition t s tpa ent Nothing)
    (head trans)

computerObjectAction _ _ _ _ = return ()

computerObjectTransition
  :: (Monad m, MonadIO m)
  => ObjType
  -> ObjState
  -> Bool
  -> Ent
  -> Maybe Ent
  -> SystemT Entity m (Entity 'SetterOf)
computerObjectTransition ObjComputer "off" pa ent (Just aent) = do
  ldir <- efor (anEnt aent) (query rot)
  let dir = head ldir
  e <- efor (anEnt ent) $ do
    solved <- queryMaybe objSolved
    if pa
    then if not (fromMaybe False solved)
      then do
        let nstat = AnimState
              (AnimId AnimComputer "hack" dir)
              0
              0
        return unchanged
          { anim               = Set nstat
          , objState           = Set "hack"
          , objPlayerActivated = Set True
          , objUsedBy          = Set aent
          }
      else do
        let nstat = AnimState
              (AnimId AnimComputer "on" dir)
              0
              0
        return unchanged
            { anim               = Set nstat
            , objState           = Set "on"
            , objPlayerActivated = Set True
            , objUsedBy          = Set aent
            }
    else do
      let nstat = AnimState
            (AnimId AnimComputer "on" dir)
            0
            0
      return unchanged
          { anim               = Set nstat
          , objState           = Set "on"
          , objPlayerActivated = Set False
          , objUsedBy          = Set aent
          }
  return (head e)

computerObjectTransition ObjComputer "on" _ ent _ = do
  e <- efor (anEnt ent) $ do
    dir <- query rot
    let nstat = AnimState
          (AnimId AnimComputer "off" dir)
          0
          0
    return unchanged
      { anim               = Set nstat
      , objState           = Set "off"
      , objPlayerActivated = Unset
      , objStateTime       = Unset
      , objUsedBy          = Unset
      }
  return (head e)

computerObjectTransition ObjComputer "hack" pa ent _ =
  if pa
  then do
    e <- efor (anEnt ent) $ do
      dir <- query rot
      let nstat = AnimState
            (AnimId AnimComputer "off" dir)
            0
            0
      ost <- query objStateTime
      return unchanged
        { anim               = Set nstat
        , objState           = Set "off"
        , objPlayerActivated = Unset
        , objStateTime       = Unset
        , objUsedBy          = Unset
        , objSolved          = if pa then Set (ost < 0) else Keep
        }
    return (head e)
  else return unchanged

computerObjectTransition _ _ _ _ _ = return unchanged
