/*
 * 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"

/*
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);

    SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path));
    if (!stream.get()) {
        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;
}