/*
 * Graphic Library for Wonder Witch
 *   Argolithm (c) 1994 ZOBplus
 */

#include <stdlib.h>
#include <sys/bios.h>
#include <sys/libwwc.h>
#include <graph.h>
#include <sys/text.h>

static g_mode_t graphic_mode;
static g_font_t gbuffer[GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT];
static char work[16];

static WORD g_palette4[] = {
  0x0fff, 0x000f, 0x0f00, 0x00f0
};

int
g_init(int dispno_, int color_)
{
  WORD c[GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT] ;
  WORD d_status ; 
  int i ;

  /* HW check */
  if (wwc_get_hardarch() == HARDARCH_WSC) {
    if (color_ != COLOR_MODE_GRAYSCALE &&
	color_ != COLOR_MODE_4COLOR) {
      return GRAPH_ERR;
    }
    wwc_set_color_mode(color_);
    wwc_clear_font();
    if (color_ == COLOR_MODE_4COLOR) {
      for (i = 0 ; i < 4 ; i++) {
        wwc_palette_set_color(0, i, g_palette4[i]);
      }
    }
  }
  d_status = display_status() ;
  if (dispno_ == SCREEN2) {
    d_status |= DCM_SCR2 ;
    d_status &= ~DCM_SCR1 ;
  } else {
    d_status |= DCM_SCR1 ;
    d_status &= ~DCM_SCR2 ;
  }
  display_control(d_status) ;

  graphic_mode = GRAPH_DIRECT;

  g_clear() ;

  for (i = 0 ; i < GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT ; i++) {
    c[i] = i ;
  }
  screen_set_char(dispno_, 0, 0, GRAPH_CHAR_WIDTH, GRAPH_CHAR_HEIGHT, c) ;

  return GRAPH_OK;
}

void
g_clear(void)
{
  memset(gbuffer, 0, 
         GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT * sizeof(g_font_t));
  if (graphic_mode == GRAPH_DIRECT) {
    font_set_colordata(0, GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT, gbuffer);
  }
}

void
g_flush(void)
{
  if (graphic_mode == GRAPH_BUFFERED) {
    font_set_colordata(0, GRAPH_CHAR_WIDTH * GRAPH_CHAR_HEIGHT, gbuffer);
  }
}


g_mode_t
g_setmode(g_mode_t mode_)
{
  return graphic_mode = mode_;
}

g_mode_t
g_getmode(void)
{
  return graphic_mode;
}

void
g_plot(int x_, int y_, int color_)
{
  WORD *p;
  int           i;

  if (x_ < 0 || x_ >= 224 || y_ < 0 || y_ >= 144) {
    return;
  }
  i = (y_ / GRAPH_CHAR_UNIT) * GRAPH_CHAR_WIDTH + x_ / GRAPH_CHAR_UNIT;
  p = &(gbuffer[i][y_ & (GRAPH_CHAR_UNIT - 1)]);

  x_ &= GRAPH_CHAR_UNIT - 1;
  if (color_ & 2) {
    *p |= 0x8000 >> x_;
  } else {
    *p &= ~(0x8000 >> x_);
  }
  if (color_ & 1) {
    *p |= 0x80 >> x_;
  } else {
    *p &= ~(0x80 >> x_);
  }
  if (graphic_mode == GRAPH_DIRECT) {
    font_set_colordata(i, 1, gbuffer[i]);
  }
}

static void
g_plot8(int x_, int y_, int color_)
{
  WORD *p;
  int           i;

  if (x_ < 0 || x_ >= 224 || y_ < 0 || y_ >= 144) {
    return;
  }
  i = (y_ / GRAPH_CHAR_UNIT) * GRAPH_CHAR_WIDTH + x_ / GRAPH_CHAR_UNIT;
  p = &(gbuffer[i][y_ & (GRAPH_CHAR_UNIT - 1)]);
  if (color_ & 2) {
    *p |= 0xff00;
  } else {
    *p &= 0x00ff;
  }
  if (color_ & 1) {
    *p |= 0x00ff;
  } else {
    *p &= 0xff00;
  }
  if (graphic_mode == GRAPH_DIRECT) {
    font_set_colordata(i, 1, gbuffer[i]);
  }
}

