#include <slang.h>
#include <stdlib.h>
#include <string.h>

#include "newt.h"
#include "newt_pr.h"

struct button {
    char * text;
    int compact;
};

static void buttonDrawIt(newtComponent co, int active, int pushed);
static void buttonDrawText(newtComponent co, int active, int pushed);

static void buttonDraw(newtComponent c);
static void buttonDestroy(newtComponent co);
static struct eventResult buttonEvent(newtComponent c,
				      struct event ev);
static void buttonPlace(newtComponent co, int newLeft, int newTop);

static struct componentOps buttonOps = {
    buttonDraw,
    buttonEvent,
    buttonDestroy,
    buttonPlace,
    newtDefaultMappedHandler,
} ;

/* 
 * return NULL on malloc error.
 *  FIXME: all createButton calls should check for error
 */
static newtComponent createButton(int left, int row, const char * text, int compact) {
    newtComponent co;
    struct button * bu;
    int width = wstrlen(text,-1);

    co = malloc(sizeof(*co));
    if (co == NULL) 
	return NULL;
    bu = malloc(sizeof(struct button));
    if (bu == NULL)  {
	free (co);
	return NULL;
    }
    co->data = bu;
    co->destroyCallback = NULL;

    bu->text = strdup(text);
    bu->compact = compact;
    co->ops = &buttonOps;

    if (bu->compact) {
	co->height = 1;
	co->width = width + 3;
    } else {
	co->height = 4;
	co->width = width + 5;
    }

    co->top = row;
    co->left = left;
    co->takesFocus = 1;
    co->isMapped = 0;

    newtGotorc(co->top, co->left);

    return co;

}

newtComponent newtCompactButton(int left, int row, const char * text) {
    return createButton(left, row, text, 1);
}

newtComponent newtButton(int left, int row, const char * text) {
    return createButton(left, row, text, 0);
}

static void buttonDestroy(newtComponent co) {
    struct button * bu = co->data;

    free(bu->text);
    free(bu);
    free(co);
}

static void buttonPlace(newtComponent co, int newLeft, int newTop) {
    co->top = newTop;
    co->left = newLeft;

    newtGotorc(co->top, co->left);
}

static void buttonDraw(newtComponent co) {
    buttonDrawIt(co, 0, 0);
}

static void buttonDrawIt(newtComponent co, int active, int pushed) {
    struct button * bu = co->data;

    if (!co->isMapped) return;

    SLsmg_set_color(NEWT_COLORSET_BUTTON);

    if (bu->compact) {
	if (!active)
	    SLsmg_set_color(NEWT_COLORSET_COMPACTBUTTON);
	else if (SLtt_Use_Ansi_Colors)
	    SLsmg_set_color(NEWT_COLORSET_BUTTON);
	else
	    SLsmg_set_color(NEWT_COLORSET_ACTBUTTON);
	newtGotorc(co->top+ pushed, co->left + 1 + pushed);
	SLsmg_write_char('<');
	SLsmg_write_string(bu->text);
	SLsmg_write_char('>');
    } else {
	if (pushed) {
	    SLsmg_set_color(NEWT_COLORSET_BUTTON);
	    newtDrawBox(co->left + 1, co->top + 1, co->width - 1, 3, 0);

	    SLsmg_set_color(NEWT_COLORSET_WINDOW);
	    newtClearBox(co->left, co->top, co->width, 1);
	    newtClearBox(co->left, co->top, 1, co->height);
	} else {
	    newtDrawBox(co->left, co->top, co->width - 1, 3, 1);
	}

	buttonDrawText(co, active, pushed);
    }
    /* put cursor at beginning of text for better accessibility */
    newtGotorc(co->top + (bu->compact ? 0 : 1) + pushed, co->left + 1 + pushed + 1);
}

static void buttonDrawText(newtComponent co, int active, int pushed) {
    struct button * bu = co->data;

    if (pushed) pushed = 1;

    if (active)
	SLsmg_set_color(NEWT_COLORSET_ACTBUTTON);
    else
	SLsmg_set_color(NEWT_COLORSET_BUTTON);

    newtGotorc(co->top + 1 + pushed, co->left + 1 + pushed);
    SLsmg_write_char(' ');
    SLsmg_write_string(bu->text);
    SLsmg_write_char(' ');
}

static struct eventResult buttonEvent(newtComponent co,
				      struct event ev) {
    struct eventResult er;
    struct button * bu = co->data;

    er.result = ER_IGNORED;

    if (ev.when == EV_NORMAL) {
	switch (ev.event) {
	  case EV_FOCUS:
	    buttonDrawIt(co, 1, 0);
	    er.result = ER_SWALLOWED;
	    break;

	  case EV_UNFOCUS:
	    buttonDrawIt(co, 0, 0);
	    er.result = ER_SWALLOWED;
	    break;

	  case EV_KEYPRESS:
	    if (ev.u.key == ' ' || ev.u.key == '\r') {
		if (!bu->compact) {
		    /* look pushed */
		    buttonDrawIt(co, 1, 1);
		    newtRefresh();
		    newtDelay(150000);
		    buttonDrawIt(co, 1, 0);
		    newtRefresh();
		    newtDelay(150000);
		}

		er.result = ER_EXITFORM;
	    } else
		er.result = ER_IGNORED;
	    break;
	  case EV_MOUSE:
	      if (ev.u.mouse.type == MOUSE_BUTTON_DOWN &&
		  co->top <= ev.u.mouse.y &&
		  co->top + co->height - !bu->compact > ev.u.mouse.y &&
		  co->left <= ev.u.mouse.x &&
		  co->left + co->width - !bu->compact > ev.u.mouse.x) {
		  if (!bu->compact) {
		      buttonDrawIt(co, 1, 1);
		      newtRefresh();
		      newtDelay(150000);
		      buttonDrawIt(co, 1, 0);
		      newtRefresh();
		      newtDelay(150000);
		  }
		  er.result = ER_EXITFORM;
	      }
	    break;
	}
    }

    return er;
}
