#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/thread.h>
#include <9pm/plumb.h>
#include "errors.h"

/*
 * BLOCKSIZE is relatively small to keep memory consumption down.
 */

#define	BLOCKSIZE	2048
#define	RUNESIZE	sizeof(Rune)
#define	NDISC		5
#define	NBUFFILES	3+2*NDISC	/* plan 9+undo+snarf+NDISC*(transcript+buf) */
#define NSUBEXP	10

#define	TRUE		1
#define	FALSE		0

#define	INFINITY	0x7FFFFFFFL
#define	INCR		25
#define	STRSIZE		(2*BLOCKSIZE)

typedef long		Posn;		/* file position or address */
typedef	ushort		Mod;		/* modification number */

typedef struct Address	Address;
typedef struct Block	Block;
typedef struct Buffer	Buffer;
typedef struct Disk	Disk;
typedef struct Discdesc	Discdesc;
typedef struct File	File;
typedef struct List	List;
typedef struct Range	Range;
typedef struct Rangeset	Rangeset;
typedef struct String	String;

enum State
{
	Clean =		' ',
	Dirty =		'\'',
	Unread =	'-',
};

struct Range
{
	Posn	p1, p2;
};

struct Rangeset
{
	Range	p[NSUBEXP];
};

struct Address
{
	Range	r;
	File	*f;
};

struct String
{
	short	n;
	short	size;
	Rune	*s;
};

struct List	/* code depends on a long being able to hold a pointer */
{
	int	nalloc;
	int	nused;
	union{
		void	*listp;
		Block	*blkp;
		long	*longp;
		uchar*	*ucharp;
		String*	*stringp;
		File*	*filep;
		long	listv;
	}g;
};

#define	listptr		g.listp
#define	blkptr		g.blkp
#define	longptr		g.longp
#define	ucharpptr	g.ucharp
#define	stringpptr	g.stringp
#define	filepptr	g.filep
#define	listval		g.listv

enum
{
	Blockincr =	256,
	Maxblock = 	8*1024,

	BUFSIZE = Maxblock,	/* size from fbufalloc() */
	RBUFSIZE = BUFSIZE/sizeof(Rune),
};


enum
{
	Null		= '-',
	Delete		= 'd',
	Insert		= 'i',
	Filename	= 'f',
	Dot		= 'D',
	Mark		= 'm',
};

struct Block
{
	uint		addr;	/* disk address in bytes */
	union
	{
		uint	n;	/* number of used runes in block */
		Block	*next;	/* pointer to next in free list */
	};
};

struct Disk
{
	int		fd;
	uint		addr;	/* length of temp file */
	Block		*free[Maxblock/Blockincr+1];
};

Disk*		diskinit(void);
Block*		disknewblock(Disk*, uint);
void		diskrelease(Disk*, Block*);
void		diskread(Disk*, Block*, Rune*, uint);
void		diskwrite(Disk*, Block**, Rune*, uint);

struct Buffer
{
	uint		nc;
	Rune		*c;	/* cache */
	uint		cnc;	/* bytes in cache */
	uint		cmax;	/* size of allocated cache */
	uint		cq;	/* position of cache */
	int		cdirty;	/* cache needs to be written */
	uint		cbi;	/* index of cache Block */
	Block		**bl;	/* array of blocks */
	uint		nbl;	/* number of blocks */
};
void		bufinsert(Buffer*, uint, Rune*, uint);
void		bufdelete(Buffer*, uint, uint);
uint		bufload(Buffer*, uint, int, int*);
void		bufread(Buffer*, uint, Rune*, uint);
void		bufclose(Buffer*);
void		bufreset(Buffer*);

struct File
{
	Buffer;				/* the data */
	Buffer		delta;		/* transcript of changes */
	Buffer		epsilon;	/* inversion of delta for redo */
	String		name;		/* name of associated file */
	uvlong		qidpath;	/* of file when read */
	uint		mtime;		/* of file when read */
	int		dev;		/* of file when read */
	int		unread;		/* file has not been read from disk */

