/*
 * UPIWIN - Micro Pi Windowing Framework Kernel
 * Copyright (C) 2019 Amy Bowersox/Erbosoft Metaverse Design Solutions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include <alloca.h>
#include "wintype.h"
#include "log.h"
#include "fbinit.h"
#include "fontengine.h"
#include "fbprimitive.h"

inline static PUINT16 loc_from_coords(INT32 x, INT32 y)
{
  return Fb_Ptr + (y * Fb_Info->width) + x;
}

UINT16 Fb_setpixel(INT32 x, INT32 y, UINT16 color, BOOL xor)
{
  UINT16 rc;
  PUINT16 loc = loc_from_coords(x, y);

  rc = *loc;
  if (xor)
    *loc ^= color;
  else
    *loc = color;
  return rc;
}

void Fb_line(INT32 x1, INT32 y1, INT32 x2, INT32 y2, UINT16 color, BOOL xor)
{
  INT32 tmp;
  INT32 dx = x2 - x1;
  INT32 dy = y2 - y1;

  /* uses Bresenham's line algorithm with fixed-point arithmetic */
  if (ABS(dx) < ABS(dy))
  {
    if (y1 > y2)
    {
      tmp = x1;
      x1 = x2;
      x2 = tmp;
      tmp = y1;
      y1 = y2;
      y2 = tmp;
      dx = -dx;
      dy = -dy;
    }
    x1 <<= 16;
    dx = (dx << 16) / dy;
    while (y1 <= y2)
    {
      Fb_setpixel(x1 >> 16, y1, color, xor);
      x1 += dx;
      y1++;
    }
  }
  else
  {
    if (x1 > x2)
    {
      tmp = x1;
      x1 = x2;
      x2 = tmp;
      tmp = y1;
      y1 = y2;
      y2 = tmp;
      dx = -dx;
      dy = -dy;
    }
    y1 <<= 16;
    dy = dx ? (dy << 16) / dx : 0;
    while (x1 <= x2)
    {
      Fb_setpixel(x1, y1 >> 16, color, xor);
      y1 += dy;
      x1++;
    }
  }
}

void Fb_rectangle(INT32 x1, INT32 y1, INT32 x2, INT32 y2, UINT16 color, BOOL xor)
{
  Fb_line(x1, y1, x2, y1, color, xor);
  Fb_line(x2, y1 + 1, x2, y2 - 1, color, xor);
  Fb_line(x1, y2, x2, y2, color, xor);
  Fb_line(x1, y1 + 1, x1, y2 - 1, color, xor);
}

void Fb_filled_rectangle(INT32 x1, INT32 y1, INT32 x2, INT32 y2, UINT16 color, BOOL xor)
{
  INT32 tmp;
  PUINT16 ps, p;

  if (x1 > x2)
  {
    tmp = x1;
    x1 = x2;
    x2 = tmp;
  }
  if (y1 > y2)
  {
    tmp = y1;
    y1 = y2;
    y2 = tmp;
  }

  ps = loc_from_coords(x1, y1);
  for (; y1 <= y2; y1++)
  {
    p = ps;
    for (tmp = x1; tmp <= x2; tmp++)
    {
      if (xor)
        *p++ ^= color;
      else
        *p++ = color;
    }
    ps += Fb_Info->width;
  }
}

static void internal_textout(INT32 x, INT32 y, INT32 width, INT32 height, PBYTE gsbits)
{
  INT32 i, j;
  BYTE b;
  PUINT16 dptr, lbuf;

  if ((width == 0) && (height == 0))
    return; /* must be drawing a space */
  if (((x + width) >= Fb_Info->width) || ((y + height) >= Fb_Info->height))
    return;
  dptr = loc_from_coords(x, y);
  lbuf = (PUINT16)alloca(width * sizeof(UINT16));
  for (i=0; i<height; i++)
  {
    for (j=0; j<width; j++)
    {
      b = *gsbits++;
      lbuf[j] = ((UINT16)(b >> 3) & 0x1F) << 11;
      lbuf[j] |= ((UINT16)(b >> 2) & 0x3F) << 5;
      lbuf[j] |= ((UINT16)(b >> 3) & 0x1F);
    }

    memcpy(dptr, lbuf, width * sizeof(UINT16));
    dptr += Fb_Info->width;
  }
}

void Fb_textout(INT32 x, INT32 y, PCSTR pstr)
{
  FontEng_do_text_out(x, y, pstr, internal_textout);
}
