/*
 * KON - Kanji ON Linux Console - Copyright (C) 1992, 1993 Takashi MANABE
 * (manabe@tut.ac.jp)
 * 
 * KON 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.
 * 
 * KON 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., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * This code is based on selection.
 */

#include	"big5con.h"

struct mouseInfo mInfo;

int             mouseFd = -1;

#ifdef	HAS_MOUSE

static int      cFlag;
static int      headMask;
static int      headId;
static int      dataMask;
static int      pkMax;

typedef enum {
    MOUSE_MICROSOFT,
    MOUSE_MOUSESYSTEMS,
    MOUSE_BUSMOUSE,
    MOUSE_MMSERIES,
    MOUSE_LOGITECH,
    MOUSE_PS2,
    MOUSE_NONE
}               mtype;

static mtype    mouseType = MOUSE_NONE;

#define MAX_PK_SIZE	5

struct mouseconf {
    const char     *name;
    mtype           type;
    int             cFlag;
    int             headMask;
    int             headId;
    int             dataMask;
    int             pkMax;
}               mice[] = {
    {
	"Microsoft", MOUSE_MICROSOFT,
	(CS7 | CREAD | CLOCAL | HUPCL),
	0x40, 0x40, 0x40, 3
    },
    {
	"MouseSystems", MOUSE_MOUSESYSTEMS,
	(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL),
	0xf8, 0x80, 0x00, 5
    },
    {
	"BusMouse", MOUSE_BUSMOUSE,
	0,
	0xf8, 0x80, 0x00, 5
    },
    {
	"MmSeries", MOUSE_MMSERIES,
	(CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL),
	0xe0, 0x80, 0x80, 3
    },
    {
	"Logitech", MOUSE_LOGITECH,
	(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL),
	0xe0, 0x80, 0x80, 3
    },
    {
	"PS2", MOUSE_PS2,
	(CS8 | CREAD | CLOCAL | HUPCL),
	0xcc, 0x08, 0x00, 3
    },
    {
	"None", MOUSE_NONE,
	0,
	0, 0, 0, 0
    },
    {
	NULL, MOUSE_NONE,
	0,
	0, 0, 0, 0
    }
};

static int      mouseBaud;

static int 
ConfigMouseBaud(const char *config)
{
    int             baud;

    sscanf(config, "%d", &baud);

    switch (baud) {
    case 9600:
	mouseBaud = B9600;
	break;
    case 4800:
	mouseBaud = B4800;
	break;
    case 2400:
	mouseBaud = B2400;
	break;
    default:
	warn("invalid mouse baud rate %d; set to default (1200)\r\n", baud);
    case 1200:
	mouseBaud = B1200;
	break;
    }
    return SUCCESS;
}

static char    *mouseDev;

static int 
ConfigMouseDev(const char *config)
{
    char            name[MAX_COLS];
    sscanf(config, "%s", name);

    if (mouseDev)
	free(mouseDev);
    mouseDev = strdup(name);
    return SUCCESS;
}

static int      pasteButton;

static int 
Config3Buttons(const char *config)
{
    pasteButton = BoolConf(config) ? MOUSE_MID : MOUSE_RGT;
    return SUCCESS;
}

static int 
ConfigMouse(const char *config)
{
    struct mouseconf *p;
    char            name[MAX_COLS];

    mouseType = MOUSE_NONE;
    mInfo.has_mouse = FALSE;
    strncpy(name, config, MAX_COLS - 1);
    name[MAX_COLS - 1] = '\0';
    for (p = mice; p->name != NULL; p++) {
	if (strcasecmp(name, p->name) == 0) {
	    mouseType = p->type;
	    if (mouseType == MOUSE_NONE)
		return SUCCESS;
	    message("mouse type `%s'\r\n", name);
	    mInfo.has_mouse = TRUE;
	    cFlag = p->cFlag;
	    headMask = p->headMask;
	    headId = p->headId;
	    dataMask = p->dataMask;
	    pkMax = p->pkMax;

	    if (mouseType != MOUSE_BUSMOUSE) {
		DefineCap("MouseBaud", ConfigMouseBaud, "1200");
	    }
	    DefineCap("Mouse3Buttons", Config3Buttons, "Off");
	    DefineCap("MouseDev", ConfigMouseDev, "/dev/sysmouse");
	    return SUCCESS;
	}
    }
    warn("unknown mouse type `%s' ignored; assuming no mouse\r\n", name);
    return SUCCESS;
}

static
void 
MouseSetBaud(int mfd, u_short baud, u_short cflag)
{
    struct termios  mio;
    char           *cf;

    tcgetattr(mfd, &mio);

    mio.c_iflag = IGNBRK | IGNPAR;
    mio.c_oflag = 0;
    mio.c_lflag = 0;
#if defined(linux)
    mio.c_line = 0;
#endif
    mio.c_cc[VTIME] = 0;
    mio.c_cc[VMIN] = 1;

    mio.c_cflag = cflag;
    cfsetispeed(&mio, baud);
    cfsetospeed(&mio, baud);

    tcsetattr(mfd, TCSAFLUSH, &mio);

    switch (mouseBaud) {
    case B9600:
	cf = "*q";
	break;
    case B4800:
	cf = "*p";
	break;
    case B2400:
	cf = "*o";
	break;
    case B1200:
	cf = "*n";
	break;
    }

    mio.c_cflag = cflag;
    cfsetispeed(&mio, mouseBaud);
    cfsetospeed(&mio, mouseBaud);
    write(mfd, cf, 2);
    usleep(100000);
    tcsetattr(mfd, TCSAFLUSH, &mio);
}

