/* > c.buffer
    (c) D.R.McAuley 1987
*/

#include <arthur.h>
#include <stdio.h>
#include "tty.h"

static reg_set rs;
reg_set *regptr = &rs;

#define interupts_off() swix(OS_IntOff, regptr)
#define interupts_on() swix(OS_IntOn, regptr)

#define ctrlS 19
#define ctrlQ 20

#define BUFFSIZE 0x4000
#define BUFFLWM  0x0400
#define BUFFHWM  0x3C00

/* #define OUTPUTTOO */
#define XONXOFF

#ifdef OUTPUTTOO
static char rsbufout[BUFFSIZE];
static char *headout = rsbufinn;
static char *tailout = rsbufinn;
static int  countout = 0;
#endif

static char rsbufinn[BUFFSIZE];
static char *headinn = rsbufinn;
static char *tailinn = rsbufinn;
static int  countinn = 0;


static char kbbufinn[BUFFSIZE];
static char *kbheadinn = kbbufinn;
static char *kbtailinn = kbbufinn;
static int  kbcountinn = 0;

static struct {
        unsigned count:8;
        unsigned on:1;
        unsigned run:1;
} x;

void (*keypoller)();


/* code */

static f_ptr tmp;
f_ptr define_keypoller(void (*proc)())
{
    tmp = keypoller;
    keypoller = proc;
    return tmp;
}

void set_xon_xoff(int state) { 
        x.count = 0;
        x.on = state;
        x.run = state;
}

int rsgetcount (void) { return(0); }
int kbgetcount (void) { return(0); }

#ifdef OUTPUTTOO
int _rsinsertout(char ch)
{       int wasdead = (countout == 0);
        if (countout == BUFFSIZE) return (1);
        if (ch != 0) {
            (countout)++; *headout = ch;
            if (++(headout) == (rsbufout + BUFFSIZE)) headout = rsbufout;
        }
        if (wasdead) inter();
        return (0);
}

int _rsremoveout(int examine) 
{       char ch;
        if (countout == 0) return(256);
        ch = *tailout;
        if (!examine) {
            countout--;
            if (++(tailout) == (rsbufout + BUFFSIZE)) tailout = rsbufout;
        }
        return (ch);
}

int _rscountnpout(int purge, int countt)
{       if (purge) {
            countout = 0;
            headout = tailout = rsbufout;
            return (0);
        }
        if (countt) return(BUFFSIZE-countout); else return(countout);
}
#endif

int _rsinsertinn(char ch)
{
        if (countinn == BUFFSIZE) return (1);
        if (ch != 0) {
            (countinn)++; *headinn = ch;
            if (++(headinn) == (rsbufinn + BUFFSIZE)) headinn = rsbufinn;
        }
        return (0);
}

int _rsremoveinn(int examine) 
{       char ch;
        if (countinn == 0) return(256);
        ch = *tailinn;
        if (!examine) {
            countinn--;
            if (++(tailinn) == (rsbufinn + BUFFSIZE)) tailinn = rsbufinn;
        }
        return (ch);
}

int _rscountnpinn(int purge, int countt)
{       if (purge) {
            countinn = 0;
            headinn = tailinn = rsbufinn;
            return (0);
        }
        if (countt) return(BUFFSIZE-countinn); else return(countinn);
}


static int kbcalled = 0;
int _kbinsertinn(char ch)
{
kbcalled++;
        if (kbcountinn == BUFFSIZE) return (1);
        if (ch != 0) {
            (kbcountinn)++; *kbheadinn = ch;
            if (++(kbheadinn) == (kbbufinn + BUFFSIZE)) kbheadinn = kbbufinn;
        }
        return (0);
}

int _kbremoveinn(int examine) 
{       char ch;
        if (kbcountinn == 0) return(256);
        ch = *kbtailinn;
        if (!examine) {
            kbcountinn--;
            if (++(kbtailinn) == (kbbufinn + BUFFSIZE)) kbtailinn = kbbufinn;
        }
        return (ch);
}

int _kbcountnpinn(int purge, int countt)
{       if (purge) {
            kbcountinn = 0;
            kbheadinn = kbtailinn = kbbufinn;
            return (0);
        }
        if (countt) return(BUFFSIZE-kbcountinn); else return(kbcountinn);
}


void rsinsert(int ch, int b)
{       interupts_off();
        if (b == 1) _rsinsertinn((char) ch);
#ifdef OUTPUTTOO
        else _rsinsertout((char) ch);
#endif
        interupts_on();
}

int rsremove(int b)
{       int ch = 0;
        if (b == 1 ) {
            if (countinn != 0) {
                interupts_off();
                ch = _rsremoveinn(FALSE);
                interupts_on();
            }
#ifdef OUTPUTTOO
        } else {
            if (countout != 0) {
                interupts_off();
                ch = _rsremoveout(FALSE);
                interupts_on();
            }
#endif
        }
        return(ch);
}

void rspurge(int b)
{       interupts_off();
        if (b == 1) _rscountnpinn(TRUE, 0);
#ifdef OUTPUTTOO
        else _rscountnpout(TRUE, 0);
#endif
        interupts_on();
}

int rscount(int spaces, int b)
{       int cnt;
        interupts_off();
        if (b ==1 ) cnt = _rscountnpinn(FALSE, spaces);
#ifdef OUTPUTTOO
        else cnt = _rscountnpout(FALSE, spaces);
#endif
        interupts_on();
        return(cnt);
}


void kbinsert(int ch, int b)
{       interupts_off();
        if (b == 0) _kbinsertinn((char) ch);
        interupts_on();
}

int kbremove(int b)
{       int ch = 0;
        if (b == 0 ) {
            if (kbcountinn != 0) {
                interupts_off();
                ch = _kbremoveinn(FALSE);
                interupts_on();
            }
        }
        return(ch);
}

void kbpurge(int b)
{       interupts_off();
        if (b == 0) _kbcountnpinn(TRUE, 0);
        interupts_on();
}

int kbcount(int spaces, int b)
{       int cnt;
        interupts_off();
        if (b == 0 ) cnt = _kbcountnpinn(FALSE, spaces);
        interupts_on();
        return(cnt);
}


int rsgetch(void)
{       int ch;
        int loop = 0;
        x.count++;
        while (TRUE) {
#ifdef XONXOFF
            if (x.count > 16) {
                if ((countinn >= BUFFHWM) && x.run && x.on) {
                    sendchar(ctrlS);
                    x.run = FALSE;
                } else if ((countinn <= BUFFLWM) && (!x.run) && x.on) {
                    sendchar(ctrlQ);
                    x.run = TRUE;
                }
                x.count = 0;
                (*keypoller)();
            }
#endif
            
            if ((ch = pollch()) != NOCHAR) return(ch);

            if (loop++ == 16) { loop = 0; (*keypoller)(); }
        }
}

int pollch(void) { return(rsremove(1) & 0x7F); }
/*
int pollch(void)
{       mosbyte2(145, 1);
        return(regs.r[2] & 0x7F);
}
*/

#ifdef OUTPUTTOO
void sendchar(int ch) { rsinsert(ch, 2); }
#else
void sendchar(int ch) { mosbyte3(138, 2, ch); }
#endif

void inter(void) { mosbyte3(156, 0x20, 0x9F); } /* Enable interupts poo */