	long		seq;		/* if seq==0, File acts like Buffer */
	long		cleanseq;	/* f->seq at last read/write of file */
	int		mod;		/* file appears modified in menu */
	char		rescuing;	/* sam exiting; this file unusable */

//	Text		*curtext;	/* most recently used associated text */
//	Text		**text;		/* list of associated texts */
//	int		ntext;
//	int		dumpid;		/* used in dumping zeroxed windows */

	Posn		hiposn;		/* highest address touched this Mod */
	Address		dot;		/* current position */
	Address		ndot;		/* new current position after update */
	Range		tdot;		/* what terminal thinks is current range */
	Range		mark;		/* tagged spot in text (don't confuse with Mark) */
	List		*rasp;		/* map of what terminal's got */
	short		tag;		/* for communicating with terminal */
	char		closeok;	/* ok to close file? */
	char		deleted;	/* delete at completion of command */
	Range		prevdot;	/* state before start of change */
	Range		prevmark;
	long		prevseq;
	int		prevmod;
};
//File*		fileaddtext(File*, Text*);
void		fileclose(File*);
void		filedelete(File*, uint, uint);
//void		filedeltext(File*, Text*);
void		fileinsert(File*, uint, Rune*, uint);
uint		fileload(File*, uint, int, int*);
void		filemark(File*);
void		filereset(File*);
void		filesetname(File*, String*);
void		fileundelete(File*, Buffer*, uint, uint);
void		fileuninsert(File*, Buffer*, uint, uint);
void		fileunsetname(File*, Buffer*);
void		fileundo(File*, int, int, uint*, uint*, int);
int		fileupdate(File*, int, int);

int		filereadc(File*, uint);
File		*fileopen(void);
void		loginsert(File*, uint, Rune*, uint);
void		logdelete(File*, uint, uint);
void		logsetname(File*, String*);
int		fileisdirty(File*);
long		undoseq(File*, int);
long		prevseq(Buffer*);

void		raspload(File*);
void		raspstart(File*);
void		raspdelete(File*, uint, uint, int);
void		raspinsert(File*, uint, Rune*, uint, int);
void		raspdone(File*, int);

/*
 * acme fns
 */
void*	fbufalloc(void);
void	fbuffree(void*);
uint	min(uint, uint);
void	cvttorunes(char*, int, Rune*, int*, int*, int*);

#define	runemalloc(a)		(Rune*)emalloc((a)*sizeof(Rune))
#define	runerealloc(a, b)	(Rune*)realloc((a), (b)*sizeof(Rune))
#define	runemove(a, b, c)	memmove((a), (b), (c)*sizeof(Rune))

