/*
 * Helpers for streaming over buffer data to avoid having to read it all at once.
 */

// TOP

#if !defined(FCODER_STREAMING_H)
#define FCODER_STREAMING_H

#include "4coder_helper/4coder_helper.h"

struct Stream_Chunk{
    Application_Links *app;
    Buffer_Summary *buffer;
    
    char *base_data;
    int32_t start, end;
    int32_t min_start, max_end;
    bool32 add_null;
    uint32_t data_size;
    
    char *data;
};

static int32_t
round_down(int32_t x, int32_t b){
    int32_t r = 0;
    if (x >= 0){
        r = x - (x % b);
    }
    return(r);
}

static int32_t
round_up(int32_t x, int32_t b){
    int32_t r = 0;
    if (x >= 0){
        r = x - (x % b) + b;
    }
    return(r);
}

static bool32
init_stream_chunk(Stream_Chunk *chunk, Application_Links *app, Buffer_Summary *buffer,
                  int32_t pos, char *data, uint32_t size){
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    if (pos >= 0 && pos < buffer->size && size > 0){
        chunk->app = app;
        chunk->buffer = buffer;
        chunk->base_data = data;
        chunk->data_size = size;
        chunk->start = round_down(pos, size);
        chunk->end = round_up(pos, size);
        
        if (chunk->max_end > buffer->size || chunk->max_end == 0){
            chunk->max_end = buffer->size;
        }
        
        if (chunk->max_end && chunk->max_end < chunk->end){
            chunk->end = chunk->max_end;
        }
        if (chunk->min_start && chunk->min_start > chunk->start){
            chunk->start = chunk->min_start;
        }
        
        if (chunk->start < chunk->end){
            buffer_read_range(app, buffer, chunk->start, chunk->end, chunk->base_data);
            chunk->data = chunk->base_data - chunk->start;
            result = 1;
        }
    }
    
    return(result);
}

static bool32
forward_stream_chunk(Stream_Chunk *chunk){
    Application_Links *app = chunk->app;
    Buffer_Summary *buffer = chunk->buffer;
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    if (chunk->end < buffer->size){
        chunk->start = chunk->end;
        chunk->end += chunk->data_size;
        
        if (chunk->max_end && chunk->max_end < chunk->end){
            chunk->end = chunk->max_end;
        }
        if (chunk->min_start && chunk->min_start > chunk->start){
            chunk->start = chunk->min_start;
        }
        
        if (chunk->start < chunk->end){
            buffer_read_range(app, buffer, chunk->start, chunk->end, chunk->base_data);
            chunk->data = chunk->base_data - chunk->start;
            result = 1;
        }
    }
    
    else if (chunk->add_null && chunk->end + 1 < buffer->size){
        chunk->start = buffer->size;
        chunk->end = buffer->size + 1;
        chunk->base_data[0] = 0;
        chunk->data = chunk->base_data - chunk->start;
        result = 1;
    }
    
    return(result);
}

static bool32
backward_stream_chunk(Stream_Chunk *chunk){
    Application_Links *app = chunk->app;
    Buffer_Summary *buffer = chunk->buffer;
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    if (chunk->start > 0){
        chunk->end = chunk->start;
        chunk->start -= chunk->data_size;
        
        if (chunk->max_end && chunk->max_end < chunk->end){
            chunk->end = chunk->max_end;
        }
        if (chunk->min_start && chunk->min_start > chunk->start){
            chunk->start = chunk->min_start;
        }
        
        if (chunk->start < chunk->end){
            buffer_read_range(app, buffer, chunk->start, chunk->end, chunk->base_data);
            chunk->data = chunk->base_data - chunk->start;
            result = 1;
        }
    }
    
    else if (chunk->add_null && chunk->start > -1){
        chunk->start = -1;
        chunk->end = 0;
        chunk->base_data[0] = 0;
        chunk->data = chunk->base_data - chunk->start;
        result = 1;
    }
    
    return(result);
}

struct Stream_Tokens{
    Application_Links *app;
    Buffer_Summary *buffer;
    
    Cpp_Token *base_tokens;
    Cpp_Token *tokens;
    int32_t start, end;
    int32_t count, token_count;
};

static bool32
init_stream_tokens(Stream_Tokens *stream, Application_Links *app, Buffer_Summary *buffer, int32_t pos, Cpp_Token *data, int32_t count){
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    
    int32_t token_count = buffer_token_count(app, buffer);
    if (buffer->tokens_are_ready && pos >= 0 && pos < token_count && count > 0){
        stream->app = app;
        stream->buffer = buffer;
        stream->base_tokens = data;
        stream->count = count;
        stream->start = round_down(pos, count);
        stream->end = round_up(pos, count);
        stream->token_count = token_count;
        
        if (stream->start < 0){
            stream->start = 0;
        }
        if (stream->end > stream->token_count){
            stream->end = stream->token_count;
        }
        
        buffer_read_tokens(app, buffer, stream->start, stream->end, stream->base_tokens);
        stream->tokens = stream->base_tokens - stream->start;
        result = 1;
    }
    
    return(result);
}

static Stream_Tokens
begin_temp_stream_token(Stream_Tokens *stream){
    return(*stream);
}

static void
end_temp_stream_token(Stream_Tokens *stream, Stream_Tokens temp){
    if (stream->start != temp.start || stream->end != temp.end){
        Application_Links *app = stream->app;
        buffer_read_tokens(app, temp.buffer, temp.start, temp.end, temp.base_tokens);
        stream->tokens = stream->base_tokens - temp.start;
        stream->start = temp.start;
        stream->end = temp.end;
    }
}

static bool32
forward_stream_tokens(Stream_Tokens *stream){
    Application_Links *app = stream->app;
    Buffer_Summary *buffer = stream->buffer;
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    if (stream->end < stream->token_count){
        stream->start = stream->end;
        stream->end += stream->count;
        
        if (stream->end > stream->token_count){
            stream->end = stream->token_count;
        }
        
        if (stream->start < stream->end){
            buffer_read_tokens(app, buffer, stream->start, stream->end, stream->base_tokens);
            stream->tokens = stream->base_tokens - stream->start;
            result = 1;
        }
    }
    
    return(result);
}

static bool32
backward_stream_tokens(Stream_Tokens *stream){
    Application_Links *app = stream->app;
    Buffer_Summary *buffer = stream->buffer;
    bool32 result = 0;
    
    refresh_buffer(app, buffer);
    if (stream->start > 0){
        stream->end = stream->start;
        stream->start -= stream->count;
        
        if (0 > stream->start){
            stream->start = 0;
        }
        
        if (stream->start < stream->end){
            buffer_read_tokens(app, buffer, stream->start, stream->end, stream->base_tokens);
            stream->tokens = stream->base_tokens - stream->start;
            result = 1;
        }
    }
    
    return(result);
}

#endif

// BOTTOM