/*
 * boxui.c
 *
 * Multiplatform UI library for SDL2.
 *
 * (c) 2023 Dario Rodriguez <antartica@box-ui.org>
 * Licensed under the terms of the GNU GPL v3.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#include "SDL.h"
#include "SDL_rwops.h"
#include "SDL_ttf.h"

#include "boxui.h"

#include "notosans_regular_ttf.c"

#define DEFAULT_FPS 30

typedef struct intboxui_t {
        char *title;
        int defaultw,defaulth;
        int fps;
        long long fpsepoch;
        long long fpsepochframeno;
        void *userptr;
        int isdirty;
        long long lastrenderedframeno;
        int sizeafterbuf;
        int usedafterbuf;
        char *afterbuf;
        /* temporary members (to be able to test) */
        int screenw;
        int screenh;
        SDL_Window *win;
        SDL_Renderer *renderer;
        SDL_RWops *defaultfontdata;
        TTF_Font *defaultfont;
        int defaultfontheight;
        int defaultfontwidth;
        char currenttext[1024];
        char currentclickevent[1024];
        char currentactionstr[1024];
        int flag_currentactionstrused;
        /* end of temporary members (to be able to test) */
} intboxui_t;

int intboxui_waittick(intboxui_t *boxui);
int intboxui_render(intboxui_t *boxui);
int intboxui_after_doexpired(intboxui_t *boxui);

boxui_t *
boxui_init(char *title,int defaultw, int defaulth, int flags)
{
        intboxui_t *boxui;
        title=(title==NULL)?"":title;
        if((boxui=malloc(sizeof(intboxui_t)))==NULL)
                return(NULL); /* insuf. mem for boxui struct */
        memset(boxui,0,sizeof(intboxui_t));
        if((boxui->title=strdup(title))==NULL) {
                boxui_free((boxui_t *)boxui),boxui=NULL;
                return(NULL); /* insuf. mem for title string */
        }
        /* temporary initialization (to be able to test) */
        if(SDL_Init(SDL_INIT_VIDEO)<0
          || (boxui->win=SDL_CreateWindow(title,
            SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,
            defaultw,defaulth,0))==NULL
          || (boxui->renderer=SDL_CreateRenderer(boxui->win,-1,
            SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC))==NULL) {
                boxui_free(boxui),boxui=NULL;
                return(NULL); /* ERROR: Couldn't init video */
        }
        boxui->screenw=defaultw,boxui->screenh=defaulth;
        if((boxui->defaultfontdata=SDL_RWFromConstMem(notosans_regular_ttf,notosans_regular_ttf_len))==NULL
          || TTF_Init()==-1) {
                boxui_free(boxui),boxui=NULL;
                return(NULL); /* ERROR: Couldn't init font data */
        }
        SDL_RWseek(boxui->defaultfontdata,0,RW_SEEK_SET);
        if(boxui->defaultfontdata==NULL
          || (boxui->defaultfont=TTF_OpenFontRW(boxui->defaultfontdata,0,/* default font size */ 16))==NULL) {
                boxui_free(boxui),boxui=NULL;
                return(NULL); /* ERROR: Couldn't init defaultfont */
        }
        /* font width and height */
        {
                SDL_Surface *s;
                SDL_Color c={0,0,0,0};
                /* width */
                if((s=TTF_RenderUTF8_Blended(boxui->defaultfont,"m",c))==NULL) {
                        boxui_free(boxui),boxui=NULL;
                        return(NULL); /* ERROR: Couldn't query font width */
                }
                boxui->defaultfontwidth=s->w;
                SDL_FreeSurface(s),s=NULL;
                /* height */
                if((s=TTF_RenderUTF8_Blended(boxui->defaultfont,"gjpqy_",c))==NULL) {
                        boxui_free(boxui),boxui=NULL;
                        return(NULL); /* ERROR: Couldn't query font height */
                }
                boxui->defaultfontheight=s->h;
                SDL_FreeSurface(s),s=NULL;
        }
        /* end of temporary initialization (to be able to test) */
        /* return the struct */
        return((boxui_t *)boxui);
}

void
boxui_free(boxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return; /* sanity check failed */
        if(boxui->title!=NULL)
                free(boxui->title),boxui->title=NULL;
        /* temporary initialization freeing */
        if(boxui->defaultfont!=NULL)
                TTF_CloseFont(boxui->defaultfont),boxui->defaultfont=NULL;
        if(TTF_WasInit())
                TTF_Quit();
        if(boxui->defaultfontdata!=NULL)
                SDL_FreeRW(boxui->defaultfontdata),boxui->defaultfontdata=NULL;
        if(boxui->renderer!=NULL)
                SDL_DestroyRenderer(boxui->renderer),boxui->renderer=NULL;
        if(boxui->win!=NULL)
                SDL_DestroyWindow(boxui->win),boxui->win=NULL;
        SDL_Quit();
        /* end of temporary initialization freeing */
        free(boxui),boxui=NULL,paramboxui=NULL;
        return;
}

