--** @module player

#INCLUDE "occplayer.module"
#INCLUDE "occSDL.module"
#INCLUDE "occGL.module"
#INCLUDE "course.module"
#INCLUDE "maths.module"

#INCLUDE "player.inc"
#USE "control"
#USE "debug"

#IF NOT (DEFINED (HUD.SHINY))
#DEFINE HUD.FAST
#ENDIF

DATA TYPE POINT
  RECORD
    REAL32 x, y:
:

#INCLUDE "points.inc"

PROTOCOL LASER.DATA
  CASE
    nothing
    data; LASER
:

-- FIgure out how this is really going to work
-- Do we perhaps need to 
-- render.go ? dummy  -- indicate ready to render
-- render.go ? dummy  -- inidcate allowed to render
-- render.go ? dummy  -- indicate done render, do we need this?
PROC render.manager([]CHAN BOOL render.go?)
  TIMER tim:
  INT t:
  WHILE TRUE
    SEQ
      -- Set up for the render
      glClear(GL.COLOR.BUFFER.BIT)
      glPushMatrix()
      -- Let the components render
      SEQ i = 0 FOR SIZE render.go
        BOOL dummy:
        SEQ
          render.go[i] ? dummy
          render.go[i] ? dummy
      -- Finished rendering, swap
      glPopMatrix()
      SDL.GL.SwapBuffers()
      tim ? t
      #IF (NOT DEFINED (HUD.FAST))
      tim ? AFTER t PLUS 10000
      #ELSE
      tim ? AFTER t PLUS 100000
      #ENDIF
:

-- FIXME: Shrink
PROC keyevent()
  SDL.Event event:
  SDL.KeyboardEvent keyevent:
  TIMER tim:
  INT wait:
  VAL INT delay IS 25000:
  SDL.keysym keyinfo:
  BYTE type:
  INT re:
  INITIAL BOOL quit IS FALSE:
  SEQ
    new.SDL.Event(event)
    WHILE NOT quit
      SEQ 
        tim ? wait
        tim ? AFTER wait PLUS delay
        tim ? wait
        SDL.PollEvent(event, re)
        tim ? AFTER wait PLUS delay
        IF
          re > 0
            SEQ
              SDL.Event.type.get(event, type)
              IF 
                (INT type) = SDL.KEYDOWN
                  INT key:
                  SEQ
                    keyevent := (SDL.KeyboardEvent event)
                    SDL.KeyboardEvent.keysym.get(keyevent, keyinfo)
                    SDL.keysym.sym.get(keyinfo, key)
                (INT type) = SDL.KEYUP
                  SKIP 
                (INT type) = SDL.MOUSEBUTTONDOWN
                  SKIP
                (INT type) = SDL.QUIT
                  SEQ
                    -- GRACELESS SHUTDOWN
                    debug.string(debug.success.check, "Exiting HUD...")
                    debug.newline(debug.success.check)
                    SDL.Quit()
                    STOP
                TRUE
                  SKIP
          TRUE 
            SKIP
    delete.SDL.Event(event)
:

PROC eq(VAL REAL32 x, y, []CHAN BOOL render.go!)
  VAL INT MAX.HEIGHT IS 15:
  VAL REAL32 MAX.HEIGHT.REAL IS (REAL32 TRUNC MAX.HEIGHT):
  PROC eq.cell(VAL INT id, CHAN BOOL render.go!)
    VAL REAL32 id.real IS (REAL32 TRUNC id):
    INT seed:
    TIMER tim:
    INITIAL INT height IS MAX.HEIGHT:
    VAL INT COLOUR.SCALE IS ((MOSTPOS INT) / MAX.HEIGHT):
    VAL INT COLOUR.SCALE2 IS ((MOSTPOS INT) / MAX.HEIGHT) * 2:
    SEQ
      tim ? seed
      seed := seed + id
      WHILE TRUE
        INT val:
        SEQ
          val, seed := random(90, seed)
          IF
            (val < 20) AND (height > 0)
              height := height - 1
            (val > 70) AND (height < MAX.HEIGHT)
              height := height + 1
            TRUE
              SKIP
          render.go ! TRUE
          glShadeModel(GL.SMOOTH)
          glBegin(GL.QUAD.STRIP)
          SEQ i = 1 FOR height
            VAL REAL32 i.real IS (REAL32 TRUNC i):
            --INITIAL REAL32 red IS 0.0:
            --INITIAL REAL32 green IS 0.0:
            INITIAL INT red IS 0:
            INITIAL INT green IS 0:
            SEQ
              green := (-i) * COLOUR.SCALE2
              red :=  i * COLOUR.SCALE
              glColor3ui(red, green, 0)
              glVertex2f((x + 1.0) + (id.real * 1.5), y + i.real)
              glVertex2f(x + (id.real * 1.5), y + i.real)
          glEnd()
          render.go ! TRUE
  :

  -- FIXME: This gives me a dynamic replicated par warning, dont want it
  --PAR i = 0 FOR SIZE render.go
  PAR i = 0 FOR 10
    eq.cell(i, render.go[i]!)
