#include "SDL.h"
#include <stdio.h>
#include "draw.h"
#include <unistd.h>

#define likely(x) __builtin_expect(x, 1)
#define unlikely(x) __builtin_expect(x, 0)

static int line;
static long *buf, *endbuf;
static int overrun;


SDL_Surface *xy_surface;

void xy_checkoverrun() {
    if(overrun) {
        fprintf(stderr, "Overrun by %d lines\n", overrun);
        overrun = 0;
    }
}

#define SHIFT xy_shift
#define FACTOR xy_factor
int xy_factor=25;
int xy_shift=210;

static long lastpixel;
static void xy_firstline(void) {
    line = 0;
    buf = xy_surface->pixels;
    endbuf = buf + xy_surface->w;
}

void xy_nextline(void) {
    long q = lastpixel & ~(255 << 8);
    while(buf < endbuf) *buf++ = q;
    long *x = xy_surface->pixels + xy_surface->pitch * line, *z = x+SHIFT;
    for(; z < endbuf ; x++, z++)
        *x = (*x & 0xff00ff) | (*(x+SHIFT) & 0xff00);
    for(; x < endbuf; x++) *x = *x & 0xff00ff;
    line++;
    if(unlikely(line >= xy_surface->h)) { line = 0; overrun++; }
    buf = xy_surface->pixels + xy_surface->pitch * line;
    endbuf = buf + xy_surface->w;
    buf += SHIFT;
}

static inline void _store_moveto(int x,int y) {
}

#define SCALE 4
#if SCALE==2
int xy_size = 511;
static inline void _store_pixel(int x,int y) {
    int i;
    long *b;
    long q = (y/2) + (255 << 8) + ((x/2) << 16);
    long q1 = ((y+1)/2) + (255 << 8) + (((x+1)/2) << 16);
    // long q = y + (0 << 8) + (x << 16);
    if(unlikely(buf + FACTOR >= endbuf)) {
        xy_nextline(); _store_moveto(x, y);
    }
    b = buf;
    for(i=0; i<FACTOR/2; i++) {
        *b++ = q;
        *b++ = q1;
    }
    buf = b;
    lastpixel = q;
}
#elif SCALE==3
int xy_size = 768;
static inline void _store_pixel(int x,int y) {
    int i;
    long *b;
    long q = (y/3) + (255 << 8) + ((x/3) << 16);
    long q1 = ((y+1)/3) + (255 << 8) + (((x+1)/3) << 16);
    long q2 = ((y+2)/3) + (255 << 8) + (((x+2)/3) << 16);
    // long q = y + (0 << 8) + (x << 16);
    if(unlikely(buf + FACTOR >= endbuf)) {
        xy_nextline(); _store_moveto(x, y);
    }
    b = buf;
    for(i=0; i<FACTOR/3; i++) {
        *b++ = q1;
        *b++ = q;
        *b++ = q2;
    }
    buf = b;
    lastpixel = q;
}
#elif SCALE==4
int xy_size = 1021;
static inline void _store_pixel(int x,int y) {
    int i;
    long *b;
    long q = (y/4) + (255 << 8) + ((x/4) << 16);
    long q1 = ((y+1)/4) + (255 << 8) + (((x+1)/4) << 16);
    long q2 = ((y+2)/4) + (255 << 8) + (((x+2)/4) << 16);
    long q3 = ((y+3)/4) + (255 << 8) + (((x+3)/4) << 16);
    // long q = y + (0 << 8) + (x << 16);
    if(unlikely(buf + FACTOR >= endbuf)) {
        xy_nextline(); _store_moveto(x, y);
    }
    b = buf;
    for(i=0; i<FACTOR/3; i++) {
        *b++ = q;
        *b++ = q2;
        *b++ = q1;
        *b++ = q3;
    }
    buf = b;
    lastpixel = q;
}
#endif

int xy_hasroom(int n) {
    return buf + n * FACTOR < endbuf;
}

void xy_moveto(int x, int y) { _store_moveto(x, y); }

void xy_draw_pixel(int x, int y) { _store_pixel(x, y); }

void xy_draw_line(int x0, int y0, int x1, int y1) {
    int dx = x1-x0, dy = y1-y0;
    int sx = dx > 0 ? 1 : -1;
    int sy = dy > 0 ? 1 : -1;

    dx = abs(dx);
    dy = abs(dy);

    _store_pixel(x0, y0);
    if(dx > dy) {
        if( sx > 0 ) {
            int counter, num = 0;
            for(counter=0; counter<dx; counter++) {
                x0 ++;
                num += dy; if(num > dx) { y0 += sy; num -= dx; }
                _store_pixel(x0, y0);
            }
        } else {
            int counter, num = 0;
            for(counter=0; counter<dx; counter++) {
                x0 --;
                num += dy; if(num > dx) { y0 += sy; num -= dx; }
                _store_pixel(x0, y0);
            }
        }
    } else {
        if(sy > 0) {
            int counter, num = 0;
            for(counter=0; counter<dy; counter++) {
                y0 ++;
                num += dx; if(num > dy) { x0 += sx; num -= dy; }
                _store_pixel(x0, y0);
            }
        } else {
            int counter, num = 0;
            for(counter=0; counter<dy; counter++) {
                y0 --;
                num += dx; if(num > dy) { x0 += sx; num -= dy; }
                _store_pixel(x0, y0);
            }
        }
    }
}

int xy_do_events = 1;

void xy_flipscreen(void) {
    SDL_Event event;
    SDL_Rect rect = {0, 0, xy_surface->w, xy_surface->h};

    SDL_Flip(xy_surface);
    SDL_FillRect(xy_surface, &rect, 0);
    xy_checkoverrun();
    xy_nextline();
    for(;line < xy_surface->h;line++) {
        long l = lrand48() & 0xff00ff;
        long *buf = xy_surface->pixels + xy_surface->pitch * line;
        long *endbuf = buf + xy_surface->w;
        for(;buf < endbuf; buf += 4) {
            buf[0] = buf[1] = buf[2] = buf[3] = lrand48() & 0xff00ff;
        }
    }
    xy_firstline();

    if(xy_do_events) {
    while(SDL_PollEvent(&event)) {
        switch(event.type) {
            case SDL_KEYDOWN:
                if( event.key.keysym.sym == SDLK_ESCAPE ) exit(0);
                if( event.key.keysym.sym == 'p' ) sleep(10);
                break;
            case SDL_QUIT:
                exit(0);
        }
    }
    }
}

#define SX 1920
#define SY 1200
void xy_init() {
    SDL_Init(SDL_INIT_VIDEO);
    xy_surface = \
        SDL_SetVideoMode(SX, SY, 32, SDL_FULLSCREEN | SDL_DOUBLEBUF);
    if(!xy_surface) abort();
    xy_flipscreen();
    atexit(SDL_Quit);
}