void 
MouseInit(void)
{
    mInfo.has_mouse = TRUE;
    DefineCap("Mouse", ConfigMouse, "MouseSystems");
}

int 
MouseStart(void)
{
    int             mfd;

    if ((mfd = open(mouseDev, O_RDWR)) < 0) {
	warn("couldn't open mouse device; mouse disabled\n");
	Perror(mouseDev);
	free(mouseDev);
	mouseDev = NULL;
	mInfo.has_mouse = FALSE;
	return -1;
    }
    if (mouseType != MOUSE_BUSMOUSE) {
	MouseSetBaud(mfd, B9600, cFlag);
	MouseSetBaud(mfd, B4800, cFlag);
	MouseSetBaud(mfd, B2400, cFlag);
	MouseSetBaud(mfd, B1200, cFlag);

	if (mouseType == MOUSE_LOGITECH) {
	    write(mfd, "S", 1);
	    MouseSetBaud(mfd, mouseBaud, CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL);
	}
	write(mfd, "Q", 1);
    }
    mouseFd = mfd;
    return (mfd);
}

void 
MouseCleanup(void)
{
    close(mouseFd);
    mouseFd = -1;
}

static
void 
MouseAnalyzePacket(u_char * packet)
{
    static char     oldstat;
    static int      dx, dy;

    switch (mouseType) {
    case MOUSE_NONE:
	return;
    case MOUSE_MICROSOFT:
	mInfo.stat = ((packet[0] & 0x20) >> 3) | ((packet[0] & 0x10) >> 4);
	dx += (char)(((packet[0] & 0x03) << 6) | (packet[1] & 0x3F));
	dy += (char)(((packet[0] & 0x0C) << 4) | (packet[2] & 0x3F));
	break;
    case MOUSE_MOUSESYSTEMS:
	mInfo.stat = (~packet[0]) & 0x07;
	dx += (char)(packet[1]) + (char)(packet[3]);
	dy += -((char)(packet[2]) + (char)(packet[4]));
	break;
    case MOUSE_MMSERIES:
    case MOUSE_LOGITECH:
	mInfo.stat = packet[0] & 0x07;
	dx += (packet[0] & 0x10) ? packet[1] : -packet[1];
	dy += (packet[0] & 0x08) ? -packet[2] : packet[2];
	break;
    case MOUSE_PS2:
	mInfo.stat = ((packet[0] & 0x01) << 2) | ((packet[0] & 0x02) >> 1);
	dx += (char)(packet[1]);
	dy -= (char)(packet[2]);
	break;
    case MOUSE_BUSMOUSE:
	mInfo.stat = (~packet[0]) & 0x07;
	dx += (char)packet[1];
	dy += -(char)packet[2];
	break;
    }
    mInfo.dx = dx >> 3;
    dx -= mInfo.dx << 3;
    mInfo.dy = dy / dInfo.glineChar;
    dy -= mInfo.dy * dInfo.glineChar;

    mInfo.sw = MOUSE_LIFETIME;
    if (mInfo.dx || mInfo.dy) {
	mInfo.x += mInfo.dx;
	mInfo.y += mInfo.dy;

	if (mInfo.x < 0)
	    mInfo.x = 0;
	else if (mInfo.x > dInfo.txmax)
	    mInfo.x = dInfo.txmax;
	if (mInfo.y < 0)
	    mInfo.y = 0;
	else if (mInfo.y > dInfo.tymax)
	    mInfo.y = dInfo.tymax;
    }
    if (mInfo.stat & MOUSE_LFT) {
	if (!(oldstat & MOUSE_LFT)) {
	    mInfo.sx = mInfo.x;
	    mInfo.sy = mInfo.y;
	} else if (mInfo.dx || mInfo.dy) {
	    TextReverse(mInfo.sx, mInfo.sy, mInfo.x, mInfo.y);
	    TextRefresh();
	    TextReverse(mInfo.sx, mInfo.sy, mInfo.x, mInfo.y);
	}
    } else if (oldstat & MOUSE_LFT)
	TextCopy(mInfo.sx, mInfo.sy, mInfo.x, mInfo.y);

    if (mInfo.stat & pasteButton && !(oldstat & pasteButton))
	TextPaste();
    oldstat = mInfo.stat;
}

void 
MouseGetPacket(u_char * buff, int size)
{
    static u_char   packet[MAX_PK_SIZE];
    static          stat = 0;
    int             n;

    for (n = 0; n < size; n++, buff++) {
	if (!stat) {
	    if ((*buff & headMask) == headId) {
		packet[0] = *buff;
		stat = 1;
	    }
	    continue;
	}
	if (mouseType != MOUSE_PS2
	    && ((*buff & dataMask) || *buff == 0x80)) {
	    stat = 0;
	    continue;
	}
	packet[stat] = *buff;
	stat++;
	if (stat == pkMax) {
	    MouseAnalyzePacket(packet);
	    stat = 0;
	}
    }
}

#else				/* HAS_MOUSE */

/* Dummy routines. */

void 
MouseInit(void)
{
    mInfo.has_mouse = FALSE;
}

void 
MouseGetPacket(u_char * buff, int size)
{
}

int 
MouseStart(void)
{
    return -1;
}

void 
MouseCleanup(void)
{
}

#endif