:
 
PROC laser.scan(VAL REAL32 x, y, CHAN LASER data.points?, CHAN BOOL render.go!)
  VAL INT NUM.POINTS IS num.laser.samples:

  PROC laser.data.thingy(CHAN LASER data.points?, CHAN LASER.DATA dp!, CHAN BOOL req?)
    --MOBILE []INT data:
    LASER data:
    BOOL has.data:
    SEQ
      --data := MOBILE [180]INT
      SEQ i = 0 FOR SIZE data
        data[i] := 0
      -- Give us some initial data
      has.data := TRUE
      WHILE TRUE
        PRI ALT
          BOOL dummy:
          req ? dummy
            IF
              -- I was trying to use DEFINED here, but that did not
              -- work
              has.data
                SEQ
                  --SCALING the data to the proper range
                  SEQ i = 0 FOR SIZE data
                    INT tmp:
                    SEQ
                      -- Scale value from 0-39
                      --tmp := ((data[i] * 4) / 30)
                      tmp := data[i] / 20
                      IF 
                        tmp >= 40
                          tmp := 39
                        TRUE
                          SKIP
                      data[i] := tmp
                  -- scaled, now send it
                  dp ! data; data
                  has.data := FALSE
              TRUE
                dp ! nothing
          data.points ? data
            has.data := TRUE
  :

  -- Really I should probably have only one display list
  -- for these, and then scale the arc for every marker?
  PROC gen.markers(RESULT INT list, VAL []INT markers)
    SEQ 
      glGenLists(SIZE markers, list)
      glNewList(list, GL.COMPILE)
      SEQ
        glColor4f(0.05, 0.05, 0.7, 0.2)
        SEQ marker.num = 0 FOR SIZE markers
          VAL INT point IS markers[marker.num]:
          SEQ
            --Draw one meter distance
            glBegin(GL.LINE.STRIP)
            -- Pass the array of points instead?
            SEQ i = 0 FOR NUM.POINTS
              glVertex2f(points[point][i][x], points[point][i][y])
            -- Sort out the last point... why?
            --glVertex2f(((-1.0) * (REAL32 TRUNC (ONE.METER))) + x, y)
            glEnd()
      glEndList()
  :

  CHAN LASER.DATA laser.data:
  CHAN BOOL req:
  PAR
    laser.data.thingy(data.points?, laser.data!, req?)
    VAL REAL32 NUM.POINTS.REAL IS (REAL32 TRUNC NUM.POINTS):
    LASER ldata:
    VAL INT RADIUS IS 40:
    VAL REAL32 PI IS 3.14159265535897:
    INT markers.list:
    SEQ
      -- Generate a display list for showing markers at radius
      -- 10, 20 and 30
      gen.markers(markers.list, [10, 20, 30])
      -- Get out first bit of data, error if nothing
      req ! TRUE
      laser.data ? CASE
        nothing
          STOP
        data ; ldata
          SKIP
      -- We can now assert this:
      #PRAGMA DEFINED ldata
      WHILE TRUE
        SEQ
          req ! TRUE
          laser.data ? CASE
            nothing
              SKIP
            data ; ldata
              SKIP
          render.go ! TRUE
          #IF (NOT DEFINED (HUD.FAST))
          glPushAttrib(GL.ALL.ATTRIB.BITS)
          glEnable(GL.BLEND)
          glBlendFunc(GL.SRC.ALPHA, GL.ONE.MINUS.SRC.ALPHA)
          --glBlendFunc(GL.SRC.ALPHA, GL.ONE)
          glEnable(GL.LINE.SMOOTH)
          glEnable(GL.POLYGON.SMOOTH)
          glHint(GL.LINE.SMOOTH.HINT, GL.FASTEST)
          glHint(GL.POLYGON.SMOOTH.HINT, GL.FASTEST)
          #ENDIF
          glLineWidth(3.0)
          glColor4f(0.1, 0.1, 0.6, 0.2)
          glBegin(GL.TRIANGLE.FAN)
          glVertex2f(x, y)
          -- Pass the array of points instead?
          SEQ i = 0 FOR NUM.POINTS
            glVertex2f(points[ldata[i]][i][x], points[ldata[i]][i][y])
          glEnd()
          glColor4f(0.3, 0.3, 0.9, 0.5)
          glBegin(GL.LINE.LOOP)
          -- Pass the array of points instead?
          SEQ i = 0 FOR NUM.POINTS
            glVertex2f(points[ldata[i]][i][x], points[ldata[i]][i][y])
          -- Sort out the last point... why?
          glVertex2f(((-1.0) * (REAL32 TRUNC (ldata[(SIZE ldata) - 1]))) + x, y)
          glEnd()
          -- Draw the markers
          glCallList(markers.list)
          #IF (NOT DEFINED (HUD.FAST))
          glPopAttrib()
          #ENDIF
          render.go ! TRUE
