#define fbc -s gui

#include "fbgfx.bi"
#include "fmod.bi"

type RGBPAL
  R as integer
  G as integer
  B as integer
end type
type PALCYC
  Reverse as byte
  Rate as integer
  Low as integer
  High as integer
  Count as single
end type

dim shared as zstring*255 SPATH = "Scenes/"
dim shared as zstring*32 SCENE(...) = { _
"V08AM","Jungle Waterfall - Morning","V08.mp3", _
"V29","Seascape - Day","V29.mp3", _
"V19","Mountain Stream - Morning","V19.mp3", _
"V26SNOW","Winter Forest - Snow","V05RAIN.mp3", _
"V08RAIN","Jungle Waterfall - Rain","V08RAIN.mp3", _
"V14","Mountain Storm - Day","V14.mp3", _
"V30","Deep Forest - Day","V30.mp3", _
"V04","Highland Ruins - Rain","V04.mp3", _
"V07","Rough Seas - Day","V07.mp3", _
"V20","Crystal Caves - Day","V20.mp3", _
"V05RAIN","Haunted Castle Ruins - Rain","V05RAIN.mp3", _
"V08PM","Jungle Waterfall - Night","V08.mp3", _
"V16RAIN","Mirror Pond - Rain","V16RAIN.mp3", _
"V19AURA","Mountain Stream - Night","V19.mp3", _
"CORAL","Aquarius - Day","CORAL.mp3", _
"V15","Harbor Town - Night","V15.mp3", _
"V30RAIN","Deep Forest - Rain","V30RAIN.mp3", _
"V02","Mountain Fortress - Dusk","V02.mp3", _
"V28","Water City Gates - Fog","V28.mp3", _
"V29PM","Seascape - Sunset","V29.mp3", _
"V16","Mirror Pond - Morning","V16.mp3", _
"V01","Island Fires - Dusk","V29.mp3", _
"V09","Forest Edge - Day","V09.mp3", _
"V16PM","Mirror Pond - Afternoon","V16.mp3", _
"V08","Jungle Waterfall - Afternoon","V08.mp3", _
"V03","Swamp Cathedral - Day","V03.mp3", _
"V05HAUNT","Haunted Castle Ruins - Night","V05HAUNT.mp3", _
"V10","Deep Swamp - Day","V10.mp3", _
"V11AM","Approaching Storm - Day","V02.mp3", _
"V13","Pond Ripples - Dawn","V13.mp3", _
"V17","Ice Wind - Day","V17.mp3", _
"V19PM","Mountain Stream - Afternoon","V19.mp3", _
"V25HEAT","Desert Heat Wave - Day","V25HEAT.mp3", _
"V27","Magic Marsh Cave - Night","V25HEAT.mp3", _
"V29FOG","Seascape - Fog","V29.mp3" }

dim shared as byte SMOOTH=1,OPT,UPDATE