#define GRAPH_ABS(n) (((n) < 0) ? -(n) : (n))

void
g_line(int x_, int y_, int dx_, int dy_, int color_)
{
  int sx, sy;
  int delta;
  int i;

  sx  = (dx_ < 0) ? -1 : 1;
  sy  = (dy_ < 0) ? -1 : 1;
  dx_ = GRAPH_ABS(dx_);
  dy_ = GRAPH_ABS(dy_);

  if (dx_ < dy_) {
    delta = dy_;
    for (i = 0 ; i < dy_ ; i++) {
      g_plot(x_, y_, color_);
      delta -= dx_;
      y_ += sy;
      if (delta <= 0) {
	delta += dy_;
	x_ += sx;
      }
    }
  } else {
    if (dy_ == 0) {
      for (i = 0 ; i < dx_ ; ) {
        if (x_ % GRAPH_CHAR_UNIT || dx_ - i < GRAPH_CHAR_UNIT) {
          g_plot(x_, y_, color_);
          x_ += sx;
          ++i;
        } else {
          g_plot8(x_, y_, color_);
          x_ += sx * GRAPH_CHAR_UNIT;
          i += GRAPH_CHAR_UNIT;
        }
      }
    } else {
      delta = dx_;
      for (i = 0 ; i < dx_ ; i++) {
        g_plot(x_, y_, color_);
        delta -= dy_;
        x_ += sx;
        if (delta <= 0) {
	  delta += dx_;
	  y_ += sy;
        }
      }
    }
  }
}

void
g_box(int x_, int y_, int w_, int h_, int color_)
{
  g_line(x_, y_, w_, 0, color_);
  g_line(x_, y_, 0, h_, color_);
  g_line(x_, y_ + h_ - 1, w_, 0, color_);
  g_line(x_ + w_ - 1, y_, 0, h_, color_);
}

void
g_box_fill(int x_, int y_, int w_, int h_, int color_)
{
  int dx, dy;
  dx = (w_ < 0) ? -1 : 1;
  dy = (h_ < 0) ? -1 : 1;
  if (GRAPH_ABS(w_) > GRAPH_ABS(h_)) {
    while (h_) {
      g_line(x_, y_, w_, 0, color_);
      y_ += dy;
      h_ -= dy;
    }
  } else {
    while (w_) {
      g_line(x_, y_, 0, h_, color_);
      x_ += dx;
      w_ -= dx;
    }
  }
}

void
g_circle(int x_, int y_, int r_, int color_)
{
  int dx, dy;
  dx = r_; dy = 0;
  while (dx >= dy) {
    g_plot(x_ + dx, y_ + dy, color_);
    g_plot(x_ + dy, y_ + dx, color_);
    g_plot(x_ - dx, y_ + dy, color_);
    g_plot(x_ - dy, y_ + dx, color_);
    g_plot(x_ - dx, y_ - dy, color_);
    g_plot(x_ - dy, y_ - dx, color_);
    g_plot(x_ + dx, y_ - dy, color_);
    g_plot(x_ + dy, y_ - dx, color_);
    r_ -= (dy << 1) - 1; ++dy;
    if (r_ < 0) {
      r_ += (dx << 1); --dx;
    }
  }
}

void
g_circle_fill(int x_, int y_, int r_, int color_)
{
  int dx, dy;
  dx = r_; dy = 0;
  while (dx >= dy) {
    g_line(x_ - dx, y_ + dy, 2 * dx, 0, color_);
    g_line(x_ - dy, y_ + dx, 2 * dy, 0, color_);
    g_line(x_ - dx, y_ - dy, 2 * dx, 0, color_);
    g_line(x_ - dy, y_ - dx, 2 * dy, 0, color_);
    r_ -= (dy << 1) - 1; ++dy;
    if (r_ < 0) {
      r_ += (dx << 1); --dx;
    }
  }
}

