#include <u.h>
#include <libc.h>
#include <bio.h>
#include "httpd.h"
#include "httpsrv.h"

typedef struct Point    Point;
typedef struct OkPoint  OkPoint;
typedef struct Strings  Strings;

struct Point
{
       int     x;
       int     y;
};

struct OkPoint
{
       Point   p;
       int     ok;
};

struct Strings
{
       char    *s1;
       char    *s2;
};

static  char *me;

int             polytest(int, Point, Point, Point);
Strings         getfield(char*);
OkPoint         pt(char*);
char*           translate(HConnect*, char*, char*);
Point           sub(Point, Point);
float           dist(Point, Point);

void
main(int argc, char **argv)
{
       HConnect *c;
       Hio *hout;
       char *dest;

       me = "imagemap";
       c = init(argc, argv);
       hout = &c->hout;
       if(hparseheaders(c, HSTIMEOUT) < 0)
               exits("failed");
       anonymous(c);

       if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){
               hunallowed(c, "GET, HEAD");
               exits("unallowed");
       }
       if(c->head.expectother || c->head.expectcont){
               hfail(c, HExpectFail, nil);
               exits("failed");
       }
       dest = translate(c, c->req.uri, c->req.search);

       if(dest == nil){
               if(c->req.vermaj){
                       hokheaders(c);
                       hprint(hout, "Content-type: text/html\r\n");
                       hprint(hout, "\r\n");
               }
               hprint(hout, "<head><title>Nothing Found</title></head><body>\n");
               hprint(hout, "Nothing satisfying your search request could be found.\n</body>\n");
               hflush(hout);
               writelog(c, "Reply: 200 imagemap %ld %ld\n", hout->seek, hout->seek);
               exits(nil);
       }

       if(http11(c) && strcmp(c->req.meth, "POST") == 0)
               hredirected(c, "303 See Other", dest);
       else
               hredirected(c, "302 Found", dest);
       exits(nil);
}

char*
translate(HConnect *c, char *uri, char *search)
{
       Biobuf *b;
       Strings ss;
       OkPoint okp;
       Point p, cen, q, start;
       float close, d;
       char *line, *to, *def, *s, *dst;
       int n, inside, r, ncsa;

       if(search == nil){
               hfail(c, HNoData, me);
               exits("failed");
       }
       okp = pt(search);
       if(!okp.ok){
               hfail(c, HBadSearch, me);
               exits("failed");
       }
       p = okp.p;

       b = Bopen(uri, OREAD);
       if(b == nil){
               hfail(c, HNotFound, uri);
               exits("failed");
       }

       to = nil;
       def = nil;
       dst = nil;
       close = 0.;
       ncsa = 1;
       while(line = Brdline(b, '\n')){
               line[Blinelen(b)-1] = 0;

               ss = getfield(line);
               s = ss.s1;
               line = ss.s2;
               if(ncsa){
                       ss = getfield(line);
                       dst = ss.s1;
                       line = ss.s2;
               }
               if(strcmp(s, "#cern") == 0){
                       ncsa = 0;
                       continue;
               }
               if(strcmp(s, "rect") == 0){
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       okp = pt(s);
                       q = okp.p;
                       if(!okp.ok || q.x > p.x || q.y > p.y)
                               continue;
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       okp = pt(s);
                       q = okp.p;
                       if(!okp.ok || q.x < p.x || q.y < p.y)
                               continue;
                       if(!ncsa){
                               ss = getfield(line);
                               dst = ss.s1;
                       }
                       return dst;
               }else if(strcmp(s, "circle") == 0){
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       okp = pt(s);
                       cen = okp.p;
                       if(!okp.ok)
                               continue;
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       if(ncsa){
                               okp = pt(s);
                               if(!okp.ok)
                                       continue;
                               if(dist(okp.p, cen) >= dist(p, cen))
                                       return dst;
                       }else{
                               r = strtol(s, nil, 10);
                               ss = getfield(line);
                               dst = ss.s1;
                               d = (float)r * r;
                               if(d >= dist(p, cen))
                                       return dst;
                       }
               }else if(strcmp(s, "poly") == 0){
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       okp = pt(s);
                       start = okp.p;
                       if(!okp.ok)
                               continue;
                       inside = 0;
                       cen = start;
                       for(n = 1; ; n++){
                               ss = getfield(line);
                               s = ss.s1;
                               line = ss.s2;
                               okp = pt(s);
                               q = okp.p;
                               if(!okp.ok)
                                       break;
                               inside = polytest(inside, p, cen, q);
                               cen = q;
                       }
                       inside = polytest(inside, p, cen, start);
                       if(!ncsa)
                               dst = s;
                       if(n >= 3 && inside)
                               return dst;
               }else if(strcmp(s, "point") == 0){
                       ss = getfield(line);
                       s = ss.s1;
                       line = ss.s2;
                       okp = pt(s);
                       q = okp.p;
                       if(!okp.ok)
                               continue;
                       d = dist(p, q);
                       if(!ncsa){
                               ss = getfield(line);
                               dst = ss.s1;
                       }
                       if(d == 0.)
                               return dst;
                       if(close == 0. || d < close){
                               close = d;
                               to = dst;
                       }
               }else if(strcmp(s, "default") == 0){
                       if(!ncsa){
                               ss = getfield(line);
                               dst = ss.s1;
                       }
                       def = dst;
               }
       }
       if(to == nil)
               to = def;
       return to;
}

int
polytest(int inside, Point p, Point b, Point a)
{
       Point pa, ba;

       if(b.y>a.y){
               pa=sub(p, a);
               ba=sub(b, a);
       }else{
               pa=sub(p, b);
               ba=sub(a, b);
       }
       if(0<=pa.y && pa.y<ba.y && pa.y*ba.x<=pa.x*ba.y)
               inside = !inside;
       return inside;
}

Point
sub(Point p, Point q)
{
       p.x -= q.x;
       p.y -= q.y;
       return p;
}

float
dist(Point p, Point q)
{
       p.x -= q.x;
       p.y -= q.y;
       return (float)p.x * p.x + (float)p.y * p.y;
}

OkPoint
pt(char *s)
{
       OkPoint okp;
       Point p;
       char *t, *e;

       if(*s == '(')
               s++;
       t = strchr(s, ')');
       if(t != nil)
               *t = 0;
       p.x = 0;
       p.y = 0;
       t = strchr(s, ',');
       if(t == nil){
               okp.p = p;
               okp.ok = 0;
               return okp;
       }
       e = nil;
       p.x = strtol(s, &e, 10);
       if(e != t){
               okp.p = p;
               okp.ok = 0;
               return okp;
       }
       p.y = strtol(t+1, &e, 10);
       if(e == nil || *e != 0){
               okp.p = p;
               okp.ok = 0;
               return okp;
       }
       okp.p = p;
       okp.ok = 1;
       return okp;
}

Strings
getfield(char *s)
{
       Strings ss;
       char *f;

       while(*s == '\t' || *s == ' ')
               s++;
       f = s;
       while(*s && *s != '\t' && *s != ' ')
               s++;
       if(*s)
               *s++ = 0;
       ss.s1 = f;
       ss.s2 = s;
       return ss;
}