int
boxui_setuserptr(boxui_t *paramboxui, void *userptr)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(-1); /* sanity check failed */
        boxui->userptr=userptr;
        return(0);

}

void *
boxui_getuserptr(boxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(NULL); /* sanity check failed */
        return(boxui->userptr);
}

int
boxui_setfps(boxui_t *paramboxui, int fps)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || fps<1)
                return(-1); /* sanity check failed */
        boxui->fps=fps;
#if SDL_VERSION_ATLEAST(2,0,18)
        boxui->fpsepoch=(long long)SDL_GetTicks64();
#else
        boxui->fpsepoch=(long long)SDL_GetTicks();
#endif
        boxui->fpsepochframeno=boxui->lastrenderedframeno;
        return(0);
}

int
boxui_getfps(boxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(DEFAULT_FPS); /* sanity check failed */
        return((boxui->fps==0)?DEFAULT_FPS:boxui->fps);
}



int
boxui_tick(boxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL) {
                SDL_Delay(1);
                return(-1); /* sanity check failed */
        }
        if(boxui->isdirty) {
                boxui_ghostrender((boxui_t *)boxui);
                intboxui_waittick(boxui);
                intboxui_render(boxui);
        } else {
                intboxui_waittick(boxui);
        }
        boxui->lastrenderedframeno++;
        return(0);
}

int
boxui_ghostrender(boxui_t *boxui)
{
#warning TODO
        return(-1);
}

int
boxui_refreshevents(boxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(-1); /* sanity check failed */
        SDL_PumpEvents();
        return(0);
}

int
boxui_getevent(boxui_t *paramboxui, SDL_Event *event)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || event==NULL)
                return(-1); /* sanity check failed */
        while(SDL_PeepEvents(event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) {
#if 1
                /* test */
                if(event->type==SDL_MOUSEBUTTONUP) {
                        if(boxui->currentclickevent[0]!='\0') {
                                boxui_putactionstr((boxui_t *)boxui,boxui->currentclickevent,NULL);
                                continue; /* we have used the event */
                        }
                }
                /* end of test */
#endif
                return(0); /* event found and put into "event" */
        }
        return(-1); /* no events remaining */
}

int
boxui_event_isquit(boxui_t *paramboxui, SDL_Event *event)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || event==NULL)
                return(0); /* sanity check failed */
        if(event->type==SDL_WINDOWEVENT && event->window.event==SDL_WINDOWEVENT_CLOSE)
                return(1);
        return(0);
}
int
boxui_event_iskeydown(boxui_t *paramboxui, SDL_Event *event, int *keycode)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || event==NULL)
                return(0); /* sanity check failed */
        if(event->type==SDL_KEYDOWN) {
                if(keycode!=NULL)
                        *keycode=event->key.keysym.sym;
                return(1);
        }
        return(0);
}

int
boxui_event_isresize(boxui_t *paramboxui, SDL_Event *event, int *neww, int *newh)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || event==NULL)
                return(0); /* sanity check failed */
        if(event->type==SDL_WINDOWEVENT_RESIZED
          || event->type==SDL_WINDOWEVENT_SIZE_CHANGED) {
                if(neww!=NULL)
                        *neww=event->window.data1;
                if(newh!=NULL)
                        *newh=event->window.data2;
                return(1);
        }
        return(0);
}

void
boxui_resize(boxui_t *boxui, int neww, int newh)
{
#warning TODO
        return;
}

int
boxui_putactionstr(boxui_t *paramboxui, char *firstelem, ...)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || firstelem==NULL)
                return(-1); /* sanity check failed */
        /* test */
        strncpy(boxui->currentactionstr,firstelem,sizeof(boxui->currentactionstr));
        boxui->currentactionstr[sizeof(boxui->currentactionstr)-1]='\0';
        boxui->flag_currentactionstrused=0;
        /* end of test */
#warning TODO
        return(-1);
}

char *
boxui_getactionstr(boxui_t *paramboxui, char **secondelemtoend)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(NULL); /* sanity check failed */
        /* test */
        if(boxui->flag_currentactionstrused || boxui->currentactionstr[0]=='\0')
                return(NULL);
        boxui->flag_currentactionstrused=1;
        return(boxui->currentactionstr);
        /* end of test */
#warning TODO
        return(NULL);
}