void
g_oval(int x_, int y_, int rx_, int ry_, int color_)
{
  int dx, dy;
  int xx, yy;
  int ddx, ddy;
  int dd;

  if (rx_ > ry_) {
    dx = rx_; dy = 0; xx = 0; yy = ry_; ddx = ddy = dd = rx_;
    while (dx >= dy) {
      g_plot(x_ + dx, y_ + xx, color_);
      g_plot(x_ + dy, y_ + yy, color_);
      g_plot(x_ - dx, y_ + xx, color_);
      g_plot(x_ - dy, y_ + yy, color_);
      g_plot(x_ - dx, y_ - xx, color_);
      g_plot(x_ - dy, y_ - yy, color_);
      g_plot(x_ + dx, y_ - xx, color_);
      g_plot(x_ + dy, y_ - yy, color_);
      dd -= (dy << 1) - 1; ++dy;
      ddx -= ry_;
      if (ddx < 0) {
	ddx += rx_; ++xx;
      }
      if (dd < 0) {
	dd += (dx << 1); --dx;
	ddy -= ry_;
	if (ddy < 0) {
	  ddy += rx_; --yy;
	}
      }
    }
  } else {
    dx = 0; dy = ry_; xx = rx_; yy = 0; ddx = ddy = dd = ry_;
    while (dx <= dy) {
      g_plot(x_ + xx, y_ + dx, color_);
      g_plot(x_ + yy, y_ + dy, color_);
      g_plot(x_ - xx, y_ + dx, color_);
      g_plot(x_ - yy, y_ + dy, color_);
      g_plot(x_ - xx, y_ - dx, color_);
      g_plot(x_ - yy, y_ - dy, color_);
      g_plot(x_ + xx, y_ - dx, color_);
      g_plot(x_ + yy, y_ - dy, color_);
      dd -= (dx << 1) - 1; ++dx;
      ddy -= rx_;
      if (ddy < 0) {
	ddy += ry_; ++yy;
      }
      if (dd < 0) {
	dd += (dy << 1); --dy;
	ddx -= rx_;
	if (ddx < 0) {
	  ddx += ry_; --xx;
	}
      }
    }
  }
}

void
g_oval_fill(int x_, int y_, int rx_, int ry_, int color_)
{
  int dx, dy;
  int xx, yy;
  int ddx, ddy;
  int dd;

  if (rx_ > ry_) {
    dx = rx_; dy = 0; xx = 0; yy = ry_; ddx = ddy = dd = rx_;
    while (dx >= dy) {
      g_line(x_ - dx, y_ + xx, 2 * dx, 0, color_);
      g_line(x_ - dy, y_ + yy, 2 * dy, 0, color_);
      g_line(x_ - dx, y_ - xx, 2 * dx, 0, color_);
      g_line(x_ - dy, y_ - yy, 2 * dy, 0, color_);
      dd -= (dy << 1) - 1; ++dy;
      ddx -= ry_;
      if (ddx < 0) {
	ddx += rx_; ++xx;
      }
      if (dd < 0) {
	dd += (dx << 1); --dx;
	ddy -= ry_;
	if (ddy < 0) {
	  ddy += rx_; --yy;
	}
      }
    }
  } else {
    dx = 0; dy = ry_; xx = rx_; yy = 0; ddx = ddy = dd = ry_;
    while (dx <= dy) {
      g_line(x_ - xx, y_ + dx, 2 * xx, 0, color_);
      g_line(x_ - yy, y_ + dy, 2 * yy, 0, color_);
      g_line(x_ - xx, y_ - dx, 2 * xx, 0, color_);
      g_line(x_ - yy, y_ - dy, 2 * yy, 0, color_);
      dd -= (dx << 1) - 1; ++dx;
      ddy -= rx_;
      if (ddy < 0) {
	ddy += ry_; ++yy;
      }
      if (dd < 0) {
	dd += (dy << 1); --dy;
	ddx -= rx_;
	if (ddx < 0) {
	  ddx += ry_; --xx;
	}
      }
    }
  }
}

