module Main where
import System.Time
import System.Locale
import System.Random
import Control.Monad
import Graphics.UI.SDL as SDL

import Cache
import Image
import Paths
import Tiles
import Render
import Zoom

data Status
    = Static  { sWidth :: Int, sHeight :: Int, sTime :: String }
    | ZoomIn  { sWidth :: Int, sHeight :: Int, sTime :: String, sZoom :: Zoom, sSpeed :: Speed }
    | ZoomOut { sWidth :: Int, sHeight :: Int, sTime :: String, sZoom :: Zoom, sSpeed :: Speed }

main :: IO ()
main = initSDL $ \screen initW initH -> loop (Static initW initH "") $ \status -> do
    time' <- getCurrentTime
    let paint = paintScreen screen (sWidth status) (sHeight status) time'
    case status of
        Static{ sTime = t } | t == time' -> do
            SDL.delay 50
            return status
        Static{} -> do
            paint noZoom
            if last time' == '0'
                then initZoomIn time' (sWidth status) (sHeight status)
                else return status{ sTime = time' }
        ZoomIn{ sZoom = z, sWidth = w, sHeight = h, sSpeed = s } -> do
            paint z
            return $ if ratio z >= maxBound
                then ZoomOut
                    { sTime   = time'
                    , sZoom   = z
                    , sSpeed  = 0.05
                    , sWidth  = w
                    , sHeight = h
                    }
                else status
                    { sTime  = time'
                    , sZoom  = nextZoom z (+ s)
                    , sSpeed = (maxBound - ratio z) / 50 + 0.01
                    }
        ZoomOut{ sZoom = z, sWidth = w, sHeight = h, sSpeed = s } -> do
            paint z
            return $ if ratio z <= minBound
                then Static
                    { sTime   = time'
                    , sWidth  = w
                    , sHeight = h
                    }
                else status
                    { sTime = time'
                    , sZoom  = nextZoom z (subtract s)
                    }

data Tiling = MkTiling
    { tTiles :: Tiles
    , tSeeds :: [Int]
    , tEdge  :: Point
    }

{-# NOINLINE _calculateTiling #-}
_calculateTiling :: CacheOnce String [Tiling]
_calculateTiling = initCache newCacheOnce
calculateTiling :: String -> IO [Tiling]
calculateTiling = cacheOnce _calculateTiling $ \time -> forM time $ \ch -> do
    ttf     <- getDataFileName "TimePiece.ttf"
    pts     <- renderChar ttf 22 ch
    tiling  <- makeRandomTiles pts
    seeds   <- forM tiling . const $ randomRIO (1, 3)
    return $ MkTiling tiling seeds (edgePoint (toPointSet pts))

paintScreen :: Surface -> Int -> Int -> String -> Zoom -> IO ()
paintScreen screen w h time zoom = do
    bgColor <- mapRGB (surfaceGetPixelFormat screen) 0x00 0x00 0x33
    fillRect screen Nothing bgColor
    rvs <- calculateTiling time
    let maxX  = 10 -- maximum (map pointX edges)
        maxY  = maximum (map pointY edges)
        edges = map tEdge rvs
    forM_ ([0..] `zip` rvs) $ \(n, MkTiling tiles seeds edge) -> do
        let tiles' = map adjust tiles
            adjust (MkPoint x y, sz) = (MkPoint (x + deltaX) (y + deltaY), sz)
            deltaX = (maxX - pointX edge + 1) `div` 2 + 1
            deltaY = (maxY - pointY edge + 1) `div` 2 + 1
        forM_ (tiles' `zip` seeds) $ \(tile, seed) -> do
            let x = (((16 * 15 * n) `div` 2) + ((w - 964) `div` 2))
                y = (h - 288) `div` 2
            blitTile x y screen seed zoom tile
    SDL.flip screen

getCurrentTime :: IO String
getCurrentTime = do
    fmap (formatCalendarTime defaultTimeLocale "%H:%M:%S")
        . toCalendarTime =<< getClockTime

loop :: Status -> (Status -> IO Status) -> IO ()
loop status f = f status >>= \status' -> do
    event <- pollEvent
    case event of
        Quit -> return ()
        KeyDown (Keysym SDLK_SPACE _ _) -> do
            status'' <- initZoomIn (sTime status') (sWidth status') (sHeight status')
            loop status'' f
        KeyDown (Keysym SDLK_k _ _) -> loop status' f
        KeyDown{}           -> return ()
        MouseButtonDown{}   -> return ()
        _                   -> loop status' f

initZoomIn :: String -> Int -> Int -> IO Status
initZoomIn time' w h = do
    xf <- randomRIO (0, w)
    yf <- randomRIO ((h `div` 2)-96, (h `div` 2)+96)
    return $ ZoomIn
        { sZoom   = MkZoom
            { ratio = 1
            , focus = MkPoint xf yf
            }
        , sTime   = time'
        , sSpeed  = 0
        , sWidth  = w
        , sHeight = h
        }