/'===============================================================================================
 Stream IO Example
 Copyright (c), Firelight Technologies Pty, Ltd 2004-2011.

 This example shows how to play a stream and use a custom file handler that defers reads for the
 streaming part.  FMOD will allow the user to return straight away from a file read request and
 supply the data at a later time.
==============================================================================================='/
#include "windows.bi"
#include "crt.bi"
#include "crt/process.bi"
#include "fmodex/fmodex.bi"
#include "fmodex/fmod_errors.bi"

sub ERRCHECK(byval result as FMOD_RESULT)
    if (result <> FMOD_OK) then
        printf(!"FMOD error! (%d) %s\n", result, FMOD_ErrorString(result))
        end -1
end if
end sub



dim shared as CRITICAL_SECTION gCrit

type dataqueue
    as dataqueue ptr next
    as FMOD_ASYNCREADINFO ptr info
end type

dim shared as dataqueue queuehead = type( @queuehead, 0 )
dim shared as integer gThreadQuit = 0
dim shared as integer gSleepBreak = 0


/'
    File callbacks
'/
function myopen(byval fname as zstring ptr, byval unicode as integer, byval filesize as uinteger ptr, byval handle as any ptr ptr, byval userdata as any ptr ptr) as FMOD_RESULT
    if (fname) then
        dim as FILE ptr fp = any

        fp = fopen(fname, "rb")
        if (fp = 0) then
            return FMOD_ERR_FILE_NOTFOUND
        end if

        fseek(fp, 0, SEEK_END)
        *filesize = ftell(fp)
        fseek(fp, 0, SEEK_SET)

        *userdata = cast(any ptr,&h12345678)
        *handle = fp
    end if

    return FMOD_OK
end function

function myclose(byval handle as any ptr, byval userdata as any ptr) as FMOD_RESULT
    if (handle = 0) then
        return FMOD_ERR_INVALID_PARAM
    end if

    fclose(handle)

    return FMOD_OK
end function

function myread(byval handle as any ptr, byval buffer as any ptr, byval sizebytes as uinteger, byval bytesread as uinteger ptr, byval userdata as any ptr) as FMOD_RESULT
    if (handle = 0) then
        return FMOD_ERR_INVALID_PARAM
    end if

    if (bytesread) then
        *bytesread = fread(buffer, 1, sizebytes, handle)
        if (*bytesread < sizebytes) then
            return FMOD_ERR_FILE_EOF
        end if
    end if


    return FMOD_OK
end function


function myseek(byval handle as any ptr, byval position as uinteger, byval userdata as any ptr) as FMOD_RESULT
    printf(!"************ seek to %d\n", position)
    if (handle = 0) then
        return FMOD_ERR_INVALID_PARAM
    end if
   
    fseek(handle, position, SEEK_SET)

    return FMOD_OK
end function


function myasyncread(byval info as FMOD_ASYNCREADINFO ptr, byval userdata as any ptr) as FMOD_RESULT
    dim as dataqueue ptr current = any
    
    printf(!"REQUESTING %5d bytes from offset %5d PRIORITY = %d.\n", info->sizebytes, info->offset, info->priority)
    
    current = malloc(sizeof(dataqueue))
    if (current = 0) then
        info->result = FMOD_ERR_MEMORY
        return FMOD_ERR_MEMORY
    end if
    
    EnterCriticalSection(@gCrit)

    current->next = queuehead.next    
    current->info = info
    queuehead.next = current
    
    if (info->priority > 50) then
        gSleepBreak = 1     /' Tell file thread to stop sleeping, we have an urgent request here! '/
    end if
    
    LeaveCriticalSection(@gCrit)

    return FMOD_OK
end function

function myasynccancel(byval handle as any ptr, byval userdata as any ptr) as FMOD_RESULT
    dim as dataqueue ptr current = any

    EnterCriticalSection(@gCrit)
    current = queuehead.next

    while (current <> @queuehead)
        if (current->info->handle = handle) then
            queuehead.next = current->next
            free(current)
            current = queuehead.next
        else
            current = current->next
        end if
    wend

    LeaveCriticalSection(@gCrit)

    return FMOD_OK
end function


