
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkWidgetViews.h"
#include "SkAnimator.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include "SkStream.h"
#include "SkSystemEventTypes.h"

#ifdef SK_DEBUG
	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
	{
		const char* value = dom.findAttr(node, attr);
		if (value)
			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
	}
#else
	#define assert_no_attr(dom, node, attr)
#endif
/*
I have moved this to SkWidgetViews.h
enum SkinEnum {
	kButton_SkinEnum,
	kProgress_SkinEnum,
	kScroll_SkinEnum,
	kStaticText_SkinEnum,
	
	kSkinEnumCount
};
*/

const char* get_skin_enum_path(SkinEnum se)
{
	SkASSERT((unsigned)se < kSkinEnumCount);

	static const char* gSkinPaths[] = {
            "common/default/default/skins/border3.xml",
            "common/default/default/skins/button.xml",
            "common/default/default/skins/progressBar.xml",
            "common/default/default/skins/scrollBar.xml",
            "common/default/default/skins/statictextpaint.xml"
	};

	return gSkinPaths[se];
}

void init_skin_anim(const char path[], SkAnimator* anim)
{
	SkASSERT(path && anim);

	SkFILEStream	stream(path);

	if (!stream.isValid())
	{
		SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
		sk_throw();
	}

	if (!anim->decodeStream(&stream))
	{
		SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
		sk_throw();
	}
}

void init_skin_anim(SkinEnum se, SkAnimator* anim)
{
	init_skin_anim(get_skin_enum_path(se), anim);
}

void init_skin_paint(SkinEnum se, SkPaint* paint)
{
	SkASSERT(paint);

	SkAnimator	anim;
	SkCanvas	canvas;
	
	init_skin_anim(se, &anim);
	anim.draw(&canvas, paint, 0);
}

void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
{
	SkASSERT(paint);

	SkAnimator	anim;
	SkCanvas	canvas;
	
	if (!anim.decodeDOM(dom, node))
	{
		SkDEBUGF(("inflate_paint: decoding dom failed\n"));
		SkDEBUGCODE(dom.dump(node);)
		sk_throw();
	}	
	anim.draw(&canvas, paint, 0);
}

////////////////////////////////////////////////////////////////////////////////////////

SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
{
}

const char* SkWidgetView::getLabel() const
{
	return fLabel.c_str();
}
	
void SkWidgetView::getLabel(SkString* label) const
{
	if (label)
		*label = fLabel;
}

void SkWidgetView::setLabel(const char label[])
{
	this->setLabel(label, label ? strlen(label) : 0);
}

void SkWidgetView::setLabel(const char label[], size_t len)
{
	if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len))
	{
		SkString	tmp(label, len);

		this->onLabelChange(fLabel.c_str(), tmp.c_str());
		fLabel.swap(tmp);
	}
}

void SkWidgetView::setLabel(const SkString& label)
{
	if (fLabel != label)
	{
		this->onLabelChange(fLabel.c_str(), label.c_str());
		fLabel = label;
	}
}

bool SkWidgetView::postWidgetEvent()
{
	if (!fEvent.isType(""))
	{
		SkEvent	evt(fEvent);	// make a copy since onPrepareWidgetEvent may edit the event

		if (this->onPrepareWidgetEvent(&evt))
		{
			SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)

			this->postToListeners(evt);	// wonder if this should return true if there are > 0 listeners...
			return true;
		}
	}
	return false;
}

/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
{
	this->INHERITED::onInflate(dom, node);

	const char* label = dom.findAttr(node, "label");
	if (label)
		this->setLabel(label);
		
	if ((node = dom.getFirstChild(node, "event")) != NULL)
		fEvent.inflate(dom, node);
}

/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
{
	this->inval(NULL);
}

static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";

/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
{
	evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
	return true;
}

SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
{
	int32_t	sinkID;
	
	return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
{
	if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
	{
		this->postWidgetEvent();
		return true;
	}
	return this->INHERITED::onEvent(evt);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
{
}

void SkCheckButtonView::setCheckState(CheckState state)
{
	SkASSERT((unsigned)state <= kUnknown_CheckState);
	
	if (fCheckState != state)
	{
		this->onCheckStateChange(this->getCheckState(), state);
		fCheckState = SkToU8(state);
	}
}
	
/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
{
	this->inval(NULL);
}

/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
{
	this->INHERITED::onInflate(dom, node);
	
	int index = dom.findList(node, "check-state", "off,on,unknown");
	if (index >= 0)
		this->setCheckState((CheckState)index);
}

static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";

/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
{
	// could check if we're "disabled", and return false...

	evt->setS32(gCheckStateSlotName, this->getCheckState());
	return true;
}

bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
{
	int32_t	state32;
	
	if (evt.findS32(gCheckStateSlotName, &state32))
	{
		if (state)
			*state = (CheckState)state32;
		return true;
	}
	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

#include "SkTime.h"
#include <stdio.h>

class SkAnimButtonView : public SkButtonView {
public:
	SkAnimButtonView()
	{
		fAnim.setHostEventSink(this);
		init_skin_anim(kButton_SkinEnum, &fAnim);
	}

protected:
	virtual void onLabelChange(const char oldLabel[], const char newLabel[])
	{
		this->INHERITED::onLabelChange(oldLabel, newLabel);

		SkEvent evt("user");
		evt.setString("id", "setLabel");
		evt.setString("LABEL", newLabel);
		fAnim.doUserEvent(evt);
	}
	
	virtual void onFocusChange(bool gainFocus)
	{
		this->INHERITED::onFocusChange(gainFocus);

		SkEvent evt("user");
		evt.setString("id", "setFocus");
		evt.setS32("FOCUS", gainFocus);
		fAnim.doUserEvent(evt);
	}

	virtual void onSizeChange()
	{
		this->INHERITED::onSizeChange();

		SkEvent evt("user");
		evt.setString("id", "setDim");
		evt.setScalar("dimX", this->width());
		evt.setScalar("dimY", this->height());
		fAnim.doUserEvent(evt);
	}

	virtual void onDraw(SkCanvas* canvas)
	{
		SkPaint						paint;		
		SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
		
		if (diff == SkAnimator::kDifferent)
			this->inval(NULL);
		else if (diff == SkAnimator::kPartiallyDifferent)
		{
			SkRect	bounds;
			fAnim.getInvalBounds(&bounds);
			this->inval(&bounds);
		}
	}
	
	virtual bool onEvent(const SkEvent& evt)
	{
		if (evt.isType(SK_EventType_Inval))
		{
			this->inval(NULL);
			return true;
		}
		if (evt.isType("recommendDim"))
		{
			SkScalar	height;
			
			if (evt.findScalar("y", &height))
				this->setHeight(height);
			return true;
		}
		return this->INHERITED::onEvent(evt);
	}
	
	virtual bool onPrepareWidgetEvent(SkEvent* evt)
	{
		if (this->INHERITED::onPrepareWidgetEvent(evt))
		{
			SkEvent	e("user");
			e.setString("id", "handlePress");
			(void)fAnim.doUserEvent(e);
			return true;
		}
		return false;
	}

private:
	SkAnimator	fAnim;
	
	typedef SkButtonView INHERITED;
};

////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

SkView* SkWidgetFactory(const char name[])
{
	if (name == NULL)
		return NULL;
	
	// must be in the same order as the SkSkinWidgetEnum is declared
	static const char* gNames[] = {
		"sk-border",
		"sk-button",
		"sk-image",
		"sk-list",
		"sk-progress",
		"sk-scroll",
		"sk-text"
		
	};

	for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++)
		if (!strcmp(gNames[i], name))
			return SkWidgetFactory((SkWidgetEnum)i);

	return NULL;
}

#include "SkImageView.h"
#include "SkProgressBarView.h"
#include "SkScrollBarView.h"
#include "SkBorderView.h"

SkView* SkWidgetFactory(SkWidgetEnum sw)
{
	switch (sw) {
	case kBorder_WidgetEnum:
		return new SkBorderView;
	case kButton_WidgetEnum:
		return new SkAnimButtonView;
	case kImage_WidgetEnum:
		return new SkImageView;
	case kList_WidgetEnum:
		return new SkListView;
	case kProgress_WidgetEnum:
		return new SkProgressBarView;
	case kScroll_WidgetEnum:
		return new SkScrollBarView;
	case kText_WidgetEnum:
		return new SkStaticTextView;
	default:
		SkDEBUGFAIL("unknown enum passed to SkWidgetFactory");
		break;
	}
	return NULL;
}
