#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "audio.h"
#include "audio_internal.h"

typedef struct _SourceFileContext {
    AudioBuffer *buffer;
    FILE *fp;
} SourceFileContext;

AudioPipelineStatus source_file_start(AudioPipelineElement *self) {
    SourceFileContext *context = (SourceFileContext *)self->ctx;
    
    while(!feof(context->fp)) {
        // fprintf(stderr, "INFO: File offset %d, reading %d\n", ftell(context->fp), context->buffer->buf_size);
        size_t bytes = fread(context->buffer->data, 1, context->buffer->buf_size, context->fp);
        
        if (bytes > 0) {
            uint32_t buf_size = context->buffer->buf_size;
            context->buffer->buf_size = (uint32_t)bytes;
            
            AudioPipelineStatus result = self->next->push(self->next, context->buffer);
            if ((result != PipelineRunning) && (result != PipelineBuffering)) {
                self->pipeline->status = result;
                if (self->pipeline->statusCallback != NULL) {
                    self->pipeline->statusCallback(self->pipeline, result);
                }
                return result;
            }
            context->buffer->buf_size = buf_size;
        }
    }
    
    return PipelineFinished;
}

AudioPipelineStatus source_file_reset(AudioPipelineElement *self) {
    SourceFileContext *context = (SourceFileContext *)self->ctx;
    fseek(context->fp, 0, SEEK_SET);
    
    return PipelineStopped;
}

char *source_file_describe(AudioPipelineElement *self) {
    return "file source";
}

void source_file_destroy(AudioPipelineElement *self) {
    SourceFileContext *context = (SourceFileContext *)self->ctx;
    free_audio_buffer(context->buffer);
    fclose(context->fp);
    free(self->ctx);
    free(self);
}

AudioPipelineElement *audio_source_file(char *filename, uint32_t block_size) {
    AudioPipelineElement *self = calloc(1, sizeof(AudioPipelineElement));
    if (self == NULL) {
        return NULL;
    }

    SourceFileContext *context = calloc(1, sizeof(SourceFileContext));
    if (context == NULL) {
        free(self);
        return NULL;
    }
        
    context->buffer = alloc_audio_buffer(block_size);
    if (context->buffer == NULL) {
        fprintf(stderr, "ERROR: Not enough memory for buffer of size %d!\n", block_size);
        free(context);
        free(self);
        return NULL;
    }
    
    context->fp = fopen(filename, "rb");
    if (context->fp == NULL) {
        fprintf(stderr, "ERROR: Could not open file %s\n", filename);
        free_audio_buffer(context->buffer);
        free(context);
        free(self);
        return NULL;
    }
    
    self->bits_per_sample = 0;
    self->channels = 0;
    self->sample_rate = 0;
    
    self->ctx = context;
    self->describe = source_file_describe;
    self->start = source_file_start;
    self->reset = source_file_reset;
    self->stop = source_stop_nop;
    self->push = NULL;
    self->link = NULL;
    self->destroy = source_file_destroy;
    self->type = AudioElementSource;
    
    return self;
}