
/*
 * 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 "SkCornerPathEffect.h"
#include "SkCullPoints.h"
#include "SkGradientShader.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkRandom.h"

static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump) {
    SkVector    tang;

    tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump);

    path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY,
                 SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX);
    path->lineTo(pts[1]);
}

static void subdivide(SkPath* path, SkScalar bump) {
    SkPath::Iter    iter(*path, false);
    SkPoint         pts[4];
    SkPath          tmp;

    for (;;)
        switch (iter.next(pts)) {
        case SkPath::kMove_Verb:
            tmp.moveTo(pts[0]);
            break;
        case SkPath::kLine_Verb:
            addbump(&tmp, pts, bump);
            bump = -bump;
            break;
        case SkPath::kDone_Verb:
            goto FINISH;
        default:
            break;
        }

FINISH:
    path->swap(tmp);
}

static SkIPoint* getpts(const SkPath& path, int* count) {
    SkPoint     pts[4];
    int         n = 1;
    SkIPoint*   array;

    {
        SkPath::Iter    iter(path, false);
        for (;;)
            switch (iter.next(pts)) {
            case SkPath::kLine_Verb:
                n += 1;
                break;
            case SkPath::kDone_Verb:
                goto FINISHED;
            default:
                break;
            }
    }

FINISHED:
    array = new SkIPoint[n];
    n = 0;

    {
        SkPath::Iter    iter(path, false);
        for (;;)
            switch (iter.next(pts)) {
            case SkPath::kMove_Verb:
                array[n++].set(SkScalarRoundToInt(pts[0].fX),
                               SkScalarRoundToInt(pts[0].fY));
                break;
            case SkPath::kLine_Verb:
                array[n++].set(SkScalarRoundToInt(pts[1].fX),
                               SkScalarRoundToInt(pts[1].fY));
                break;
            case SkPath::kDone_Verb:
                goto FINISHED2;
            default:
                break;
            }
    }

FINISHED2:
    *count = n;
    return array;
}

static SkScalar nextScalarRange(SkRandom& rand, SkScalar min, SkScalar max) {
    return min + SkScalarMul(rand.nextUScalar1(), max - min);
}

class CullView : public SampleView {
public:
    CullView() {
        fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));

        SkRandom    rand;

        for (int i = 0; i < 50; i++) {
            SkScalar x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2);
            SkScalar y = nextScalarRange(rand, -fClip.height()*1, fClip.height()*2);
            if (i == 0)
                fPath.moveTo(x, y);
            else
                fPath.lineTo(x, y);
        }

        SkScalar bump = fClip.width()/8;
        subdivide(&fPath, bump);
        subdivide(&fPath, bump);
        subdivide(&fPath, bump);
        fPoints = getpts(fPath, &fPtCount);

        this->setBGColor(0xFFDDDDDD);
    }

    virtual ~CullView() {
        delete[] fPoints;
    }

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

    virtual void onDrawContent(SkCanvas* canvas) {
        SkAutoCanvasRestore ar(canvas, true);

        canvas->translate(  SkScalarHalf(this->width() - fClip.width()),
                            SkScalarHalf(this->height() - fClip.height()));

   //     canvas->scale(SK_Scalar1*3, SK_Scalar1*3, 0, 0);

        SkPaint paint;

    //    paint.setAntiAliasOn(true);
        paint.setStyle(SkPaint::kStroke_Style);

        canvas->drawRect(fClip, paint);

#if 1
        paint.setColor(0xFF555555);
        paint.setStrokeWidth(SkIntToScalar(2));
//        paint.setPathEffect(new SkCornerPathEffect(SkIntToScalar(30)))->unref();
        canvas->drawPath(fPath, paint);
//        paint.setPathEffect(nullptr);
#endif

        SkPath  tmp;
        SkIRect iclip;
        fClip.round(&iclip);

        SkCullPointsPath    cpp(iclip, &tmp);

        cpp.moveTo(fPoints[0].fX, fPoints[0].fY);
        for (int i = 0; i < fPtCount; i++)
            cpp.lineTo(fPoints[i].fX, fPoints[i].fY);

        paint.setColor(SK_ColorRED);
        paint.setStrokeWidth(SkIntToScalar(3));
        paint.setStrokeJoin(SkPaint::kRound_Join);
        canvas->drawPath(tmp, paint);

        this->inval(nullptr);
    }

private:
    SkRect      fClip;
    SkIPoint*   fPoints;
    SkPath      fPath;
    int         fPtCount;

    typedef SampleView INHERITED;
};

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

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