// Copyright (c) 2011 Peter Ohler. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for license details.

#ifndef OJ_READER_H
#define OJ_READER_H

#include "mem.h"

typedef struct _reader {
    char  base[0x00001000];
    char *head;
    char *end;
    char *tail;
    char *read_end; /* one past last character read */
    char *pro;      /* protection start, buffer can not slide past this point */
    char *str;      /* start of current string being read */
    long  pos;
    int   line;
    int   col;
    int   free_head;
    int (*read_func)(struct _reader *reader);
    union {
        int         fd;
        VALUE       io;
        const char *in_str;
    };
} *Reader;

extern void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s);
extern int  oj_reader_read(Reader reader);

static inline char reader_get(Reader reader) {
    // printf("*** drive get from '%s'  from start: %ld	buf: %p	 from read_end: %ld\n",
    // reader->tail, reader->tail - reader->head, reader->head, reader->read_end - reader->tail);
    if (reader->read_end <= reader->tail) {
        if (0 != oj_reader_read(reader)) {
            return '\0';
        }
    }
    if ('\n' == *reader->tail) {
        reader->line++;
        reader->col = 0;
    }
    reader->col++;
    reader->pos++;

    return *reader->tail++;
}

static inline void reader_backup(Reader reader) {
    reader->tail--;
    reader->col--;
    reader->pos--;
    if (0 >= reader->col) {
        reader->line--;
        // allow col to be negative since we never backup twice in a row
    }
}

static inline void reader_protect(Reader reader) {
    reader->pro = reader->tail;
    reader->str = reader->tail;  // can't have str before pro
}

static inline void reader_release(Reader reader) {
    reader->pro = 0;
}

/* Starts by reading a character so it is safe to use with an empty or
 * compacted buffer.
 */
static inline char reader_next_non_white(Reader reader) {
    char c;

    while ('\0' != (c = reader_get(reader))) {
        switch (c) {
        case ' ':
        case '\t':
        case '\f':
        case '\n':
        case '\r': break;
        default: return c;
        }
    }
    return '\0';
}

/* Starts by reading a character so it is safe to use with an empty or
 * compacted buffer.
 */
static inline char reader_next_white(Reader reader) {
    char c;

    while ('\0' != (c = reader_get(reader))) {
        switch (c) {
        case ' ':
        case '\t':
        case '\f':
        case '\n':
        case '\r':
        case '\0': return c;
        default: break;
        }
    }
    return '\0';
}

static inline int reader_expect(Reader reader, const char *s) {
    for (; '\0' != *s; s++) {
        if (reader_get(reader) != *s) {
            return -1;
        }
    }
    return 0;
}

static inline void reader_cleanup(Reader reader) {
    if (reader->free_head && 0 != reader->head) {
        OJ_R_FREE((char *)reader->head);
        reader->head      = 0;
        reader->free_head = 0;
    }
}

static inline int is_white(char c) {
    switch (c) {
    case ' ':
    case '\t':
    case '\f':
    case '\n':
    case '\r': return 1;
    default: break;
    }
    return 0;
}

#endif /* OJ_READER_H */