int	alnum(int);
int	Read(int, void*, int);
void	Seek(int, long, int);
int	plan9(File*, int, String*, int);
int	Write(int, void*, int);
int	bexecute(File*, Posn);
void	cd(String*);
void	closefiles(File*, String*);
void	closeio(Posn);
void	cmdloop(void);
void	cmdupdate(void);
void	compile(String*);
void	copy(File*, Address);
File	*current(File*);
void	delete(File*);
void	delfile(File*);
void	dellist(List*, int);
void	doubleclick(File*, Posn);
void	dprint(char*, ...);
void	edit(File*, int);
void	*emalloc(ulong);
void	*erealloc(void*, ulong);
void	error(Err);
void	error_c(Err, int);
void	error_s(Err, char*);
int	execute(File*, Posn, Posn);
int	filematch(File*, String*);
void	filename(File*);
void	fixname(String*);
void	fullname(String*);
void	getcurwd(void);
File	*getfile(String*);
int	getname(File*, String*, int);
long	getnum(int);
void	hiccough(char*);
void	inslist(List*, int, long);
Address	lineaddr(Posn, Address, int);
void	listfree(List*);
void	load(File*);
File	*lookfile(String*);
void	lookorigin(File*, Posn, Posn);
int	lookup(int);
void	move(File*, Address);
void	moveto(File*, Range);
File	*newfile(void);
void	nextmatch(File*, String*, Posn, int);
int	newtmp(int);
void	notifyf(void*, char*);
void	panic(char*);
void	printposn(File*, int);
void	print_ss(char*, String*, String*);
void	print_s(char*, String*);
int	rcv(void);
Range	rdata(List*, Posn, Posn);
Posn	readio(File*, int*, int, int);
void	rescue(void);
void	resetcmd(void);
void	resetsys(void);
void	resetxec(void);
void	rgrow(List*, Posn, Posn);
void	samerr(char*);
void	settempfile(void);
int	skipbl(void);
void	snarf(File*, Posn, Posn, Buffer*, int);
void	sortname(File*);
void	startup(char*, int, char**, char **end);
void	state(File*, int);
int	statfd(int, ulong*, uvlong*, long*, long*, long*);
int	statfile(char*, ulong*, uvlong*, long*, long*, long*);
void	Straddc(String*, int);
void	Strclose(String*);
int	Strcmp(String*, String*);
void	Strdelete(String*, Posn, Posn);
void	Strdupl(String*, Rune*);
void	Strduplstr(String*, String*);
void	Strinit(String*);
void	Strinit0(String*);
void	Strinsert(String*, String*, Posn);
void	Strinsure(String*, ulong);
int	Strispre(String*, String*);
void	Strzero(String*);
int	Strlen(Rune*);
char	*Strtoc(String*);
void	syserror(char*);
void	sysinit(void);
void	telldot(File*);
void	tellpat(void);
String	*tmpcstr(char*);
String	*tmprstr(Rune*, int);
void	freetmpstr(String*);
void	termcommand(void);
void	termwrite(char*);
File	*tofile(String*);
void	trytoclose(File*);
void	trytoquit(void);
int	undo(int);
void	update(void);
int	waitfor(int);
void	warn(Warn);
void	warn_s(Warn, char*);
void	warn_SS(Warn, String*, String*);
void	warn_S(Warn, String*);
int	whichmenu(File*);
void	writef(File*);
Posn	writeio(File*);
Discdesc *Dstart(void);

extern Rune	samname[];	/* compiler dependent */
extern Rune	*left[];
extern Rune	*right[];

extern char	RSAM[];		/* system dependent */
extern char	SAMTERM[];
extern char	HOME[];
extern char	TMPDIR[];
extern char	SH[];
extern char	SHPATH[];
extern char	RX[];
extern char	RXPATH[];
extern char	SAMSAVECMD[];

/*
 * acme globals
 */
extern long		seq;
extern Disk		*disk;

extern char	*rsamname;	/* globals */
extern char	*samterm;
extern Rune	genbuf[];
extern char	*genc;
extern int	io;
extern int	patset;
extern int	quitok;
extern Address	addr;
extern Buffer	snarfbuf;
extern Buffer	plan9buf;
extern List	file;
extern List	tempfile;
extern File	*cmd;
extern File	*curfile;
extern File	*lastfile;
extern Mod	modnum;
extern Posn	cmdpt;
extern Posn	cmdptadv;
extern Rangeset	sel;
extern String	curwd;
extern String	cmdstr;
extern String	genstr;
extern String	lastpat;
extern String	lastregexp;
extern String	plan9cmd;
extern int	downloaded;
extern int	eof;
extern int	bpipeok;
extern int	panicking;
extern Rune	empty[];
extern int	termlocked;
extern int	noflush;

#include "mesg.h"

void	outTs(Hmesg, int);
void	outT0(Hmesg);
void	outTl(Hmesg, long);
void	outTslS(Hmesg, int, long, String*);
void	outTS(Hmesg, String*);
void	outTsS(Hmesg, int, String*);
void	outTsllS(Hmesg, int, long, long, String*);
void	outTsll(Hmesg, int, long, long);
void	outTsl(Hmesg, int, long);
void	outTsv(Hmesg, int, long);
void	outstart(Hmesg);
void	outcopy(int, void*);
void	outshort(int);
void	outlong(long);
void	outvlong(void*);
void	outsend(void);
void	outflush(void);
