
/*
 * 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 "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkGraphics.h"
#include "SkRandom.h"

static void test_clearonlayers(SkCanvas* canvas) {
    SkCanvas& c = *canvas;

    SkPaint paint;
    paint.setColor(SK_ColorBLUE);
    paint.setStyle(SkPaint::kStrokeAndFill_Style);
    SkRect rect = SkRect::MakeXYWH(25, 25, 50, 50);
    c.drawRect(rect, paint);

    c.clipRect(rect);

    c.saveLayer(nullptr, nullptr);
    rect = SkRect::MakeXYWH(50, 10, 40, 80);
    c.clipRect(rect, SkRegion::kUnion_Op);

    rect = SkRect::MakeXYWH(50, 0, 50, 100);
    // You might draw something here, but it's not necessary.
    // paint.setColor(SK_ColorRED);
    // c.drawRect(rect, paint);
    paint.setXfermodeMode(SkXfermode::kClear_Mode);
    c.drawRect(rect, paint);
    c.restore();
}

static void test_strokerect(SkCanvas* canvas, const SkRect& r) {
    SkPaint p;

    p.setAntiAlias(true);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(4);

    canvas->drawRect(r, p);

    SkPath path;
    SkRect r2(r);
    r2.offset(18, 0);
    path.addRect(r2);

    canvas->drawPath(path, p);
}

static void test_strokerect(SkCanvas* canvas) {
    canvas->drawColor(SK_ColorWHITE);

    SkRect r;

    r.set(10, 10, 14, 14);
    r.offset(0.25f, 0.3333f);
    test_strokerect(canvas, r);
    canvas->translate(0, 20);

    r.set(10, 10, 14.5f, 14.5f);
    r.offset(0.25f, 0.3333f);
    test_strokerect(canvas, r);
    canvas->translate(0, 20);

    r.set(10, 10, 14.5f, 20);
    r.offset(0.25f, 0.3333f);
    test_strokerect(canvas, r);
    canvas->translate(0, 20);

    r.set(10, 10, 20, 14.5f);
    r.offset(0.25f, 0.3333f);
    test_strokerect(canvas, r);
    canvas->translate(0, 20);

    r.set(10, 10, 20, 20);
    r.offset(0.25f, 0.3333f);
    test_strokerect(canvas, r);
    canvas->translate(0, 20);

}

class Draw : public SkRefCnt {
public:
    Draw() : fFlags(0) {}

    enum Flags {
        kSelected_Flag  = 1 << 0
    };
    int getFlags() const { return fFlags; }
    void setFlags(int flags);

    bool isSelected() const { return SkToBool(fFlags & kSelected_Flag); }
    void setSelected(bool pred) {
        if (pred) {
            fFlags |= kSelected_Flag;
        } else {
            fFlags &= ~kSelected_Flag;
        }
    }

    void draw(SkCanvas* canvas) {
        int sc = canvas->save();
        this->onDraw(canvas);
        canvas->restoreToCount(sc);

        if (this->isSelected()) {
            this->drawSelection(canvas);
        }
    }

    void drawSelection(SkCanvas* canvas) {
        int sc = canvas->save();
        this->onDrawSelection(canvas);
        canvas->restoreToCount(sc);
    }

    void getBounds(SkRect* bounds) {
        this->onGetBounds(bounds);
    }

    bool hitTest(SkScalar x, SkScalar y) {
        return this->onHitTest(x, y);
    }

    void offset(SkScalar dx, SkScalar dy) {
        if (dx || dy) {
            this->onOffset(dx, dy);
        }
    }

protected:
    virtual void onDraw(SkCanvas*) = 0;
    virtual void onGetBounds(SkRect*) = 0;
    virtual void onOffset(SkScalar dx, SkScalar dy) = 0;
    virtual void onDrawSelection(SkCanvas* canvas) {
        SkRect r;
        this->getBounds(&r);
        SkPaint paint;
        SkPoint pts[4];
        r.toQuad(pts);
        paint.setStrokeWidth(SkIntToScalar(10));
        paint.setColor(0x80FF8844);
        paint.setStrokeCap(SkPaint::kRound_Cap);
        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, pts, paint);
    }
    virtual bool onHitTest(SkScalar x, SkScalar y) {
        SkRect bounds;
        this->getBounds(&bounds);
        return bounds.contains(x, y);
    }

private:
    int fFlags;
};

class RDraw : public Draw {
public:
    enum Style {
        kRect_Style,
        kOval_Style,
        kRRect_Style,
        kFrame_Style
    };

    RDraw(const SkRect& r, Style s) : fRect(r), fStyle(s) {}

    void setRect(const SkRect& r) {
        fRect = r;
    }

    void setPaint(const SkPaint& p) {
        fPaint = p;
    }

protected:
    virtual void onDraw(SkCanvas* canvas) {
        switch (fStyle) {
            case kRect_Style:
                canvas->drawRect(fRect, fPaint);
                break;
            case kOval_Style:
                canvas->drawOval(fRect, fPaint);
                break;
            case kRRect_Style: {
                SkScalar rx = fRect.width() / 5;
                SkScalar ry = fRect.height() / 5;
                if (rx < ry) {
                    ry = rx;
                } else {
                    rx = ry;
                }
                canvas->drawRoundRect(fRect, rx, ry, fPaint);
                break;
            }
            case kFrame_Style: {
                SkPath path;
                path.addOval(fRect, SkPath::kCW_Direction);
                SkRect r = fRect;
                r.inset(fRect.width()/6, 0);
                path.addOval(r, SkPath::kCCW_Direction);
                canvas->drawPath(path, fPaint);
                break;
            }
        }
    }

    virtual void onGetBounds(SkRect* bounds) {
        *bounds = fRect;
    }

    virtual void onOffset(SkScalar dx, SkScalar dy) {
        fRect.offset(dx, dy);
    }

private:
    SkRect  fRect;
    SkPaint fPaint;
    Style   fStyle;
};

class DrawFactory {
public:
    DrawFactory() {
        fPaint.setAntiAlias(true);
    }

    const SkPaint& getPaint() const { return fPaint; }

    void setPaint(const SkPaint& p) {
        fPaint = p;
    }

    virtual Draw* create(const SkPoint&, const SkPoint&) = 0;

private:
    SkPaint fPaint;
};

class RectFactory : public DrawFactory {
public:
    virtual Draw* create(const SkPoint& p0, const SkPoint& p1) {
        SkRect r;
        r.set(p0.x(), p0.y(), p1.x(), p1.y());
        r.sort();

//        RDraw* d = new RDraw(r, RDraw::kRRect_Style);
        RDraw* d = new RDraw(r, RDraw::kFrame_Style);
        d->setPaint(this->getPaint());
        return d;
    }
};

class DrawView : public SkView {
    Draw*           fDraw;
    DrawFactory*    fFactory;
    SkRandom        fRand;
    SkTDArray<Draw*> fList;

public:
    DrawView() : fDraw(nullptr) {
        fFactory = new RectFactory;
    }

    virtual ~DrawView() {
        fList.unrefAll();
        SkSafeUnref(fDraw);
        delete fFactory;
    }

    Draw* setDraw(Draw* d) {
        SkRefCnt_SafeAssign(fDraw, d);
        return d;
    }

    SkColor randColor() {
        return (SkColor)fRand.nextU() | 0xFF000000;
    }

    Draw* hitTestList(SkScalar x, SkScalar y) const {
        Draw** first = fList.begin();
        for (Draw** iter = fList.end(); iter > first;) {
            --iter;
            if ((*iter)->hitTest(x, y)) {
                return *iter;
            }
        }
        return nullptr;
    }

protected:
    // overrides from SkEventSink
    virtual bool onQuery(SkEvent* evt) {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "Draw");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
//        canvas->drawColor(SK_ColorWHITE);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);
        test_clearonlayers(canvas); return;
     //   test_strokerect(canvas); return;

        for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
            (*iter)->draw(canvas);
        }
        if (fDraw) {
            fDraw->draw(canvas);
        }
    }

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
            (*iter)->setSelected(false);
        }

        Click* c = new Click(this);
        Draw* d = this->hitTestList(x, y);
        if (d) {
            d->setSelected(true);
            c->setType("dragger");
        } else {
            c->setType("maker");
        }
        return c;
    }

    virtual bool onClick(Click* click) {
        if (Click::kUp_State == click->fState) {
            if (click->isType("maker")) {
                if (SkPoint::Distance(click->fOrig, click->fCurr) > SkIntToScalar(3)) {
                    *fList.append() = fDraw;
                } else {
                    fDraw->unref();
                }
                fDraw = nullptr;
            }
            return true;
        }

        if (Click::kDown_State == click->fState) {
            SkPaint p = fFactory->getPaint();
            p.setColor(this->randColor());
            fFactory->setPaint(p);
        }

        if (click->isType("maker")) {
            this->setDraw(fFactory->create(click->fOrig, click->fCurr))->unref();
        } else if (click->isType("dragger")) {
            for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
                if ((*iter)->isSelected()) {
                    (*iter)->offset(click->fCurr.x() - click->fPrev.x(),
                                    click->fCurr.y() - click->fPrev.y());
                }
            }
        }
        this->inval(nullptr);
        return true;
    }

private:
    typedef SkView INHERITED;
};

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

static SkView* MyFactory() { return new DrawView; }
static SkViewRegister reg(MyFactory);
