module Script.Module
  (
  -- * モジュール
  -- ** コマンドを実行
    command
  , commands
  -- ** 環境を得る
  , getTime
  -- *** キーボード
  , getKeyboardEvents
  , getKeyboardState
  -- *** ゲームパッド
  , getJoystickEvents
  , getJoyAxes
  -- *** フレームの状態
  , doesPartExist
  -- ** デバッグ用
  , putStr_
  ) where

import           Control.Monad.Reader
import           Control.Monad.State
import qualified Data.IntMap          as IM
import qualified Data.Map             as M
import           Data.Maybe           (isJust)
import qualified Data.Set             as Set

import           Common
import           Input.Keycode        (Keycode)
import           Script.Core
import           Script.Event
import           Script.Input
import           Script.Types

-- | コマンド `Command` を実行する
--
-- @
-- script :: Scriot ()
-- script = do
--   command $ ShootGun "name-of-gun"
-- @
--
command :: Command -> Script ()
command cmd =
  modify $ \st -> st {sstCommands = cmd : sstCommands st}

-- | 複数のコマンド `Command` を実行する
--
-- @
-- script :: Scriot ()
-- script = do
--   let cmd1 = ShootGun "name-of-gun1"
--       cmd2 = ShootGun "name-of-gun2"
--   commands [cmd1, cmd2]
-- @
--
commands :: [Command] -> Script ()
commands cs =
  modify $ \st -> st {sstCommands = cs ++ sstCommands st}

-- | 指定した名前のパーツが存在するかどうか。
--
-- @
-- script :: Script ()
-- script = do
--   exist <- doesPartExist "name-of-part"
--   when exist $ do
--     let message = if exist
--                     then "あります(^_^)"
--                     else "ありません(._.)"
--     putStr_ message
-- @
--
doesPartExist :: Name -- ^ パーツの名前
              -> Script Bool -- ^ 存在する場合はTrue
doesPartExist name = isJust . M.lookup name <$> asks envFrame

-- | フレーム時刻を得る。
--
-- 1秒は60フレーム。
--
-- @
-- script :: Script ()
-- script = do
--   t <- getTime -- フレーム時刻を得る
--   putStr_ $ show t -- 画面に表示する
-- @
--
getTime :: Script Time
getTime = asks envTime


-- | 現在の時刻における、キーボードイベント `KeyboardEvent` を得る。
--
-- @
-- import Control.Monad    (when)
-- import qualified Script as S
-- import qualified Input.Scancode as Scancode
--
-- script :: Script ()
-- script = do
--   es <- S.getKeyboardEvents -- ジョイスティックイベントを得る
--   when (S.KeyboardEvent Scancode.Scancode'A S.Pressed \`elem\` es) $ -- Aキーが押されたことを示すイベントが存在するか?
--     putStr_ "ボタン5が押されました。"
-- @
--
getKeyboardEvents :: Script [KeyboardEvent]
getKeyboardEvents = asks envKeyboardEvents

-- | 現在のキーボードの状態を得る。
getKeyboardState :: Keycode -- ^ 対象の `Keycode`
                 -> Script Bool -- ^ 押されている場合は True
getKeyboardState keycode =
  Set.member keycode <$> asks envKeycodeSet

-- | 現在の時刻における、ジョイスティックのボタンや方向ボタンのイベント `JoystickEvent` を得る。
--
-- @
-- import Control.Monad    (when)
-- import qualified Script as S
--
-- script :: Script ()
-- script = do
--   es <- S.getJoystickEvents -- ジョイスティックイベントを得る
--   when (S.JoystickEvent 5 S.Pressed \`elem\` es) $ -- ボタン5が押されたことを示すイベントが存在するか?
--     putStr_ "ボタン5が押されました。"
-- @
--
-- = TODO
--
-- * 現在のボタンの状態(押されているか否か?)を得るコマンドは未実装
getJoystickEvents :: Script [JoystickEvent]
getJoystickEvents = asks envJoystickEvents

-- | 現在の時刻における、ジョイスティックの軸(トリガーとスティック)の度合いを得る。
--
-- @
-- import Control.Monad         (when)
-- import qualified Data.IntMap as IM
-- import qualified Script      as S
--
-- script :: Script ()
-- script = do
--   axes <- S.getJoyAxes         -- 軸イベントを得る
--   case IM.lookup 0 axes of     -- 番号0の軸の値を探す
--     Nothing       -> return () -- 軸0が見つからない
--     Just katamuki -> do        -- 軸0の傾きが得られた
--       putStr_ $ show katamuki  -- 画面上に表示してみる
-- @
getJoyAxes :: Script (IM.IntMap AxisPosition)
getJoyAxes = asks envJoyAxes

-- | 画面上に指定のメッセージを表示する。
--
-- @`putStr_` "..."@ は
--
-- @`command` (`PutStr` "...")@ と同じ
--
-- @
-- script :: Script ()
-- script = do
--   putStr_ "画面上に表示されるメッセージ"
-- @
putStr_ :: String -- ^ 画面上に表示したいメッセージ
        -> Script ()
putStr_ = command . PutStr