{-# LANGUAGE OverloadedStrings #-}
import Control.Concurrent
import Graphics.GL
import Linear
import System.Random
import Data.Time
import Control.Monad
import Control.Monad.State
import Data.Bits
import Text.Show.Pretty
import Data.Maybe

import Halive.Utils
import Cube
import Shader
import Window

import SDL hiding (get)
import qualified SDL as SDL


import qualified Green as Green -- Try changing the green amount
                                -- while the program is running

main :: IO ()
main = do
    putStrLn "MAIN BEGIN"
    -- error "DANG!" -- It's ok if your program crashes, it shouldn't crash Halive

    -- Wrap any persistent state in 'reacquire' plus a unique asset ID.
    -- Reacquire uses foreign-store to only run your setup function once,
    -- and then return it again on subsequent recompilations.
    -- In this case, GLFW doesn't like being initialized more than once
    -- per process, so this solves the problem handily.
    -- (Our window stays persistent as well thanks to this,
    -- so it would probably be a good idea anyway!)

    (win, _ctx) <- reacquire "win" $ createGLWindow "Hot SDL"

    -- You can change the window title here.
    --GLFW.setWindowTitle win "Hot Swap!"

    -- Changing the shaders' contents will trigger Halive as well!
    program <- createShaderProgram "demo/cube.vert" "demo/cube.frag"
    cube <- makeCube program

    -- Any GL state will stick around, so be aware of that.
    glEnable GL_DEPTH_TEST

    -- Sometimes it's useful to know if we're running under Halive or not
    putStrLn . ("Running under Halive: " ++ ) . show =<< isHaliveActive

    -- Reacquire our state from the last run, if any - otherwise create a new state
    initialState <- reacquire "state" (return ([]::[V3 GLfloat]))
    void . flip runStateT initialState . whileWindow win $ \events -> do
        -- Store our state persistently in a named slot
        persistState "state"



        -- Try turning on a stream of events
        -- unless (null events) $
        --     liftIO $ pPrint events

        winSize@(V2 w h) <- fmap realToFrac <$> SDL.get (SDL.windowSize win)
        now <- realToFrac . utctDayTime <$> liftIO getCurrentTime
        -- print now -- Try turning on a stream of now logs
        let redFreq  = 0.6 * pi -- Try changing the red and blue frequencies.
            red      = sin (now * redFreq)
            blueFreq = 0.5 * pi
            blue     = sin (now * blueFreq)
        --putStrLn "glClearColor"
        glClearColor red 0.3 blue 1
        --putStrLn "glClear"
        glClear (GL_COLOR_BUFFER_BIT .|. GL_DEPTH_BUFFER_BIT)
        -- Render our scene
        --putStrLn "getWinSize"

        let projection = perspective 45 (w / h) 0.01 1000
            model      = mkTransformation (axisAngle (V3 0 1 1) now) (V3 (sin now) 0 (-4))
            view       = lookAt (V3 0 2 5) (V3 0 0 (-4)) (V3 0 1 0)
            projView   = projection !*! view
            mvp        = projView !*! model

        --putStrLn "renderCube"
        renderCube cube mvp

        -- Accumulate mouse drags as cube trails
        forM_ (catMaybes $ map matchMouse events) $ \cursorPos -> do
            isMouseDown <- SDL.getMouseButtons
            when (isMouseDown ButtonLeft) $ do
                let worldPos = windowPosToWorldPos winSize projView cursorPos 20
                modify' ((worldPos :) . take 40)

        positions <- get
        forM_ positions $ \cursorPos -> do
            let model = mkTransformation (axisAngle (V3 0 1 1) now) cursorPos
                mvp   = projView !*! model
            renderCube cube mvp
        --putStrLn "glSwapWindow"
        SDL.glSwapWindow win


matchMouse Event
    { eventPayload =
        MouseMotionEvent
          MouseMotionEventData
            { mouseMotionEventWhich = Mouse 0
            , mouseMotionEventPos = P pos
            }
    }
    = Just (fromIntegral <$> pos)
matchMouse _
    = Nothing