#include <stdio.h>
#include <dos.h>
#include "graphics.h"

int screen_mode = 0x13;
int sx = 320, sy = 200;

char screen[320][200];
static char pscreen[320][200];

double circle_ratio;

void init_engine(char color, int draw) {
    int x, y;
    for(x = 0; x < sx; x ++) {
        for(y = 0; y < sy; y ++) {
            screen[x][y] = color;
            pscreen[x][y] = color;
            if(draw) wp(x, y, color);
        }
    }
}

void refresh_screen() {
    int x, y;

    /* wait until done with vertical retrace */
    while((inp(0x03da) & 0x08));
    /* wait until done refreshing */
    while(!(inp(0x03da) & 0x08));

    for(y = 0; y < sy; y ++) {
        for(x = 0; x < sx; x ++) {
            if(pscreen[x][y] != screen[x][y]) {
                wp(x, y, screen[x][y]);
                pscreen[x][y] = screen[x][y];
            }
        }
    }
}

void wp(int x, int y, int col) {
    union REGS r;

    /* don't display if out of range */
    if(x<0 || x>=sx || y<0 || y>=sy) return;

    r.h.ah = 12; /* set for write */
    r.h.al = col; /* color */
    r.x.cx = x; /* set column */
    r.x.dx = y; /* set row */

    int86(0x10, &r, &r); /* call BIOS */
}

void graphics_screen_mode() {
    union REGS r;

    r.h.al = screen_mode;
    r.h.ah = 0;
    int86(0x10, &r, &r);
}

void text_screen_mode() {
    union REGS r;

    r.h.al = 0x02;
    r.h.ah = 0;
    int86(0x10, &r, &r);
}

void require_repaint() {
    int x, y;
    for(x = 0; x < sx; x ++) {
        for(y = 0; y < sy; y ++) {
            pscreen[x][y] = -1;
        }
    }
}

void nonblack_repaint() {
    int x, y;

    graphics_screen_mode();
    
    for(x = 0; x < sx; x ++) {
        for(y = 0; y < sy; y ++) {
            if(screen[x][y]) pscreen[x][y] = -1;
        }
    }
}

void no(int x, int y, int garbage) {
    ps(x, y);
}

void ps(int x, int y) {
    if(x >= 0 && x < sx &&
       y >= 0 && y < sy) pscreen[x][y] = -1;
}

int sp(int x, int y) {
    if(x >= 0 && x < sx &&
       y >= 0 && y < sy) return (pscreen[x][y] = screen[x][y]);
}

void set(int x, int y, int s) {
    if(x >= 0 && x < sx &&
       y >= 0 && y < sy) screen[x][y] = s;
}

int get(int x, int y) {
    if(x >= 0 && x < sx &&
       y >= 0 && y < sy) return screen[x][y];
}

void box(int startx, int starty, int endx, int endy, int color, void (*func)()) {
    int x, y;

    for(x = startx; x < endx; x ++) {
        for(y = starty; y < endy; y ++) {
            func(x, y, color);
        }
    }
}

void line(int startx, int starty, int endx, int endy, int color, void (*func)()) {
    register int t, distance;
    int xerr=0, yerr=0, delta_x, delta_y;
    int incx, incy;

    /* compute the distances in both directions */
    delta_x=endx-startx;
    delta_y=endy-starty;

    /* Compute the direction of the increment,
       an increment of 0 means either a horizontal or vertical
       line.
    */
    if(delta_x>0) incx=1;
    else if(delta_x==0) incx=0;
    else incx=-1;

    if(delta_y>0) incy=1;
    else if(delta_y==0) incy=0;
    else incy=-1;

    /* determine which distance is greater */
    delta_x=abs(delta_x);
    delta_y=abs(delta_y);
    if(delta_x>delta_y) distance=delta_x;
    else distance=delta_y;

    /* draw the line */
    for(t=0; t<=distance+1; t++) {
        func(startx, starty, color);
        
        xerr+=delta_x;
        yerr+=delta_y;
        if(xerr>distance) {
            xerr-=distance;
            startx+=incx;
        }
        if(yerr>distance) {
            yerr-=distance;
            starty+=incy;
        }
    }
}

void circle(int x_center, int y_center, int radius, int color_code, void (*func)()) {
    register int x, y, delta;

    circle_ratio = 1.0;  /* for different aspect ratios, adjust this */

    y = radius;
    delta = 3 - 2 * radius;

    for(x=0; x<y; ) {
        plot_circle(x, y, x_center, y_center, color_code, func);

        if(delta < 0)
            delta += 4*x+6;
        else {
            delta += 4*(x-y)+10;
            y--;
        }
        x++;
    }

    x=y;
    if(y) plot_circle(x, y, x_center, y_center, color_code, func);
}

void plot_circle(int x, int y, int x_center, int y_center, int color_code, void (*func)()) {
    int startx, endx, x1, starty, endy, y1;

    starty = y*circle_ratio;
    endy = (y+1)*circle_ratio;
    startx = x*circle_ratio;
    endx = (x+1)*circle_ratio;

    for(x1=startx; x1<endx; ++x1) {
        func(x1+x_center, y+y_center, color_code);
        func(x1+x_center, y_center-y, color_code);
        func(x_center-x1, y_center-y, color_code);
        func(x_center-x1, y+y_center, color_code);
    }

    for(y1=starty; y1<endy; ++y1) {
        set(y1+x_center, x+y_center, color_code);
        set(y1+x_center, y_center-x, color_code);
        set(x_center-y1, y_center-x, color_code);
        set(x_center-y1, x+y_center, color_code);
    }
}

void fill_circle(int x, int y, int r, int c, void (*func)()) {
    while(r) {
        circle(x, y, r, c, func);
        r--;
        if(r) {
            circle(x+1, y, r, c, func);
            circle(x, y+1, r, c, func);
            circle(x-1, y, r, c, func);
            circle(x, y-1, r, c, func);
        }
    }
}