:
 
PROC pioneer(VAL REAL32 x, y, CHAN BOOL render.go!)
  VAL REAL32 px IS x + (5.0):
  VAL REAL32 mx IS x + (-5.0):
  VAL REAL32 py IS y + (6.0):
  VAL REAL32 my IS y + (-10.0):
  WHILE TRUE
    SEQ
      render.go ! TRUE
      glColor3f(0.9, 0.1, 0.2)
      glBegin(GL.QUADS)
      glVertex2f(mx, my)
      glVertex2f(mx, py)
      glVertex2f(px, py)
      glVertex2f(px, my)
      glEnd()
      render.go ! TRUE
:

PROC pioneer.hud(CHAN LASER laser.data)
  INT result:
  SDL.Surface surface:
  VAL INT err.const IS 100:
  INITIAL INT width, height IS 500, 500:
  SEQ
    SDL.Init(SDL.INIT.VIDEO, result)
    --{{{ Test to see if SDL init failed, quit if it did
    IF
      result <> 0
        --[err.const]BYTE err.str:
        --INT err.len:
        SEQ
          --out.string("SDL initialisation failed: ", 0, to.debug!)
          --occ.SDL.GetError(err.str, err.const, err.len)
          --out.string([err.str FOR err.len], 0, to.debug!)
          --out.string("*c*n", 0, to.debug!)
          SDL.Quit()
          STOP
      TRUE
        SKIP
    --}}}
    -- Set up OpenGL
    INT re:
    SEQ
      SDL.GL.SetAttribute(SDL.GL.RED.SIZE, 5, re)
      SDL.GL.SetAttribute(SDL.GL.GREEN.SIZE, 5, re)
      SDL.GL.SetAttribute(SDL.GL.BLUE.SIZE, 5, re)
      SDL.GL.SetAttribute(SDL.GL.DOUBLEBUFFER, 1, re)
      SDL.SetVideoMode(width, height, 16, SDL.OPENGL, surface)
    --{{{ Test to see if OpenGL init failed, qiut if it did
    IF
      (INT surface) = 0
        --[100]BYTE err.str:
        --INT err.len:
        SEQ
          --out.string("GL initialisation failed: ", 0, to.debug!)
          --occ.SDL.GetError(err.str, err.const, err.len)
          --out.string([err.str FOR err.len], 0, to.debug!)
          --out.string("*c*n", 0, to.debug!)
          SDL.Quit()
          STOP
      TRUE
        SKIP
    --}}}
    -- Set up our viewport
    glShadeModel(GL.FLAT)
    glViewport(0, 0, width, height)
    glMatrixMode(GL.PROJECTION)
    glLoadIdentity()
    glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0)
    glMatrixMode(GL.MODELVIEW)
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClear(GL.COLOR.BUFFER.BIT)
    glFlush()
    -- Set up our rendering components
    #IF (NOT DEFINED (HUD.FAST))
    [12]CHAN BOOL render.go:
    #ELSE
    [2]CHAN BOOL render.go:
    #ENDIF
    PAR
      render.manager(render.go?)
      #IF (NOT DEFINED (HUD.FAST))
      eq(35.0, -50.0, [render.go! FROM 2 FOR 10])
      #ENDIF
      pioneer(0.0, 4.6, render.go[0]!)
      laser.scan(0.0, 8.0, laser.data?, render.go[1]!)
      keyevent()
:

PROC pioneer.hud.L(CHAN LASER sick?)
  pioneer.hud(sick?)
:

PROC pioneer.hud.LL(CHAN LASER sick.in?, sick.out!, VAL BOOL enabled)
  IF
    enabled
      CHAN LASER sick.delta:
      PAR
        pioneer.hud(sick.delta?)
        delta.L(sick.in?, sick.out!, sick.delta!)
    NOT enabled
      LASER sick.data:
      WHILE TRUE
        SEQ
          sick.in ? sick.data
          sick.out ! sick.data
:

