#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"

#define Image   IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

static Memimage* back;
static Memimage *conscol;

static Point curpos;
static Rectangle window;
static int *xp;
static int xbuf[256];
static Lock vgascreenlock;
int drawdebug;

void
vgaimageinit(ulong chan)
{
       if(back == nil){
               back = allocmemimage(Rect(0,0,1,1), chan);      /* RSC BUG */
               if(back == nil)
                       panic("back alloc");            /* RSC BUG */
               back->flags |= Frepl;
               back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
               memfillcolor(back, DBlack);
       }

       if(conscol == nil){
               conscol = allocmemimage(Rect(0,0,1,1), chan);   /* RSC BUG */
               if(conscol == nil)
                       panic("conscol alloc"); /* RSC BUG */
               conscol->flags |= Frepl;
               conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
               memfillcolor(conscol, DWhite);
       }
}

static void
vgascroll(VGAscr* scr)
{
       int h, o;
       Point p;
       Rectangle r;

       h = scr->memdefont->height;
       o = 8*h;
       r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
       p = Pt(window.min.x, window.min.y+o);
       memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p);
       r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
       memimagedraw(scr->gscreen, r, back, ZP, nil, ZP);

       curpos.y -= o;
}

static void
vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
{
       Point p;
       int h, w, pos;
       Rectangle r;

//      drawdebug = 1;
       if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
               xp = xbuf;

       h = scr->memdefont->height;
       switch(buf[0]){

       case '\n':
               if(curpos.y+h >= window.max.y){
                       vgascroll(scr);
                       *flushr = window;
               }
               curpos.y += h;
               vgascreenputc(scr, "\r", flushr);
               break;

       case '\r':
               xp = xbuf;
               curpos.x = window.min.x;
               break;

       case '\t':
               p = memsubfontwidth(scr->memdefont, " ");
               w = p.x;
               if(curpos.x >= window.max.x-4*w)
                       vgascreenputc(scr, "\n", flushr);

               pos = (curpos.x-window.min.x)/w;
               pos = 4-(pos%4);
               *xp++ = curpos.x;
               r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
               memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
               combinerect(flushr, r);
               curpos.x += pos*w;
               break;

       case '\b':
               if(xp <= xbuf)
                       break;
               xp--;
               r = Rect(*xp, curpos.y, curpos.x, curpos.y+h);
               memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
               combinerect(flushr, r);
               curpos.x = *xp;
               break;

       case '\0':
               break;

       default:
               p = memsubfontwidth(scr->memdefont, buf);
               w = p.x;

               if(curpos.x >= window.max.x-w)
                       vgascreenputc(scr, "\n", flushr);

               *xp++ = curpos.x;
               r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
               memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min);
               memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf);
               combinerect(flushr, r);
               curpos.x += w;
       }
//      drawdebug = 0;
}

static void
vgascreenputs(char* s, int n)
{
       int i;
       Rune r;
       char buf[4];
       VGAscr *scr;
       Rectangle flushr;

       scr = &vgascreen[0];

       if(!islo()){
               /*
                * Don't deadlock trying to
                * print in an interrupt.
                */
               if(!canlock(&vgascreenlock))
                       return;
       }
       else
               lock(&vgascreenlock);

       flushr = Rect(10000, 10000, -10000, -10000);

       while(n > 0){
               i = chartorune(&r, s);
               if(i == 0){
                       s++;
                       --n;
                       continue;
               }
               memmove(buf, s, i);
               buf[i] = 0;
               n -= i;
               s += i;
               vgascreenputc(scr, buf, &flushr);
       }
       flushmemscreen(flushr);

       unlock(&vgascreenlock);
}

void
vgascreenwin(VGAscr* scr)
{
       int h, w;

       h = scr->memdefont->height;
       w = scr->memdefont->info[' '].width;

       window.min = Pt(48, 48);
       window.max = addpt(window.min, Pt(10+w*80, 10+h*50));
       if(window.max.y >= scr->gscreen->r.max.y)
               window.max.y = scr->gscreen->r.max.y-1;
       if(window.max.x >= scr->gscreen->r.max.x)
               window.max.x = scr->gscreen->r.max.x-1;
       window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
       curpos = window.min;

       screenputs = vgascreenputs;
}

/*
* Supposedly this is the way to turn DPMS
* monitors off using just the VGA registers.
* Unfortunately, it seems to mess up the video mode
* on the cards I've tried.
*/
void
vgablank(VGAscr*, int blank)
{
       uchar seq1, crtc17;

       if(blank) {
               seq1 = 0x00;
               crtc17 = 0x80;
       } else {
               seq1 = 0x20;
               crtc17 = 0x00;
       }

       outs(Seqx, 0x0100);                     /* synchronous reset */
       seq1 |= vgaxi(Seqx, 1) & ~0x20;
       vgaxo(Seqx, 1, seq1);
       crtc17 |= vgaxi(Crtx, 0x17) & ~0x80;
       delay(10);
       vgaxo(Crtx, 0x17, crtc17);
       outs(Crtx, 0x0300);                             /* end synchronous reset */
}