int
g_put_char(int cx_, int cy_, int color_, int ch_)
{
  t_font_t     font;
  g_font_t    *buf;
  int          i, j;
  WORD p;

  if (cx_ < 0 || cx_ >= GRAPH_CHAR_WIDTH ||
      cy_ < 0 || cy_ >= GRAPH_CHAR_HEIGHT) {
    return GRAPH_OK;
  }
  text_get_fontdata(ch_, font);
  /* masking */
  i = cx_ + cy_ * GRAPH_CHAR_WIDTH;
  buf = &gbuffer[i];
  for (j = 0 ; j < GRAPH_CHAR_UNIT ; j++) {
    p = ~font[j] | ((WORD)(~font[j]) << 8);
    (*buf)[j] &= p;
    p = 0;
    if (color_ & 1) {
      (*buf)[j] |= font[j];
    } else {
      (*buf)[j] &= ~font[j];
    }
    if (color_ & 2) {
      (*buf)[j] |= (WORD)font[j] << 8;
    } else {
      (*buf)[j] &= ~((WORD)font[j] << 8);
    }
  }
  if (graphic_mode == GRAPH_DIRECT) {
    font_set_colordata(i, 1, gbuffer[i]);
  }
  return GRAPH_OK;
}

int
g_put_string(int cx_, int cy_, int color_, char far *str_)
{
  while (*str_) {
    g_put_char(cx_++, cy_, color_, *str_++);
    if (cx_ >= GRAPH_CHAR_WIDTH) {
      cx_ = 0;
      ++cy_;
    }
  }
  return GRAPH_OK;
}

int
g_put_stringn(int cx_, int cy_, int color_, char far *str_, int n_)
{
  if (n_ <= 0 || str_ == 0) return GRAPH_OK;
  while (*str_ && n_--) {
    g_put_char(cx_++, cy_, color_, *str_++);
    if (cx_ >= GRAPH_CHAR_WIDTH) {
      cx_ = 0;
      ++cy_;
    }
  }
  return GRAPH_OK;
}

#define g_text_store_numeric(buf_, len_, format_, num_)\
  servicewww(INT_TEXT, TEXT_PUT_NUMERIC,\
             (int)(buf_), MK_WORD((len_), (format_)|NUM_STORE), (num_))

int
g_put_numeric(int cx_, int cy_, int color_, int len_, int format_, int n_)
{
  if (format_ >= 16) {
    return GRAPH_ERR;
  }
  g_text_store_numeric(work, len_, format_, n_);
  return g_put_string(cx_, cy_, color_, work);
}

void
g_solid_line(int x_, int y_, int dx_, int dy_, int color_, int width_)
{
  int sx, sy;
  int delta;
  int i;

  sx  = (dx_ < 0) ? -1 : 1;
  sy  = (dy_ < 0) ? -1 : 1;
  dx_ = GRAPH_ABS(dx_);
  dy_ = GRAPH_ABS(dy_);

  if (dx_ < dy_) {
    delta = dy_;
    for (i = 0 ; i < dy_ ; i++) {
      g_box_fill(x_, y_, width_, width_, color_);
      delta -= dx_;
      y_ += sy;
      if (delta <= 0) {
	delta += dy_;
	x_ += sx;
      }
    }
  } else {
    delta = dx_;
    for (i = 0 ; i < dx_ ; i++) {
      g_box_fill(x_, y_, width_, width_, color_);
      delta -= dy_;
      x_ += sx;
      if (delta <= 0) {
	delta += dx_;
	y_ += sy;
      }
    }
  }
}

void
g_save_buffer(int x_, int y_, int w_, int h_, g_font_t far *buf_)
{
  int by;

  for (by = 0 ; by < h_ ; by++) {
    memcpy(buf_[by * w_],
	   gbuffer[x_ + (y_ + by) * GRAPH_CHAR_WIDTH],
	   sizeof(g_font_t) * w_);
  }
}

void
g_restore_buffer(int x_, int y_, int w_, int h_, g_font_t far *buf_)
{
  int by;

  for (by = 0 ; by < h_ ; by++) {
    memcpy(gbuffer[x_ + (y_ + by) * GRAPH_CHAR_WIDTH],
           buf_[by * w_],
	   sizeof(g_font_t) * w_);
  }
}