char *
boxui_add(boxui_t *paramboxui, char *filedebug, int linedebug, char *classwiname, char *format, ...)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || classwiname==NULL || format==NULL)
                return(NULL); /* sanity check failed */
        /* test */
        if(strchr(format,'%')==NULL)
                boxui_config(boxui,NULL,-1,classwiname,format);
        /* end of test */
#warning TODO
        return(NULL);
}

char *
boxui_config(boxui_t *paramboxui, char *filedebug, int linedebug, char *winame, char *format, ...)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL || winame==NULL || format==NULL)
                return(NULL); /* sanity check failed */
        /* test */
        {
                char *start,*end;
                int l;
                /* text */
                if((start=strchr(format,'\"'))!=NULL
                  && (start=start+1)!=NULL
                  && (end=strchr(start,'\"'))!=NULL) {
                        l=end-start;
                        l=(l>sizeof(boxui->currenttext))?sizeof(boxui->currenttext)-1:l;
                        memcpy(boxui->currenttext,start,l);
                        boxui->currenttext[l]='\0';
                        boxui->isdirty=1;
                }
                /* click */
                for(start=strchr(format,'-');start!=NULL;start=strchr(start+1,'-')) {
                        if(memcmp(start,"-click ",7)==0) {
                                start+=7;
                                end=strchr(start,' ');
                                end=(end==NULL)?start+strlen(start):end;
                                l=end-start;
                                l=(l>sizeof(boxui->currentclickevent))?sizeof(boxui->currentclickevent)-1:l;
                                memcpy(boxui->currentclickevent,start,l);
                                boxui->currentclickevent[l]='\0';
                        }

                }
        }
        /* end of test */
#warning TODO
        return(NULL);
}

char *
boxui_pack(boxui_t *boxui, char *filedebug, int linedebug, char *winame, char *format, ...)
{
#warning TODO
        return(NULL);
}

int
intboxui_waittick(intboxui_t *paramboxui)
{
        long long delay;
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL) {
                SDL_Delay(1);
                return(-1); /* sanity check failed */
        }
#if SDL_VERSION_ATLEAST(2,0,18)
        delay=((long long)SDL_GetTicks64());
#else
        delay=((long long)SDL_GetTicks());
#endif
        delay-=boxui->fpsepoch;
        delay=(((boxui->lastrenderedframeno-boxui->fpsepochframeno)+1)*1000/((boxui->fps==0)?DEFAULT_FPS:boxui->fps))-delay;
        if(delay>0) {
                SDL_Delay(delay);
        } else if(delay<-1000) {
                /* reset epoch if unsyncronized by more than 1s */
#if SDL_VERSION_ATLEAST(2,0,18)
                boxui->fpsepoch=((long long)SDL_GetTicks64());
#else
                boxui->fpsepoch=((long long)SDL_GetTicks());
#endif
                boxui->fpsepochframeno=boxui->lastrenderedframeno;
        }
        intboxui_after_doexpired(boxui);
        return(0);
}

int
intboxui_render(intboxui_t *paramboxui)
{
        intboxui_t *boxui=(intboxui_t *)paramboxui;
        if(boxui==NULL)
                return(-1); /* sanity check failed */
        /* test */
        SDL_SetRenderDrawColor(boxui->renderer,0xff,0xff,0xff,0xff);
        SDL_RenderClear(boxui->renderer);
        if(boxui->currenttext[0]!='\0') {
                SDL_Surface *fgsurface=NULL;
                SDL_Texture *fg=NULL;
                SDL_Rect dstrect;
                SDL_Color c={0x00,0x00,0x00,0xff};
                if((fgsurface=TTF_RenderUTF8_Blended(boxui->defaultfont,boxui->currenttext,c))!=NULL
                  && (fg=SDL_CreateTextureFromSurface(boxui->renderer,fgsurface))!=NULL) {
                        dstrect.x=(boxui->screenw-fgsurface->w)/2;
                        dstrect.y=(boxui->screenh-fgsurface->h)/2;
                        dstrect.w=fgsurface->w,dstrect.h=fgsurface->h;
                        SDL_RenderCopy(boxui->renderer,fg,NULL,&dstrect);
                }
                if(fgsurface!=NULL)
                        SDL_FreeSurface(fgsurface),fgsurface=NULL;
                if(fg!=NULL)
                        SDL_DestroyTexture(fg),fg=NULL;
        }
        SDL_RenderPresent(boxui->renderer);
        boxui->isdirty=0;
        /* end of test */
#warning TODO
        return(0);
}

int
intboxui_after_doexpired(intboxui_t *boxui)
{
#warning TODO
        return(0);
}