function ProcessQueue(byval param as any ptr) as uinteger
    while (gThreadQuit = 0)
        dim as integer count = any
        dim as dataqueue ptr current = any
        
        EnterCriticalSection(@gCrit)

        current = queuehead.next
        while (current <> @queuehead)
            dim as uinteger toread = current->info->sizebytes
            if (toread > 16384) then
                toread = 16384     /' For fun - Let's deprive the read of the whole block.  Only give 16kb at a time to make it re-ask for more later. '/
            end if
            
            for count = 0 to 50-1
                Sleep(10)
                if (gSleepBreak) then
                    printf(!"URGENT REQUEST - reading now!\n")
                    gSleepBreak = 0
                    exit for
                end if
            next
            
            fseek(current->info->handle, current->info->offset, SEEK_SET)           
            current->info->bytesread = fread(current->info->buffer, 1, toread, current->info->handle)
            
            if (current->info->bytesread < toread) then
                printf(!"FED        %5d bytes from offset %5d (* EOF)\n", current->info->bytesread, current->info->offset)
                current->info->result = FMOD_ERR_FILE_EOF
            else
                printf(!"FED        %5d bytes from offset %5d\n", current->info->bytesread, current->info->offset)
                current->info->result = FMOD_OK
            end if
                       
            queuehead.next = current->next
            free(current)
            current = queuehead.next
        wend

        LeaveCriticalSection(@gCrit)
    wend
    
    _endthreadex(0)
    
    return 0
end function


' *** program start
    dim as FMOD_SYSTEM     ptr fsystem = any
    dim as FMOD_SOUND      ptr sound = any
    dim as FMOD_CHANNEL    ptr channel = 0
    dim as FMOD_RESULT       result = any
    dim as string               key
    dim as uinteger      version = any
    dim as HANDLE            threadhandle = any

    InitializeCriticalSection(@gCrit)
    
	threadhandle = cast(handle,_beginthreadex(NULL, 0, @ProcessQueue, 0, 0, 0))
    if (threadhandle = 0) then
        printf(!"Failed to create file thread.\n")
end 0
    end if
    
    /'
        Create a System object and initialize.
    '/
    result = FMOD_System_Create(@fsystem)
    ERRCHECK(result)

    result = FMOD_System_GetVersion(fsystem, @version)
    ERRCHECK(result)

    if (version < FMOD_VERSION) then
        printf(!"Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION)
        end 0
    end if

    result = FMOD_System_Init(fsystem, 1, FMOD_INIT_NORMAL, 0)
    ERRCHECK(result)

    result = FMOD_System_SetStreamBufferSize(fsystem, 32768, FMOD_TIMEUNIT_RAWBYTES)
    ERRCHECK(result)
    
    result = FMOD_System_SetFileSystem(fsystem, @myopen, @myclose, @myread, @myseek, @myasyncread, @myasynccancel, 2048)
    ERRCHECK(result)

    printf(!"====================================================================\n")
    printf(!"Stream IO Example.  Copyright (c) Firelight Technologies 2004-2011.\n")
    printf(!"====================================================================\n")
    printf(!"\n")
    printf(!"\n")
    printf(!"====================== CALLING CREATESOUND ON MP3 =======================\n")
    
    result = FMOD_System_CreateStream(fsystem, "media/wave.mp3", FMOD_SOFTWARE or FMOD_LOOP_NORMAL or FMOD_2D or FMOD_IGNORETAGS, 0, @sound)
    ERRCHECK(result)

    printf(!"====================== CALLING PLAYSOUND ON MP3 =======================\n")

    result = FMOD_System_PlaySound(fsystem, FMOD_CHANNEL_FREE, sound, 0, @channel)
    ERRCHECK(result)

    /'
        Main loop.
    '/
    do
        if (sound) then
            dim as FMOD_OPENSTATE openstate = any
            dim as integer starving = any

            FMOD_Sound_GetOpenState(sound, @openstate, 0, @starving, 0)
            
            if (starving) then
                printf(!"Starving\n")
                result = FMOD_Channel_SetMute(channel, 1)
            else
                result = FMOD_Channel_SetMute(channel, 0)
                ERRCHECK(result)
            end if
        end if

       
            key = inkey
select case key
                case " " :
                    result = FMOD_Sound_Release(sound)
                    if (result = FMOD_OK) then
                        sound = 0
                        printf(!"Released sound.\n")
                    end if
end select

        FMOD_System_Update(fsystem)
        Sleep(10)

loop while (key <> chr(27))

    printf(!"\n")

    /'
        Shut down
    '/
    if (sound) then
        result = FMOD_Sound_Release(sound)
        ERRCHECK(result)
    end if
    result = FMOD_System_Close(fsystem)
    ERRCHECK(result)
    result = FMOD_System_Release(fsystem)
    ERRCHECK(result)

    gThreadQuit = 1   
