/*
 * webkernel.c
 *
 * A small embeddable web server.
 *
 * Author: Dario Rodriguez dario@softhome.net
 * This library is licensed on the terms of the GNU LGPL v2+
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "socklib.h"
#include "webkernel.h"

#define FDBLOCK 256
#define CLIENTBLOCK 1024
#define CLIENTBLOCKBLOCK 256


typedef struct wk_client {
        wk *web;
        int connid; /* it is numblock*CLIENTBLOCK+offset_in_block */
        int fd;
} wk_client;

typedef struct wk_clientblock {
        int sizeclients;
        int usedclients;
        wk_client *clients;
} wk_clientblock;

typedef struct _wk {
        int serverfd;
        fd_set fdset;
        sselect *ssel;
        int sizeclientblocks;
        int usedclientblocks;
        wk_clientblock *clientblocks;
} _wk;

static int wk_accept(_wk *web);
static wk_client *wk_clientacquire(_wk *web);
static int wk_clientrelease(_wk *web, int connid);
wk_client *wk_clientget(_wk *web, int connid);

wk *
wk_init(int port, sselect *ssel, void (*callback_event)(/*wk *paramweb,int connid, wk_event event, void *userptr*/), void (*callback_http)(/*wk *paramweb,int connid, wk_uri *uri, void *userptr*/), void (*callback_continuation)(/*wk *paramweb,int connid, wk_uri *uri, void *userptr*/), void *userptr)
{
        _wk *web;
        if(ssel==NULL || callback_http==NULL)
                return(NULL);
        if((web=malloc(sizeof(_wk)))==NULL)
                return(NULL);
        memset(web,0,sizeof(_wk));
        web->serverfd=-1;
        web->ssel=ssel;
        FD_ZERO(&(web->fdset));
        if((web->serverfd=ipv4_server(port))==-1) {
                wk_free((wk *)web),web=NULL;
                return(NULL);
        }
        FD_SET(web->serverfd,&(web->fdset));
        sselect_addread(ssel,web->serverfd,NULL);
        return((wk *)web);
}

void
wk_free(wk *paramweb)
{
        _wk *web=(_wk *)paramweb;
        if(web==NULL)
                return;
        if(web->serverfd!=-1) {
                sselect_delread(web->ssel,web->serverfd);
                FD_CLR(web->serverfd,&(web->fdset));
                close(web->serverfd),web->serverfd=-1;
        }
        free(web),web=NULL;
}

wk_uri *
wk_geturi(wk *paramweb, int connid)
{

}

int
wk_service(wk *paramweb)
{
        int fds[FDBLOCK];
        int fd;
        int n;
        int i;
        _wk *web=(_wk *)paramweb;
        if(web==NULL)
                return(-1);
        while((n=sselect_getreadfiltered(web,&(web->fdset),fds,sizeof(fds)/sizeof(fds[0])))>0) {
                for(i=0;i<n;i++) {
                        fd=fds[i];
                        if(fd==web->serverfd) {
                                /* accept new connection */
                                wk_accept(web);
                                continue;
                        }
                }
        }
}

int
wk_serve_buffer_as_file(wk *paramweb, int connid, void *data, int datalen, const char *mime)
{

}

int
wk_serve_file(wk *paramweb, int connid, char *filename, const char *mime)
{

}

int
wk_serve_error(wk *paramweb, int connid, wk_error wkerror)
{

}

int
wk_writestr(wk *paramweb, int connid, char *str)
{

}

int
wk_write(wk *paramweb, int connid, void *data, int datalen)
{

}

int
wk_close(wk *paramweb, int connid)
{

}

char *
wk_uri_getheader(wk_uri *wkuri, char *header, char *defaultvalue)
{

}

char *
wk_uri_getheaderbynum(wk_uri *wkuri, int num)
{

}

const char *
mime_getdefault(const char *filename, const char *defaultmime)
{
        const char *dotptr,*dotdotptr;
        int i;
        struct {
                const char *ext;
                const char *mime;
        } dotdot[]={{"tar.gz","application/x-tgz"}},
          dot[]={
                {"html","text/html"},
                {"htm","text/html"},
                {"shtml","text/html"},
                {"css","text/css"},
                {"gif","image/gif"},
                {"jpeg","image/jpeg"},
                {"jpg","image/jpeg"},
                {"png","image/png"},
                {"tiff","image/tiff"},
                {"tif","image/tiff"},
                {"bmp","image/x-ms-bmp"},
                {"svg","image/svg+xml"},
                {"svgz","image/svg+xml"},
                {"js","application/x-javascript"},
                {"atom","application/atom+xml"},
                {"txt","text/plain"},
                {"json","application/json"},
                {"pdf","application/pdf"},
                {"zip","application/zip"},
                {"mp3","audio/mpeg"},
                {"wav","audio/x-wav"},
                {"ogg","audio/ogg"},
                {"mp4","video/mp4"},
                {"webm","video/webm"},
                {"avi","video/x-msvideo"}};
        if(filename==NULL || (dotptr=strrchr(filename,'.'))==NULL)
                return(defaultmime);
        if(dotptr>filename) {
                for(dotdotptr=(dotptr-1);dotdotptr>filename && *dotdotptr!='.';dotdotptr--)
                        ;
                if(*dotdotptr=='.') {
                        for(i=0;i<(sizeof(dotdot)/sizeof(dotdot[0]));i++) {
                                if(strcmp(dotdotptr+1,dotdot[i].ext)==0)
                                        return(dotdot[i].mime);
                        }
                }
        }
        for(i=0;i<(sizeof(dot)/sizeof(dot[0]));i++) {
                if(strcmp(dotptr+1,dot[i].ext)==0)
                        return(dot[i].mime);
        }
        return(defaultmime);
}

siobuf *
wk_iobufget(wk *paramweb)
{

}

void
wk_iobuffree(wk *paramweb, int iobufnum)
{

}

/* local functions */
static int
wk_accept(_wk *web)
{
        int newfd;
        wk_client *client;
        if((newfd=sock_accept(web->serverfd))==-1)
                return(-1);
        if((client=wk_clientacquire(web))==NULL) {
                close(newfd),newfd=-1;
                return(-1);
        }
        client->fd=newfd;
        FD_SET(client->fd,&(web->fdset));
        sselect_addread(web->ssel,client->fd,(void *)client);
        return(0);
}

static wk_client *
wk_clientacquire(_wk *web)
{
#warning TODO
}

static int
wk_clientrelease(_wk *web, int connid)
{
}

wk_client *
wk_clientget(_wk *web, int connid)
{
        int numblock;
        numblock=connid/CLIENTBLOCK;
        if(web==NULL ||
          web->clientblocks==NULL ||
          web->sizeclientblocks<=numblock ||
          web->clientblocks[numblock].clients==NULL)
                return(NULL);
        return(web->clientblocks[numblock].clients+(connid%CLIENTBLOCK));
}