/*
 * Copyright (c) 2005-2015 Hypertriton, Inc. 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include 
#include 
#include 
RG_Animview *
RG_AnimviewNew(void *parent)
{
	RG_Animview *av;
	av = Malloc(sizeof(RG_Animview));
	AG_ObjectInit(av, &rgAnimviewClass);
	AG_ObjectAttach(parent, av);
	return (av);
}
/* Timer for updating the current frame during playback. */
static Uint32
PlayUpdateTimeout(AG_Timer *to, AG_Event *event)
{
	RG_Animview *av = AG_SELF();
	if (av->anim->nframes == 0)
		return (0);
	AG_Redraw(av);
	if (++av->frame < av->anim->nframes) {
		RG_AnimFrame *fr = &av->anim->frames[av->frame];
		return (fr->delay > 0 ? fr->delay*100/av->speed : 1);
	} else {
		RG_AnimFrame *fr = &av->anim->frames[0];
		av->frame = 0;
		return (fr->delay > 0 ? fr->delay*100/av->speed: 1);
	}
}
static void
Play(AG_Event *event)
{
	RG_Animview *av = AG_PTR(1);
	AG_AddTimer(av, &av->timer, 1, PlayUpdateTimeout, NULL);
	AG_WidgetDisable(av->btns.play);
	AG_WidgetEnable(av->btns.pause);
	AG_WidgetEnable(av->btns.stop);
}
static void
Pause(AG_Event *event)
{
	RG_Animview *av = AG_PTR(1);
	
	AG_DelTimer(av, &av->timer);
	AG_WidgetEnable(av->btns.play);
	AG_WidgetDisable(av->btns.pause);
	AG_WidgetDisable(av->btns.stop);
}
static void
Stop(AG_Event *event)
{
	RG_Animview *av = AG_PTR(1);
	
	AG_DelTimer(av, &av->timer);
	av->frame = 0;
	
	AG_WidgetEnable(av->btns.play);
	AG_WidgetDisable(av->btns.pause);
	AG_WidgetDisable(av->btns.stop);
}
static void
SetSpeed(AG_Event *event)
{
	RG_Animview *av = AG_PTR(1);
	Uint factor = AG_INT(2);
	av->speed = factor;
}
static void
PopupMenu(RG_Animview *av)
{
	AG_PopupMenu *pm;
	AG_MenuItem *mSpd;
	if (av->pm != NULL) {
		AG_PopupShow(av->pm);
		return;
	}
	if ((av->pm = pm = AG_PopupNew(av)) == NULL)
		return;
	AG_MenuAction(pm->root, _("Play"),  rgIconPlay.s,	Play,"%p", av);
	AG_MenuAction(pm->root, _("Pause"), rgIconPause.s,	Pause,"%p", av);
	AG_MenuAction(pm->root, _("Stop"),  rgIconStop.s,	Stop,"%p", av);
	AG_MenuSeparator(pm->root);
	mSpd = AG_MenuNode(pm->root, _("Playback speed"), NULL);
	{
		AG_MenuAction(mSpd, "4x",  NULL, SetSpeed,"%p,%i", av, 400);
		AG_MenuAction(mSpd, "3x",  NULL, SetSpeed,"%p,%i", av, 300);
		AG_MenuAction(mSpd, "2x",  NULL, SetSpeed,"%p,%i", av, 200);
		AG_MenuAction(mSpd, "1x",  NULL, SetSpeed,"%p,%i", av, 100);
		AG_MenuAction(mSpd, "1/2", NULL, SetSpeed,"%p,%i", av, 50);
		AG_MenuAction(mSpd, "1/3", NULL, SetSpeed,"%p,%i", av, 33);
		AG_MenuAction(mSpd, "1/4", NULL, SetSpeed,"%p,%i", av, 25);
	}
	AG_PopupShow(pm);
}
static void
MouseButtonDown(AG_Event *event)
{
	RG_Animview *av = AG_SELF();
	int button = AG_INT(1);
/*	int x = AG_INT(2); */
	int y = AG_INT(3);
	if (button == AG_MOUSE_RIGHT &&
	    y < WIDGET(av)->h - WIDGET(av->btns.play)->h)
		PopupMenu(av);
}
static void
Init(void *obj)
{
	RG_Animview *av = obj;
	WIDGET(av)->flags |= AG_WIDGET_EXPAND;
	av->pre_w = 64;
	av->pre_h = 64;
	av->ranim.x = 0;
	av->ranim.y = 0;
	av->ranim.w = av->pre_w;
	av->ranim.h = av->pre_h;
	av->speed = 100;
	av->pm = NULL;
	
	av->btns.play = AG_ButtonNewS(av, 0, NULL);
	AG_ButtonSurfaceNODUP(av->btns.play, rgIconPlay.s);
	AG_SetEvent(av->btns.play, "button-pushed", Play, "%p", av);
	av->btns.pause = AG_ButtonNewS(av, 0, NULL);
	AG_ButtonSurfaceNODUP(av->btns.pause, rgIconPause.s);
	AG_SetEvent(av->btns.pause, "button-pushed", Pause, "%p", av);
	av->btns.stop = AG_ButtonNewS(av, 0, NULL);
	AG_ButtonSurfaceNODUP(av->btns.stop, rgIconStop.s);
	AG_SetEvent(av->btns.stop, "button-pushed", Stop, "%p", av);
	
	AG_WidgetEnable(av->btns.play);
	AG_WidgetDisable(av->btns.pause);
	AG_WidgetDisable(av->btns.stop);
	
	AG_SetEvent(av, "mouse-button-down", MouseButtonDown, NULL);
}
static void
Destroy(void *obj)
{
	RG_Animview *av = obj;
	
	if (av->pm != NULL)
		AG_PopupDestroy(av->pm);
}
void
RG_AnimviewSizeHint(RG_Animview *av, int w, int h)
{
	av->pre_w = w;
	av->pre_h = h;
}
static void
SizeRequest(void *p, AG_SizeReq *r)
{
	RG_Animview *av = p;
	AG_SizeReq rPlay, rPause, rStop;
	AG_WidgetSizeReq(av->btns.play, &rPlay);
	AG_WidgetSizeReq(av->btns.pause, &rPause);
	AG_WidgetSizeReq(av->btns.stop, &rStop);
	r->w = AG_MAX(av->pre_w, rPlay.w+rPause.w+rStop.w);
	r->h = av->pre_h + AG_MAX3(rPlay.h, rPause.h, rStop.h);
}
static int
SizeAllocate(void *p, const AG_SizeAlloc *a)
{
	RG_Animview *av = p;
	AG_SizeReq rBtn;
	AG_SizeAlloc aBtn;
	int hBtn = 0;
	AG_WidgetSizeReq(av->btns.play, &rBtn);
	if (hBtn < rBtn.h) { hBtn = rBtn.h; }
	AG_WidgetSizeReq(av->btns.pause, &rBtn);
	if (hBtn < rBtn.h) { hBtn = rBtn.h; }
	AG_WidgetSizeReq(av->btns.stop, &rBtn);
	if (hBtn < rBtn.h) { hBtn = rBtn.h; }
	aBtn.x = 0;
	aBtn.y = a->h - hBtn;
	aBtn.w = a->w/3;
	aBtn.h = hBtn;
	AG_WidgetSizeAlloc(av->btns.play, &aBtn);
	aBtn.x += aBtn.w;
	AG_WidgetSizeAlloc(av->btns.pause, &aBtn);
	aBtn.x += aBtn.w;
	if ((aBtn.x + aBtn.w) < a->w) { aBtn.w++; } /* For rounding */
	AG_WidgetSizeAlloc(av->btns.stop, &aBtn);
	av->ranim.x = a->w/2 - av->ranim.w/2;
	av->ranim.y = (a->h - hBtn)/2 - av->ranim.h/2;
	return (0);
}
static void
Draw(void *p)
{
	RG_Animview *av = p;
	RG_AnimFrame *fr;
	if (av->anim == NULL || av->anim->nframes < 1) {
		return;
	}
	fr = &av->anim->frames[av->frame];
	if (fr->su != NULL) {
		AG_Rect r;
		r.x = 0;
		r.y = 0;
		r.w = WIDTH(av);
		r.h = HEIGHT(av);
		AG_PushClipRect(av, &r);
		AG_WidgetBlit(av, fr->su, av->ranim.x, av->ranim.y);
		AG_PopClipRect(av);
	}
}
void
RG_AnimviewSetAnimation(RG_Animview *av, RG_Anim *anim)
{
	AG_DelTimer(av, &av->timer);
	av->anim = anim;
	av->frame = 0;
	av->ranim.x = WIDGET(av)->w/2 - anim->w/2;
	av->ranim.y = (WIDGET(av)->h - WIDGET(av->btns.play)->h)/2 - anim->h/2;
	av->ranim.w = anim->w;
	av->ranim.h = anim->h;
	AG_Redraw(av);
}
AG_WidgetClass rgAnimviewClass = {
	{
		"Agar(Widget):RG(Animview)",
		sizeof(RG_Animview),
		{ 0,0 },
		Init,
		Destroy,
		NULL,		/* destroy */
		NULL,		/* load */
		NULL,		/* save */
		NULL		/* edit */
	},
	Draw,
	SizeRequest,
	SizeAllocate
};