
/*
 * 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 "SkWidget.h"
#include "SkCanvas.h"
#include "SkKey.h"
#include "SkParsePaint.h"
#include "SkSystemEventTypes.h"
#include "SkTextBox.h"

#if 0

#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

#include "SkAnimator.h"
#include "SkTime.h"

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

enum SkinType {
    kPushButton_SkinType,
    kStaticText_SkinType,

    kSkinTypeCount
};

struct SkinSuite {
    SkinSuite();
    ~SkinSuite()
    {
        for (int i = 0; i < kSkinTypeCount; i++)
            delete fAnimators[i];
    }

    SkAnimator*    get(SkinType);

private:
    SkAnimator*    fAnimators[kSkinTypeCount];
};

SkinSuite::SkinSuite()
{
    static const char kSkinPath[] = "skins/";

    static const char* gSkinNames[] = {
        "pushbutton_skin.xml",
        "statictext_skin.xml"
    };

    for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
    {
        size_t        len = strlen(gSkinNames[i]);
        SkString    path(sizeof(kSkinPath) - 1 + len);

        memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
        memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);

        fAnimators[i] = new SkAnimator;
        if (!fAnimators[i]->decodeURI(path.c_str()))
        {
            delete fAnimators[i];
            fAnimators[i] = NULL;
        }
    }
}

SkAnimator* SkinSuite::get(SkinType st)
{
    SkASSERT((unsigned)st < kSkinTypeCount);
    return fAnimators[st];
}

static SkinSuite* gSkinSuite;

static SkAnimator* get_skin_animator(SkinType st)
{
#if 0
    if (gSkinSuite == NULL)
        gSkinSuite = new SkinSuite;
    return gSkinSuite->get(st);
#else
    return NULL;
#endif
}

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

void SkWidget::Init()
{
}

void SkWidget::Term()
{
    delete gSkinSuite;
}

void SkWidget::onEnabledChange()
{
    this->inval(NULL);
}

void SkWidget::postWidgetEvent()
{
    if (!fEvent.isType("") && this->hasListeners())
    {
        this->prepareWidgetEvent(&fEvent);
        this->postToListeners(fEvent);
    }
}

void SkWidget::prepareWidgetEvent(SkEvent*)
{
    // override in subclass to add any additional fields before posting
}

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

    if ((node = dom.getFirstChild(node, "event")) != NULL)
        fEvent.inflate(dom, node);
}

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

size_t SkHasLabelWidget::getLabel(SkString* str) const
{
    if (str)
        *str = fLabel;
    return fLabel.size();
}

size_t SkHasLabelWidget::getLabel(char buffer[]) const
{
    if (buffer)
        memcpy(buffer, fLabel.c_str(), fLabel.size());
    return fLabel.size();
}

void SkHasLabelWidget::setLabel(const SkString& str)
{
    this->setLabel(str.c_str(), str.size());
}

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

void SkHasLabelWidget::setLabel(const char label[], size_t len)
{
    if (!fLabel.equals(label, len))
    {
        fLabel.set(label, len);
        this->onLabelChange();
    }
}

void SkHasLabelWidget::onLabelChange()
{
    // override in subclass
}

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

    const char* text = dom.findAttr(node, "label");
    if (text)
        this->setLabel(text);
}

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

void SkButtonWidget::setButtonState(State state)
{
    if (fState != state)
    {
        fState = state;
        this->onButtonStateChange();
    }
}

void SkButtonWidget::onButtonStateChange()
{
    this->inval(NULL);
}

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

    int    index;
    if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
        this->setButtonState((State)index);
}

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

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

static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state)
{
    if (!enabled)
        return "disabled";
    if (state == SkButtonWidget::kOn_State)
    {
        SkASSERT(focused);
        return "enabled-pressed";
    }
    if (focused)
        return "enabled-focused";
    return "enabled";
}

#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
#include "SkEmbossMaskFilter.h"

static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
{
    SkEmbossMaskFilter::Light    light;

    light.fDirection[0] = SK_Scalar1/2;
    light.fDirection[1] = SK_Scalar1/2;
    light.fDirection[2] = SK_Scalar1/3;
    light.fAmbient        = 0x48;
    light.fSpecular        = 0x80;

    if (pressed)
    {
        light.fDirection[0] = -light.fDirection[0];
        light.fDirection[1] = -light.fDirection[1];
    }
    if (focus)
        light.fDirection[2] += SK_Scalar1/4;

    SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
    paint->setMaskFilter(new SkEmbossMaskFilter(sigma, light))->unref();
}

void SkPushButtonWidget::onDraw(SkCanvas* canvas)
{
    this->INHERITED::onDraw(canvas);

    SkString label;
    this->getLabel(&label);

    SkAnimator* anim = get_skin_animator(kPushButton_SkinType);

    if (anim)
    {
        SkEvent    evt("user");

        evt.setString("id", "prime");
        evt.setScalar("prime-width", this->width());
        evt.setScalar("prime-height", this->height());
        evt.setString("prime-text", label);
        evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));

        (void)anim->doUserEvent(evt);
        SkPaint paint;
        anim->draw(canvas, &paint, SkTime::GetMSecs());
    }
    else
    {
        SkRect    r;
        SkPaint    p;

        r.set(0, 0, this->width(), this->height());
        p.setAntiAliasOn(true);
        p.setColor(SK_ColorBLUE);
        create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
        canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
        p.setMaskFilter(NULL);

        p.setTextAlign(SkPaint::kCenter_Align);

        SkTextBox    box;
        box.setMode(SkTextBox::kOneLine_Mode);
        box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
        box.setBox(0, 0, this->width(), this->height());

//        if (this->getButtonState() == kOn_State)
//            p.setColor(SK_ColorRED);
//        else
            p.setColor(SK_ColorWHITE);

        box.draw(canvas, label.c_str(), label.size(), p);
    }
}

SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi)
{
    this->acceptFocus();
    return new Click(this);
}

bool SkPushButtonWidget::onClick(Click* click)
{
    SkRect    r;
    State    state = kOff_State;

    this->getLocalBounds(&r);
    if (r.contains(click->fCurr))
    {
        if (click->fState == Click::kUp_State)
            this->postWidgetEvent();
        else
            state = kOn_State;
    }
    this->setButtonState(state);
    return true;
}

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

SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
{
    fMargin.set(0, 0);
    fMode = kFixedSize_Mode;
    fSpacingAlign = SkTextBox::kStart_SpacingAlign;
}

SkStaticTextView::~SkStaticTextView()
{
}

void SkStaticTextView::computeSize()
{
    if (fMode == kAutoWidth_Mode)
    {
        SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL);
        this->setWidth(width + fMargin.fX * 2);
    }
    else if (fMode == kAutoHeight_Mode)
    {
        SkScalar width = this->width() - fMargin.fX * 2;
        int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;

        SkScalar    before, after;
        (void)fPaint.measureText(0, NULL, &before, &after);

        this->setHeight(lines * (after - before) + fMargin.fY * 2);
    }
}

void SkStaticTextView::setMode(Mode mode)
{
    SkASSERT((unsigned)mode < kModeCount);

    if (fMode != mode)
    {
        fMode = SkToU8(mode);
        this->computeSize();
    }
}

void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
{
    fSpacingAlign = SkToU8(align);
    this->inval(NULL);
}

void SkStaticTextView::getMargin(SkPoint* margin) const
{
    if (margin)
        *margin = fMargin;
}

void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
{
    if (fMargin.fX != dx || fMargin.fY != dy)
    {
        fMargin.set(dx, dy);
        this->computeSize();
        this->inval(NULL);
    }
}

size_t SkStaticTextView::getText(SkString* text) const
{
    if (text)
        *text = fText;
    return fText.size();
}

size_t SkStaticTextView::getText(char text[]) const
{
    if (text)
        memcpy(text, fText.c_str(), fText.size());
    return fText.size();
}

void SkStaticTextView::setText(const SkString& text)
{
    this->setText(text.c_str(), text.size());
}

void SkStaticTextView::setText(const char text[])
{
    this->setText(text, strlen(text));
}

void SkStaticTextView::setText(const char text[], size_t len)
{
    if (!fText.equals(text, len))
    {
        fText.set(text, len);
        this->computeSize();
        this->inval(NULL);
    }
}

void SkStaticTextView::getPaint(SkPaint* paint) const
{
    if (paint)
        *paint = fPaint;
}

void SkStaticTextView::setPaint(const SkPaint& paint)
{
    if (fPaint != paint)
    {
        fPaint = paint;
        this->computeSize();
        this->inval(NULL);
    }
}

void SkStaticTextView::onDraw(SkCanvas* canvas)
{
    this->INHERITED::onDraw(canvas);

    if (fText.isEmpty())
        return;

    SkTextBox    box;

    box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
    box.setSpacingAlign(this->getSpacingAlign());
    box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
    box.draw(canvas, fText.c_str(), fText.size(), fPaint);
}

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

    int    index;
    if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
        this->setMode((Mode)index);
    else
        assert_no_attr(dom, node, "mode");

    if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
        this->setSpacingAlign((SkTextBox::SpacingAlign)index);
    else
        assert_no_attr(dom, node, "mode");

    SkScalar s[2];
    if (dom.findScalars(node, "margin", s, 2))
        this->setMargin(s[0], s[1]);
    else
        assert_no_attr(dom, node, "margin");

    const char* text = dom.findAttr(node, "text");
    if (text)
        this->setText(text);

    if ((node = dom.getFirstChild(node, "paint")) != NULL)
        SkPaint_Inflate(&fPaint, dom, node);
}

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

#include "SkImageDecoder.h"

SkBitmapView::SkBitmapView(U32 flags) : SkView(flags)
{
}

SkBitmapView::~SkBitmapView()
{
}

bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
{
    if (bitmap)
        *bitmap = fBitmap;
    return fBitmap.colorType() != kUnknown_SkColorType;
}

void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels)
{
    if (bitmap)
    {
        fBitmap = *bitmap;
        fBitmap.setOwnsPixels(viewOwnsPixels);
    }
}

bool SkBitmapView::loadBitmapFromFile(const char path[])
{
    SkBitmap    bitmap;

    if (SkImageDecoder::DecodeFile(path, &bitmap))
    {
        this->setBitmap(&bitmap, true);
        bitmap.setOwnsPixels(false);
        return true;
    }
    return false;
}

void SkBitmapView::onDraw(SkCanvas* canvas)
{
    if (fBitmap.colorType() != kUnknown_SkColorType &&
        fBitmap.width() && fBitmap.height())
    {
        SkAutoCanvasRestore    restore(canvas, true);
        SkPaint                p;

        p.setFilterType(SkPaint::kBilinear_FilterType);
        canvas->scale(    this->width() / fBitmap.width(),
                        this->height() / fBitmap.height(),
                        0, 0);
        canvas->drawBitmap(fBitmap, 0, 0, p);
    }
}

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

    const char* src = dom.findAttr(node, "src");
    if (src)
        (void)this->loadBitmapFromFile(src);
}

#endif
