#define USE_OPENAL
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#ifdef USE_OPENAL
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#endif
#include "ibm.h"
#include "sound.h"

FILE *allog;
#ifdef USE_OPENAL
ALuint buffers[4];       // front and back buffers
ALuint buffers_cd[4];    // front and back buffers
static ALuint source[2]; // audio source
#endif
#define FREQ 48000

int sound_buf_len_al = 48000 / 20;

void closeal();
ALvoid alutInit(ALint *argc, ALbyte **argv) {
        ALCcontext *Context;
        ALCdevice *Device;

        // Open device
        // 	Device=alcOpenDevice((ALubyte*)"");
        Device = alcOpenDevice((void *)"");
        // Create context(s)
        Context = alcCreateContext(Device, NULL);
        // Set active context
        alcMakeContextCurrent(Context);
        // Register extensions
}

ALvoid alutExit(ALvoid) {
        ALCcontext *Context;
        ALCdevice *Device;

        // Unregister extensions

        // Get active context
        Context = alcGetCurrentContext();
        // Get device for active context
        Device = alcGetContextsDevice(Context);
        // Disable context
        alcMakeContextCurrent(NULL);
        // Release context(s)
        alcDestroyContext(Context);
        // Close device
        alcCloseDevice(Device);
}
void initalmain(int argc, char *argv[]) {
#ifdef USE_OPENAL
        alutInit(0, 0);
        //        printf("AlutInit\n");
        atexit(closeal);
//        printf("AlutInit\n");
#endif
}

void closeal() {
#ifdef USE_OPENAL
        alutExit();
#endif
}

void check() {
#ifdef USE_OPENAL
        ALenum error;
        if ((error = alGetError()) != AL_NO_ERROR) {
                //                printf("Error : %08X\n", error);
                //                exit(-1);
        }
#endif
}

void inital() {
#ifdef USE_OPENAL
        int c;
        int16_t buf[MAXSOUNDBUFLEN * 2];
        int16_t cd_buf[CD_BUFLEN * 2];

        assert(sound_buf_len_al <= MAXSOUNDBUFLEN);

        //        printf("1\n");
        check();

        //        printf("2\n");
        alGenBuffers(4, buffers);
        check();
        alGenBuffers(4, buffers_cd);
        check();

        //        printf("3\n");
        alGenSources(2, source);
        check();

        //        printf("4\n");
        alSource3f(source[0], AL_POSITION, 0.0, 0.0, 0.0);
        alSource3f(source[0], AL_VELOCITY, 0.0, 0.0, 0.0);
        alSource3f(source[0], AL_DIRECTION, 0.0, 0.0, 0.0);
        alSourcef(source[0], AL_ROLLOFF_FACTOR, 0.0);
        alSourcei(source[0], AL_SOURCE_RELATIVE, AL_TRUE);
        check();
        alSource3f(source[1], AL_POSITION, 0.0, 0.0, 0.0);
        alSource3f(source[1], AL_VELOCITY, 0.0, 0.0, 0.0);
        alSource3f(source[1], AL_DIRECTION, 0.0, 0.0, 0.0);
        alSourcef(source[1], AL_ROLLOFF_FACTOR, 0.0);
        alSourcei(source[1], AL_SOURCE_RELATIVE, AL_TRUE);
        check();

        memset(buf, 0, sound_buf_len_al * 4);
        memset(cd_buf, 0, CD_BUFLEN * 4);

        //        printf("5\n");
        for (c = 0; c < 4; c++) {
                alBufferData(buffers[c], AL_FORMAT_STEREO16, buf, sound_buf_len_al * 2 * 2, FREQ);
                alBufferData(buffers_cd[c], AL_FORMAT_STEREO16, cd_buf, CD_BUFLEN * 2 * 2, CD_FREQ);
        }

        alSourceQueueBuffers(source[0], 4, buffers);
        check();
        alSourceQueueBuffers(source[1], 4, buffers_cd);
        check();
        //        printf("6 %08X\n",source);
        alSourcePlay(source[0]);
        check();
        alSourcePlay(source[1]);
        check();
//        printf("InitAL!!! %08X\n",source);
#endif
}

void givealbuffer(int32_t *buf) {
#ifdef USE_OPENAL
        int16_t buf16[MAXSOUNDBUFLEN * 2];
        int processed;
        int state;

        assert(sound_buf_len_al <= MAXSOUNDBUFLEN);

        // return;

        //        printf("Start\n");
        check();

        //        printf("GiveALBuffer %08X\n",source);

        alGetSourcei(source[0], AL_SOURCE_STATE, &state);

        check();

        if (state == 0x1014) {
                alSourcePlay(source[0]);
                //                printf("Resetting sound\n");
        }
        //        printf("State - %i %08X\n",state,state);
        alGetSourcei(source[0], AL_BUFFERS_PROCESSED, &processed);

        //        printf("P ");
        check();
        //        printf("Processed - %i\n",processed);

        if (processed >= 1) {
                int c;
                ALuint buffer;
                double gain = pow(10.0, (double)sound_gain / 20.0);

                alListenerf(AL_GAIN, gain);

                alSourceUnqueueBuffers(source[0], 1, &buffer);
                //                printf("U ");
                check();

                for (c = 0; c < sound_buf_len_al * 2; c++) {
                        if (buf[c] < -32768)
                                buf16[c] = -32768;
                        else if (buf[c] > 32767)
                                buf16[c] = 32767;
                        else
                                buf16[c] = buf[c];
                }
                //                for (c=0;c<sound_buf_len_al*2;c++) buf[c]^=0x8000;
                alBufferData(buffer, AL_FORMAT_STEREO16, buf16, sound_buf_len_al * 2 * 2, FREQ);
                //                printf("B ");
                check();

                alSourceQueueBuffers(source[0], 1, &buffer);
                //                printf("Q ");
                check();

                //                printf("\n");

                //                if (!allog) allog=fopen("al.pcm","wb");
                //                fwrite(buf,sound_buf_len_al*2,1,allog);
        }
//        printf("\n");
#endif
}

void givealbuffer_cd(int16_t *buf) {
#ifdef USE_OPENAL
        int processed;
        int state;

        // return;

        //        printf("Start\n");
        check();

        //        printf("GiveALBuffer %08X\n",source);

        alGetSourcei(source[1], AL_SOURCE_STATE, &state);

        check();

        if (state == 0x1014) {
                alSourcePlay(source[1]);
                //                printf("Resetting sound\n");
        }
        //        printf("State - %i %08X\n",state,state);
        alGetSourcei(source[1], AL_BUFFERS_PROCESSED, &processed);

        //        printf("P ");
        check();
        //        printf("Processed - %i\n",processed);

        if (processed >= 1) {
                ALuint buffer;
                double gain = pow(10.0, (double)sound_gain / 20.0);

                alListenerf(AL_GAIN, gain);

                alSourceUnqueueBuffers(source[1], 1, &buffer);
                //                printf("U ");
                check();

                //                for (c=0;c<sound_buf_len_al*2;c++) buf[c]^=0x8000;
                alBufferData(buffer, AL_FORMAT_STEREO16, buf, CD_BUFLEN * 2 * 2, CD_FREQ);
                //                printf("B ");
                check();

                alSourceQueueBuffers(source[1], 1, &buffer);
                //                printf("Q ");
                check();

                //                printf("\n");

                //                if (!allog) allog=fopen("al.pcm","wb");
                //                fwrite(buf,sound_buf_len_al*2,1,allog);
        }
//        printf("\n");
#endif
}
