#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include "dat.h"
#include "fns.h"

Pal *
newpal(char *f)
{
       Pal *p;

       p = emalloc(sizeof(*p));
       p->type = PAL;
       p->sel = -1;
       filinit(p, f);
       return p;
}

void
putpal(Pal *p)
{
       int i;

       for(i = 0; i < p->ncol; i++)
               freeimage(p->ims[i]);
       free(p->cols);
       free(p->ims);
}

int
readpal(Pal *p, Biobuf *bp)
{
       char *s, *sp;
       char *args[8];
       int nc, i, c;

       s = nil;
       if(tline(bp, &s, args, nelem(args)) != 2)
               goto err;
       if(strcmp(args[0], "pal") != 0)
               goto err;
       nc = strtol(args[1], &sp, 0);
       if(*sp != 0 || nc < 0)
               goto err;
       free(s);
       s = nil;
       p->ncol = nc;
       p->cols = emalloc(nc * sizeof(*p->cols));
       p->ims = emalloc(nc * sizeof(*p->ims));
       for(i = 0; i < nc; i++){
               if(tline(bp, &s, args, nelem(args)) != 1)
                       goto err;
               c = strtol(args[0], &sp, 0);
               if(*sp != 0 || c < 0 || c > 0xffffff)
                       goto err;
               p->cols[i] = c;
               free(s);
               s = nil;
       }
       for(i = 0; i < nc; i++)
               p->ims[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, p->cols[i] << 8 | 0xff);
       p->id = getident(bp->fid);
       return 0;
err:
       if(s != nil)
               free(s);
       werrstr("invalid format");
       return -1;
}

int
writepal(Pal *p, char *f)
{
       Biobuf *bp;
       int i, rc, n;

       if(f == nil)
               f = p->name;
       bp = Bopen(f, OWRITE);
       if(bp == nil){
               cmdprint("?%r\n");
               return -1;
       }
       n = 0;
       rc = Bprint(bp, "pal %d\n", p->ncol);
       if(rc < 0) goto err;
       n += rc;
       for(i = 0; i < p->ncol; i++){
               rc = Bprint(bp, "%#.6x\n", p->cols[i]);
               if(rc < 0) goto err;
               n += rc;
       }
       if(Bterm(bp) < 0){
               cmdprint("?%r\n");
               return -1;
       }
       p->change = 0;
       cmdprint("%s: #%d\n", f, n);
       return 0;
err:
       cmdprint("?%r\n");
       Bterm(bp);
       return -1;
}

Pal *
findpal(char *sf, char *fn, int op)
{
       File *f;
       char *s, *q;
       Ident i;
       int fd;
       Biobuf *bp;
       Pal *p;

       if(sf == nil)
               sf = "";
       s = emalloc(strlen(sf) + strlen(fn) + 2);
       strcpy(s, sf);
       q = strrchr(s, '/');
       if(q != nil)
               *++q = 0;
       else
               *s = 0;
       strcpy(s, fn);
       fd = open(s, OREAD);
       if(fd < 0){
               free(s);
               return nil;
       }
       i = getident(fd);
       if(i.type == (uint)-1){
               close(fd);
               return nil;
       }
       for(f = flist.next; f != &flist; f = f->next)
               if(f->type == PAL && identcmp(&f->id, &i) == 0){
                       close(fd);
                       putident(i);
                       return (Pal *) f;
               }
       putident(i);
       if(op == 0){
               close(fd);
               return nil;
       }
       bp = emalloc(sizeof(*bp));
       Binit(bp, fd, OREAD);
       p = newpal(s);
       if(readpal(p, bp) < 0){
               putfil(p);
               p = nil;
               goto end;
       }
end:
       Bterm(bp);
       close(fd);
       free(bp);
       free(s);
       return p;
}

static void
palredraw(Pal *p)
{
       File *f;

       filredraw(p);
       for(f = flist.next; f != &flist; f = f->next)
               if(f->type == SPR && ((Spr *) f)->pal == p)
                       filredraw(f);
}

void
palsize(Pal *p, int sz, int ch)
{
       int i;

       if(sz == p->ncol)
               return;
       p->cols = realloc(p->cols, sz * sizeof(*p->cols));
       p->ims = realloc(p->ims, sz * sizeof(*p->ims));
       if(sz > p->ncol)
               for(i = p->ncol; i < sz; i++){
                       p->cols[i] = 0;
                       p->ims[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0);
               }
       p->ncol = sz;
       if(ch)
               change(p);
       palredraw(p);
}

void
paldraw(Win *w)
{
       Pal *p;
       int n, i;
       Rectangle r;

       if(w->type != PAL || w->f == nil)
               sysfatal("paldraw: phase error");
       p = (Pal *) w->f;
       n = Dx(w->inner) / w->zoom;
       draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP);
       for(i = 0; i < p->ncol; i++){
               r.min = addpt(w->inner.min, mulpt(Pt(i%n, i/n), w->zoom));
               r.max.x = r.min.x + w->zoom;
               r.max.y = r.min.y + w->zoom;
               draw(w->im, r, p->ims[i], nil, ZP);
               if(p->sel == i)
                       border(w->im, r, SELSIZ, display->white, ZP);
       }
}

void
palset(Pal *p, int s, u32int c)
{
       if(s < 0 || s >= p->ncol || p->cols[s] == c)
               return;
       p->cols[s] = c;
       freeimage(p->ims[s]);
       p->ims[s] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c << 8 | 0xff);
       change(p);
       palredraw(p);
}

static int
palinit(Win *w)
{
       w->zoom = 32;
       return 0;
}

static void
palzerox(Win *w, Win *v)
{
       v->zoom = w->zoom;
}

static void
palclick(Win *w, Mousectl *mc)
{
       int n, i;
       Point pt;
       Pal *p;

       if(!ptinrect(mc->xy, w->inner))
               return;
       if(w->f == nil)
               sysfatal("palclick: phase error");
       p = (Pal *) w->f;
       n = Dx(w->inner) / w->zoom;
       pt = subpt(mc->xy, w->inner.min);
       if(pt.x >= n * w->zoom)
               return;
       i = pt.x / w->zoom + pt.y / w->zoom * n;
       if(i >= p->ncol)
               return;
       p->sel = i;
       palredraw(p);
}

Wintab paltab = {
       .init = palinit,
       .click = palclick,
       .draw = paldraw,
       .zerox = palzerox,
       .hexcols = {
               [BORD] 0xAA0000FF,
               [DISB] 0xCC8888FF,
               [BACK] 0xFFCCFFFF,
       },
};