function ReadJSON(FNAME as string,PAL() as RGBPAL,CYCLE() as PALCYC) as fb.image ptr
  dim as integer SFILE = freefile()
  
  if open(FNAME for input as #SFILE)=0 then
    
    dim as string FDATA
    dim as integer WID,HEI,CNT
    dim as integer POS1,POS2,POS3,POSZ
    dim as fb.image ptr TMPIMG
    dim as ubyte ptr PIXPTR
    
    while not eof(SFILE)
      dim as string STMP
      line input #SFILE,STMP
      FDATA += lcase$(STMP)
    wend
    close #SFILE    
    
    POSZ = instr(1,FDATA,"colors:[")
    if POSZ < 1 then return 0
    POS1 = instr(POS2+1,FDATA,"width:")
    if POS1 < 1 or POS1 > POSZ then return 0
    POS1 += 6: POS2 = instr(POS1,FDATA,",")
    if POS2=0 or (POS2-POS1) > 5 then return 0    
    WID = valint(mid$(FDATA,POS1,POS2-POS1))
    POS1 = instr(POS2+1,FDATA,"height:")
    if POS1 < 1 or POS1 > POSZ then return 0
    POS1 += 7: POS2 = instr(POS1,FDATA,",")
    if POS2=0 or (POS2-POS1) > 5 then return 0    
    HEI = valint(mid$(FDATA,POS1,POS2-POS1))
    
    POS2 = POSZ+7: POSZ = instr(POS2,FDATA,"cycles:[")
    if POSZ < 1 then return 0
    for CNT = 0 to 255
      with PAL(CNT)
        POS1 = instr(POS2+1,FDATA,"[")+1
        POS2 = instr(POS1,FDATA,",")        
        .R = valint(mid$(FDATA,POS1,POS2-POS1))        
        POS1 = POS2+1
        POS2 = instr(POS1,FDATA,",")
        .G = valint(mid$(FDATA,POS1,POS2-POS1))        
        POS1 = POS2+1
        POS2 = instr(POS1,FDATA,"]")
        if POS2 = 0 then return 0
        .B = valint(mid$(FDATA,POS1,POS2-POS1))        
      end with
      if POS2 > POSZ then return 0
    next CNT
    
    POS2 = POSZ+7: POSZ = instr(POS2,FDATA,"pixels:[")
    if POSZ <1 then return 0
    for CNT = 0 to 15
      with CYCLE(CNT)
        POS1 = instr(POS2+1,FDATA,"{reverse:")+9
        POS2 = instr(POS1,FDATA,",")        
        .Reverse = valint(mid$(FDATA,POS1,POS2-POS1))        
        POS1 = instr(POS2+1,FDATA,"rate:")+5
        POS2 = instr(POS1,FDATA,",")
        .Rate = valint(mid$(FDATA,POS1,POS2-POS1))
        POS1 = instr(POS2+1,FDATA,"low:")+4
        POS2 = instr(POS1,FDATA,",")
        .Low = valint(mid$(FDATA,POS1,POS2-POS1))
        POS1 = instr(POS2+1,FDATA,"high:")+5
        POS2 = instr(POS1,FDATA,"}")
        if POS2 = 0 then return 0        
        .High = valint(mid$(FDATA,POS1,POS2-POS1))        
      end with
      if POS2 > POSZ then return 0
      if mid$(FDATA,POS2,2) = "}]" then exit for
      if CNT = 15 then return 0
    next CNT    
    for CNT = CNT+1 to 15
      with CYCLE(CNT)
        .Reverse=0:.Rate=0:.Low=0:.High=0
      end with
    next CNT        
    TMPIMG = ImageCreate(WID,HEI)
    if TMPIMG=0 then return 0
    PIXPTR = cast(any ptr,TMPIMG+1): POS1 = POSZ+8
    for CNT = 0 to (WID*HEI)-1
      POS2 = instr(POS1,FDATA,",")+1
      if POS2 = 0 then
        *PIXPTR = valint(mid$(FDATA,POS1)): exit for
      else
        *PIXPTR = valint(mid$(FDATA,POS1,(POS2-POS1)-1))
        PIXPTR += 1:POS1=POS2
      end if
    next CNT    
    return TMPIMG
  end if
  
end function

FSOUND_Init(44100,4,0)
chdir exepath()

dim as fb.image ptr MYIMG
dim MYPAL(255) as RGBPAL,MYCYC(15) as PALCYC

screenres 640,480,8

dim as double TMR,SPD=1,VOL=.5
dim as integer SCENEOFF,SCENENUM,SCENEMAX = (ubound(SCENE)\3)

do
  SCENEOFF = SCENENUM*3
  if MYIMG then ImageDestroy(MYIMG):MYIMG=0
  MYIMG = ImageCreate(640,480)  
  scope
    dim as integer MYFILE = freefile()
    bload SPATH+SCENE(SCENEOFF)+".bmp",MYIMG
    if open(SPATH+SCENE(SCENEOFF)+".pal" for binary access read as #MYFILE)=0 then
      get #MYFILE,1,MYPAL()
      get #MYFILE,,MYCYC()
      close #MYFILE
    end if  
  end scope
  
  windowtitle "Palette Animation: ["+SCENE(SCENEOFF+1)+"] "
  for CNT as integer = 0 to 255
    with MYPAL(CNT)
      palette CNT,.R,.G,.B
    end with
  next CNT  
  
  dim as FSOUND_STREAM ptr MUSIC
  MUSIC = FSOUND_Stream_Open("Sound/"+SCENE(SCENEOFF+2),0,0,0)  
  FSOUND_Stream_SetMode(MUSIC,FSOUND_LOOP_NORMAL)
  FSOUND_Stream_Play(1,MUSIC)
  
  put (0,0),MYIMG,pset:UPDATE=1
  line(652,212)-(653+256,213+256),255,B
  line(640,0)-(640,479),255,B
  for Y as integer = 0 to 15
    dim as integer PY = 215+Y*16
    for X as integer = 0 to 15
      dim as integer PX = 655+X*16
      line(PX,PY)-(PX+11,PY+11),Y shl 4+X,bf
    next X
  next Y
  
  do    
    for CNT as integer = 0 to 15
      dim as RGBPAL TMPATT
      with MYCYC(CNT)
        if .Low<>0 and .High<>0 and .Rate>0 then
          dim as integer VMAX = (.High-.Low)          
          .Count += iif(.Reverse,1,-1)*(.Rate/16384)*SPD
          if .Count < 0 then .Count += (VMAX)
          if .Count >= VMAX then .Count -= (VMAX)
          for ATT as integer = 0 to VMAX
            dim as single TMP = .Low+.Count+ATT
            dim as integer VLOW = .Low+ATT,TMP2 = fix(TMP)+1
            if TMP>=.High then TMP-=(VMAX) else if TMP<.Low then TMP+=(VMAX)
            if TMP2>=.High then TMP2-=(VMAX) else if TMP2<.Low then TMP2+=(VMAX)
            with MYPAL(fix(TMP))
              dim as integer RR,GG,BB
              dim as single PCT = TMP-fix(TMP)
              if SMOOTH then
                RR = (.R*(1-PCT))+(MYPAL(TMP2).R*PCT)
                GG = (.G*(1-PCT))+(MYPAL(TMP2).G*PCT)
                BB = (.B*(1-PCT))+(MYPAL(TMP2).B*PCT)              
              else
                RR = .R:GG = .G: BB = .B
              end if
              Palette VLOW,RR,GG,BB
            end with
          next ATT        
        end if
      end with
    next CNT    
    
    if UPDATE then
      UPDATE=0:line(660,20)-(920,65),0,bf
      Draw String (660,20), "SPD: " & cint(SPD*100) & "%",255
      Draw String (660,50), "Volume: " & cint(VOL*100) & "%",255
      FSOUND_SetFrequency(1,44100*SPD)
      FSOUND_SetVolume(1,255*VOL)
      if SMOOTH then
        Draw String (660,35), "Rotation: Smooth",255
      else
        Draw String (660,35), "Rotation: Normal",255
      end if
    end if
    
    if abs(timer-TMR) > .5 then
      TMR = timer
    else
      while (timer-TMR) < 1/60
        sleep 1,1
      wend
      TMR += 1/60
    end if
    
    dim as string KEY = inkey$
    if KEY = chr$(255,FB.SC_PAGEDOWN) or KEY = chr$(255,FB.SC_DOWN) then
      if SCENENUM < SCENEMAX then SCENENUM += 1: exit do
    elseif KEY = chr$(255,FB.SC_PAGEUP) or KEY = chr$(255,FB.SC_UP) then
      if SCENENUM > 0 then SCENENUM -= 1: exit do
    elseif KEY = "=" or KEY = "+" then 
      if VOL < 1 then UPDATE=1:VOL += .1: if VOL > 1 then VOL=1
    elseif KEY = "-" or KEY = "_" then 
      if VOL > 0 then UPDATE=1:VOL -= .1: if VOL < 0 then VOL=0
    elseif KEY = chr$(255,FB.SC_LEFT) then
      if SPD > 1/4 then SPD -= .1:UPDATE=1
    elseif KEY = chr$(255,FB.SC_RIGHT) then
      if SPD < 4 then SPD += .1:UPDATE=1
    elseif KEY = chr$(27) or KEY = chr$(255)+"k" then
      FSOUND_Stream_Stop(MUSIC)
      if MYIMG then ImageDestroy(MYIMG):MYIMG=0
      exit do,do
    elseif KEY = chr$(9) then
      OPT xor= 1: UPDATE = 1
      if OPT then screenres 920,480,8 else screenres 640,480,8
      exit do
    elseif KEY = chr$(13) then
      SMOOTH xor= 1: UPDATE = 1
    end if
  loop
loop


