module "/pliant/language/context.pli"
module "/pliant/SDL/SDL.pli"
module "/pliant/math/functions.pli"

constant stdlib "libc.so.6"
constant sdl_lib "libSDL.so"
constant SDL_DISABLE 0
constant SDL_ENABLE 1

#Not implemented in bindings... yet.
function SDL_ShowCursor toggle
 arg Int toggle
 external sdl_lib "SDL_ShowCursor"

#Some stdlib functions:
function srand seed
 arg Int seed
 external stdlib "srand"

function rand -> z
 arg uInt z
 external stdlib "rand"

function time timeptr -> epoc
 arg uInt epoc
 arg Address timeptr
 external stdlib "time"

constant ASTEROID_MAX     30  #Max. number of asteroids at a time
#Asteroid sprite flags
constant ASTEROID_NORMAL  00h #Asteroid is normal
constant ASTEROID_HIT     01h #Asteroid is and was hit
constant ASTEROID_VISIBLE 02h #Asteroid cannot be seen
constant ASTEROID_GONE    04h #Asteroid left game field   
constant ASTEROID_NEW     08h #Asteroid left game field   

#system
gvar Pointer:SDL_Surface screen #Screen
gvar SDL_Event event;           #Event structure
gvar Int mouse_x mouse_y        #Mouse position
gvar Int joypos_x joypos_y      #Joystick position
gvar CBool mouse_button1         #Mouse button 1

#Game objects

gvar SDL_Rect playfield         #Dimensions of playinf field
gvar SDL_Rect pan_velocity      #Speed of panning motion
gvar SDL_Rect viewingArea       #Viewable area of game field

gvar SDL_Rect targetzone        #Area within target can move w/o panning
gvar SDL_Rect targetlocation    #Where target is located.

type Sprite
 field uInt flags          # State flag
 field SDL_Rect rectangle  # Position & size
 field SDL_Rect bounds     # Collistion boundries
 field SDL_Rect velocity   # Directional velocity
 field uInt alpha          # 0-255; transparency
 field Pointer:SDL_Surface image   # Source image

gvar Array:Sprite asteroids     #Asteroids!

#Game graphics
gvar Pointer:SDL_Surface backdrop;   constant backdrop_file   "images/Planet.bmp"
gvar Pointer:SDL_Surface crosshairs; constant crosshairs_file "images/crosshairs.bmp"
gvar Pointer:SDL_Surface viewport;   constant viewport_file   "images/viewport.bmp"

gvar Pointer:SDL_Surface asteroid_field #Buffer where asteroids are scattered.
gvar Pointer:SDL_Surface asteroidA;  constant asteroidA_file "images/asteroidA.bmp"
gvar Pointer:SDL_Surface asteroidB;  constant asteroidB_file "images/asteroidB.bmp"
gvar Pointer:SDL_Surface asteroidC;  constant asteroidC_file "images/asteroidC.bmp"
gvar Pointer:SDL_Surface explosion;  constant explosion_file "images/explosion.bmp"

#Clean up for exit. 
function clean_up

 if exists:backdrop
  SDL_FreeSurface backdrop

 if exists:viewport
  SDL_FreeSurface viewport

 if exists:crosshairs
  SDL_FreeSurface crosshairs

 SDL_Quit

function Load_Bitmap surface file -> z
 arg_rw Pointer:SDL_Surface surface #arg_rw says I can modify the argument.
 arg Str file
 arg Int z

 var Pointer:SDL_Surface temp

 surface :> SDL_LoadBMP file

 if not exists:surface
  console "Could not open " file eol
  return -1

 temp :> SDL_DisplayFormat surface
 SDL_FreeSurface surface
 surface :> temp

 return 0

function Load_Graphics -> z
 arg Int z

 if (Load_Bitmap viewport viewport_file) < 0
  return -1

 if (Load_Bitmap backdrop backdrop_file) < 0
  return -1

 if (Load_Bitmap crosshairs crosshairs_file) < 0
  return -1

 if (Load_Bitmap asteroid_field backdrop_file) < 0
  return -1

 if (Load_Bitmap asteroidA asteroidA_file) < 0
  return -1

 if (Load_Bitmap asteroidB asteroidB_file) < 0
  return -1

 if (Load_Bitmap asteroidC asteroidC_file) < 0
  return -1

 if (Load_Bitmap explosion explosion_file) < 0
  return -1

 #Blank the asteroid field to get a black image w/ same dimensions of backdrop
 SDL_FillRect asteroid_field null (SDL_MapRGB asteroid_field:format 0 255 0)

 #Set color keys. Keys are colors in the image that is invisible 
 SDL_SetColorKey crosshairs SDL_SRCCOLORKEY .or. SDL_RLEACCEL 0 #black
 SDL_SetColorKey explosion SDL_SRCCOLORKEY .or. SDL_RLEACCEL 0

 SDL_SetColorKey asteroid_field SDL_SRCCOLORKEY
  SDL_MapRGB viewport:format 0 255 0 #green 

 SDL_SetColorKey viewport SDL_SRCCOLORKEY .or. SDL_RLEACCEL
  SDL_MapRGB viewport:format 0 255 0

 SDL_SetColorKey asteroidA SDL_SRCCOLORKEY .or. SDL_RLEACCEL
  SDL_MapRGB asteroidA:format 0 255 0

 SDL_SetColorKey asteroidB SDL_SRCCOLORKEY .or. SDL_RLEACCEL
  SDL_MapRGB asteroidB:format 0 255 0

 SDL_SetColorKey asteroidC SDL_SRCCOLORKEY .or. SDL_RLEACCEL
  SDL_MapRGB asteroidC:format 0 255 0

 #Making surface semi-transparent. 
 SDL_SetAlpha explosion SDL_SRCALPHA .or. SDL_RLEACCEL 128

 return 0

#Init 
function init_sdl  -> z
 arg Int z

 #Initalize SDL
 if (SDL_Init SDL_INIT_VIDEO) < 0
  console "Could not initialize video subsystem."
  return -1

 #Open screen
 screen :> SDL_SetVideoMode  640 480 16 SDL_SWSURFACE

 if not exists:screen
  console "Could not open display."
  return -1

 SDL_ShowCursor SDL_DISABLE
 return 0


function init_game -> z
 arg Int z

 playfield:x := 0; playfield:y := 0
 playfield:w := backdrop:w
 playfield:h := backdrop:h

 viewingArea:x := 0; viewingArea:y := 0
 viewingArea:w := screen:w
 viewingArea:h := screen:h

 targetlocation:x := 0; targetlocation:y := 0
 targetlocation:w := crosshairs:w
 targetlocation:h := crosshairs:h

 targetzone:x := targetlocation:x
 targetzone:y := targetlocation:y
 targetzone:w := targetlocation:w + targetlocation:x
 targetzone:h := targetlocation:h + targetlocation:y

 pan_velocity:x := 2; pan_velocity:y := 2

 #Seed the randomizer with current time
 srand (time null)

 return 0

function update_backdrop

 if mouse_x < (screen:w\2)
  if viewingArea:x > 0
   viewingArea:x -= pan_velocity:x

 eif mouse_x > (screen:w\2)
  if viewingArea:x+viewingArea:w < playfield:w
   viewingArea:x += pan_velocity:x

 if mouse_y < (screen:h\2)
  if viewingArea:y > 0
   viewingArea:y -= pan_velocity:y

 eif mouse_y > (screen:h\2)
  if viewingArea:y+viewingArea:h < playfield:h
   viewingArea:y += pan_velocity:y

 return

function asteroid_add
 var Sprite a
 var Int whichtype
 var Pointer:SDL_Surface image

 if (asteroids size) > ASTEROID_MAX
  return
 #Randomly pick either one of 3 asteroid shapes.
 #3 times out of 10, an asteroid will be made.
 whichtype := rand % 3

 if whichtype = 0
  image :> asteroidA
 eif whichtype = 1
  image :> asteroidB
 eif whichtype = 2
  image :> asteroidC
 else
  return

 a:flags := ASTEROID_NORMAL .or. ASTEROID_NEW
 a:image :> image
 a:rectangle:x := rand % (asteroid_field:w - a:image:w)
 a:rectangle:y := rand % (asteroid_field:h - a:image:h)
 a:rectangle:w := a:image:w; a:rectangle:h := a:image:h
 a:velocity:x := 1
 a:velocity:y := 1
 a:alpha:= 255

 asteroids += a
 return

function asteroid_del index
 arg Int index

 if (asteroids size) = 0
  return

 asteroids index := asteroids:(asteroids:size - 1)
 asteroids size := asteroids:size - 1
 return


function checkIfHit asteroid
 arg_rw Sprite asteroid
 var Int top right bottom left
 var Int target_x target_y
 var CBool fire

 top  := asteroid:rectangle:y
 right := asteroid:rectangle:x + asteroid:rectangle:w
 bottom := asteroid:rectangle:y + asteroid:rectangle:h
 left := asteroid:rectangle:x

 fire := mouse_button1

 target_x := mouse_x + viewingArea:x
 target_y := mouse_y + viewingArea:y

 if target_x > left and target_x < right and target_y > top and target_y < bottom and fire
   asteroid:flags := asteroid:flags .or. ASTEROID_HIT

 return

function checkIfGone asteroid
 arg_rw Sprite asteroid
 var Int top right bottom left

 if asteroid:flags .and. ASTEROID_GONE > 0
  return

 top  := asteroid:rectangle:y
 right := asteroid:rectangle:x + asteroid:rectangle:w
 bottom := asteroid:rectangle:y + asteroid:rectangle:h
 left := asteroid:rectangle:x


 if (right < playfield:x) or (left > playfield:w) or (bottom < playfield:h) or (top > playfield:y)
  asteroid:flags := asteroid:flags .and. ASTEROID_GONE
  console "gone" eol

 return


function update_asteroids
 var Int i
 var Pointer:Sprite asteroid

 for i 0 (asteroids:size - 1)
   asteroid :> (asteroids i)

   checkIfHit asteroid

   if (asteroid:flags .and. ASTEROID_HIT) > 0
    asteroid:image :> explosion
   #checkIfGone asteroid

   #if (asteroid:flags .and. ASTEROID_GONE) > 0
   # asteroid_del i    

   #asteroid:rectangle:x += asteroid:velocity:x 
   #asteroid:rectangle:y += asteroid:velocity:y 

 asteroid_add
 return

function draw_asteroids
 var Int i
 var Pointer:Sprite asteroid

 for i 0 (asteroids:size - 1)
  asteroid :> asteroids i
  SDL_BlitSurface asteroid:image null asteroid_field asteroid:rectangle

 SDL_BlitSurface asteroid_field viewingArea screen null
 return

function update_crosshairs
 targetlocation:x := mouse_x - crosshairs:w \ 2
 targetlocation:y := mouse_y - crosshairs:h \ 2
 return

function draw_backdrop
 SDL_BlitSurface backdrop viewingArea screen null
 return

function draw_viewport
 SDL_BlitSurface viewport null screen null
 return

function refresh
  SDL_Flip screen
  return

function draw_crosshairs
  SDL_BlitSurface crosshairs null screen targetlocation
  return


#Main 
function main -> z
 arg Int z

 var CBool done

 if ((init_sdl) < 0)
  console "Initialization failed." eol
  clean_up
  return 1

 if ((Load_Graphics) < 0)
  console "Cannot load graphics." eol
  clean_up
  return  1

 if ((init_game) < 0 )
  console "Cannot intialize game." eol
  clean_up
  return 1

 done := false
 var uInt32 i
 i:=0
 mouse_button1 := false
 while not done

  update_backdrop
  update_asteroids
  update_crosshairs

  draw_backdrop
  draw_asteroids
  draw_crosshairs
  draw_viewport
  refresh

  while (SDL_PollEvent:event > 0)

   if event:type = SDL_QUIT
    done := true

   eif event:type = SDL_KEYUP
    if event:key:keysym:sym = SDLK_ESCAPE
     done := true

   eif event:type = SDL_MOUSEMOTION
     mouse_x := event:motion:x
     mouse_y := event:motion:y

   eif event:type = SDL_MOUSEBUTTONDOWN
     mouse_button1 := true

   eif event:type = SDL_MOUSEBUTTONUP
     mouse_button1 := false

 clean_up
 return 0

main