#include <u.h>
#include <libc.h>
#include <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
{
       int     type;   /* 'p' for pointer, 'P' for Posn */
       int     nalloc;
       int     nused;
       union{
               void*   listp;
               void**  voidp;
               Posn*   posnp;
               String**stringp;
               File**  filep;
       }g;
};

#define listptr         g.listp
#define voidpptr        g.voidp
#define posnptr         g.posnp
#define stringpptr      g.stringp
#define filepptr        g.filep

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;
       vlong           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);
void            raspflush(File*);

/*
* 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    stretchsel(File*, Posn, int);
void    dprint(char*, ...);
void    edit(File*, int);
void    *emalloc(ulong);
void    *erealloc(void*, ulong);
void    error(Err);
void    error_c(Err, int);
void    error_r(Err, char*);
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, ...);
Address lineaddr(Posn, Address, int);
List    *listalloc(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);
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**);
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    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);
char    *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 Buffer   cmdbuf;
extern int      cmdbufpos;
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      outbuffered;

#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, vlong);
void    outflush(void);
int needoutflush(void);

Posn    nlcount(File *f, Posn p0, Posn p1);