/*
 * 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 "TransitionView.h"

#include "OverView.h"
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkTime.h"
#include "SkInterpolator.h"

static const char gIsTransitionQuery[] = "is-transition";
static const char gReplaceTransitionEvt[] = "replace-transition-view";

bool is_transition(SkView* view) {
    SkEvent isTransition(gIsTransitionQuery);
    return view->doQuery(&isTransition);
}

class TransitionView : public SampleView {
    enum {
        // kDurationMS = 500
        kDurationMS = 1
    };

public:
    TransitionView(SkView* prev, SkView* next, int direction) : fInterp(4, 2){
        fAnimationDirection = (Direction)(1 << (direction % 8));

        fPrev = prev;
        fPrev->setClipToBounds(false);
        fPrev->setVisibleP(true);
        (void)SampleView::SetUsePipe(fPrev, SkOSMenu::kOffState);
        //Not calling unref because fPrev is assumed to have been created, so
        //this will result in a transfer of ownership
        this->attachChildToBack(fPrev);

        fNext = next;
        fNext->setClipToBounds(true);
        fNext->setVisibleP(true);
        (void)SampleView::SetUsePipe(fNext, SkOSMenu::kOffState);
        //Calling unref because next is a newly created view and TransitionView
        //is now the sole owner of fNext
        this->attachChildToFront(fNext)->unref();

        fDone = false;
        //SkDebugf("--created transition\n");
    }

    ~TransitionView(){
        //SkDebugf("--deleted transition\n");
    }

    virtual void requestMenu(SkOSMenu* menu) {
        if (SampleView::IsSampleView(fNext))
            ((SampleView*)fNext)->requestMenu(menu);
    }

protected:
    virtual bool onQuery(SkEvent* evt) {
        if (SampleCode::TitleQ(*evt)) {
            SkString title;
            if (SampleCode::RequestTitle(fNext, &title)) {
                SampleCode::TitleR(evt, title.c_str());
                return true;
            }
            return false;
        }
        if (evt->isType(gIsTransitionQuery)) {
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }
    virtual bool onEvent(const SkEvent& evt) {
        if (evt.isType(gReplaceTransitionEvt)) {
            SkView* prev = fPrev;
            prev->ref();

            fPrev->detachFromParent();
            fPrev = (SkView*)SkEventSink::FindSink(evt.getFast32());
            (void)SampleView::SetUsePipe(fPrev, SkOSMenu::kOffState);
            //attach the new fPrev and call unref to balance the ref in onDraw
            this->attachChildToBack(fPrev)->unref();
            this->inval(NULL);

            SkASSERT(1 == prev->getRefCnt());
            prev->unref();
            return true;
        }
        if (evt.isType("transition-done")) {
            fNext->setLoc(0, 0);
            fNext->setClipToBounds(false);
            SkEvent* evt = new SkEvent(gReplaceTransitionEvt,
                                       this->getParent()->getSinkID());
            evt->setFast32(fNext->getSinkID());
            //increate ref count of fNext so it survives detachAllChildren
            fNext->ref();
            this->detachAllChildren();
            evt->post();
            return true;
        }
        return this->INHERITED::onEvent(evt);
    }
    virtual void onDrawBackground(SkCanvas* canvas) {}
    virtual void onDrawContent(SkCanvas* canvas) {
        if (fDone)
            return;

        if (is_overview(fNext) || is_overview(fPrev)) {
            fPipeState = SkOSMenu::kOffState;
        }

        SkScalar values[4];
        SkInterpolator::Result result = fInterp.timeToValues(SkTime::GetMSecs(), values);
        //SkDebugf("transition %x %d pipe:%d\n", this, result, fUsePipe);
        //SkDebugf("%f %f %f %f %d\n", values[0], values[1], values[2], values[3], result);
        if (SkInterpolator::kNormal_Result == result) {
            fPrev->setLocX(values[kPrevX]);
            fPrev->setLocY(values[kPrevY]);
            fNext->setLocX(values[kNextX]);
            fNext->setLocY(values[kNextY]);
            this->inval(NULL);
        }
        else {
            (new SkEvent("transition-done", this->getSinkID()))->post();
            fDone = true;
        }
    }

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

        fNext->setSize(this->width(), this->height());
        fPrev->setSize(this->width(), this->height());

        SkScalar lr = 0, ud = 0;
        if (fAnimationDirection & (kLeftDirection|kULDirection|kDLDirection))
            lr = this->width();
        if (fAnimationDirection & (kRightDirection|kURDirection|kDRDirection))
            lr = -this->width();
        if (fAnimationDirection & (kUpDirection|kULDirection|kURDirection))
            ud = this->height();
        if (fAnimationDirection & (kDownDirection|kDLDirection|kDRDirection))
            ud = -this->height();

        fBegin[kPrevX] = fBegin[kPrevY] = 0;
        fBegin[kNextX] = lr;
        fBegin[kNextY] = ud;
        fNext->setLocX(lr);
        fNext->setLocY(ud);

        if (is_transition(fPrev))
            lr = ud = 0;
        fEnd[kPrevX] = -lr;
        fEnd[kPrevY] = -ud;
        fEnd[kNextX] = fEnd[kNextY] = 0;
        SkScalar blend[] = { 0.8f, 0.0f,
                             0.0f, SK_Scalar1 };
        fInterp.setKeyFrame(0, SkTime::GetMSecs(), fBegin, blend);
        fInterp.setKeyFrame(1, SkTime::GetMSecs()+kDurationMS, fEnd, blend);
    }

private:
    enum {
        kPrevX = 0,
        kPrevY = 1,
        kNextX = 2,
        kNextY = 3
    };
    SkView* fPrev;
    SkView* fNext;
    bool    fDone;
    SkInterpolator fInterp;

    enum Direction{
        kUpDirection    = 1,
        kURDirection    = 1 << 1,
        kRightDirection = 1 << 2,
        kDRDirection    = 1 << 3,
        kDownDirection  = 1 << 4,
        kDLDirection    = 1 << 5,
        kLeftDirection  = 1 << 6,
        kULDirection    = 1 << 7
    };

    Direction fAnimationDirection;
    SkScalar fBegin[4];
    SkScalar fEnd[4];

    typedef SampleView INHERITED;
};

SkView* create_transition(SkView* prev, SkView* next, int direction) {
#ifdef SK_BUILD_FOR_ANDROID
    // Disable transitions for Android
    return next;
#else
    return SkNEW_ARGS(TransitionView, (prev, next, direction));
#endif
}