/*
 * Copyright 2012 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 "SkRandom.h"
#include "SkRRect.h"
#include "SkColorPriv.h"

static void rotateAbout(SkCanvas* canvas, SkScalar degrees,
                        SkScalar cx, SkScalar cy) {
    canvas->translate(cx, cy);
    canvas->rotate(degrees);
    canvas->translate(-cx, -cy);
}

class RotateCirclesView : public SampleView {
public:
    RotateCirclesView() {
        this->setBGColor(SK_ColorLTGRAY);

        fAngle = 0;
    }

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

    virtual void onDrawContent(SkCanvas* canvas) {
        SkRandom rand;
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStrokeWidth(20);

        SkScalar cx = 240;
        SkScalar cy = 240;
        SkScalar DX = 240 * 2;
        SkColor color = 0;

        float scale = 1;
        float sign = 0.3f;
        for (SkScalar rad = 200; rad >= 20; rad -= 15) {
            sign = -sign;
            scale += 0.2f;

            paint.setColor(rand.nextU());
            paint.setAlpha(0xFF);
            color = ~color;

            paint.setStyle(SkPaint::kFill_Style);

            canvas->save();
            rotateAbout(canvas, fAngle * scale * sign, cx, cy);
            canvas->drawCircle(cx, cy, rad, paint);
            canvas->restore();

            paint.setStyle(SkPaint::kStroke_Style);
            paint.setStrokeWidth(rad*2);

            canvas->save();
            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy);
            canvas->drawCircle(cx + DX, cy, 10, paint);
            canvas->restore();

            canvas->save();
            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy + DX);
            canvas->drawCircle(cx + DX, cy + DX, 10, paint);
            canvas->restore();

        }

        fAngle = (fAngle + 1) % 360;
        this->inval(NULL);
    }

private:
    int fAngle;
    typedef SkView INHERITED;
};

class TestCirclesView : public SampleView {
public:
    TestCirclesView() {
    }

protected:
    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "RotateCircles2");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }

    void draw_real_circle(SkCanvas* canvas, SkScalar radius) {
        int w = SkScalarCeilToInt(radius * 2);
        int h = w;

        SkBitmap bm;
        bm.allocN32Pixels(w, h);
        bm.eraseColor(0);

        SkAutoLockPixels alp(bm);

        SkScalar cx = radius;
        SkScalar cy = radius;
        for (int y = 0; y < h; y += 1) {
            for (int x = 0; x < w; x += 1) {
                float d = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy));
                if (d <= radius) {
                    *bm.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0);
                }
            }
        }

        canvas->drawBitmap(bm, 0, 0, NULL);
    }

    virtual void onDrawContent(SkCanvas* canvas) {
        SkScalar radius = 256;
        canvas->translate(10, 10);

        draw_real_circle(canvas, radius);

        SkPaint paint;
        paint.setAntiAlias(true);

        paint.setColor(0x80FF0000);
        canvas->drawCircle(radius, radius, radius, paint);

        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(radius);
        paint.setColor(0x8000FF00);
        canvas->drawCircle(radius, radius, radius/2, paint);
    }

private:
    typedef SkView INHERITED;
};

static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
    const SkScalar TOL = 7;
    return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
}

static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
    SkPath::RawIter iter(path);
    SkPoint pts[4];
    SkPath::Verb verb;

    int count = 0;
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
        switch (verb) {
            case SkPath::kMove_Verb:
            case SkPath::kLine_Verb:
            case SkPath::kQuad_Verb:
            case SkPath::kCubic_Verb:
                storage[count++] = pts[0];
                break;
            default:
                break;
        }
    }
    return count;
}

#include "SkPathMeasure.h"

class TestStrokeView : public SampleView {
    enum {
        SKELETON_COLOR = 0xFF0000FF,
        WIREFRAME_COLOR = 0x80FF0000
    };

    enum {
        kCount = 9
    };
    SkPoint fPts[kCount];
    SkScalar fWidth, fDWidth;
public:
    TestStrokeView() {
        this->setBGColor(SK_ColorLTGRAY);

        fPts[0].set(50, 200);
        fPts[1].set(50, 100);
        fPts[2].set(150, 50);
        fPts[3].set(300, 50);

        fPts[4].set(350, 200);
        fPts[5].set(350, 100);
        fPts[6].set(450, 50);

        fPts[7].set(200, 200);
        fPts[8].set(400, 400);

        fWidth = 50;
        fDWidth = 0.25f;
    }

protected:
    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "RotateCircles3");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }

    void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
                     bool show_lines) {
        SkPaint paint;
        paint.setColor(color);
        paint.setAlpha(0x80);

        int n = path.countPoints();
        SkAutoSTArray<32, SkPoint> pts(n);
        if (show_lines) {
            path.getPoints(pts.get(), n);
            canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts.get(), paint);
        } else {
            n = getOnCurvePoints(path, pts.get());
        }
        paint.setStrokeWidth(5);
        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
    }

    void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
                   SkColor color) {
        const SkScalar radius = width / 2;

        SkPathMeasure meas(path, false);
        SkScalar total = meas.getLength();

        SkScalar delta = 8;
        SkPaint paint;
        paint.setColor(color);

        SkPoint pos, tan;
        for (SkScalar dist = 0; dist <= total; dist += delta) {
            if (meas.getPosTan(dist, &pos, &tan)) {
                tan.scale(radius);
                tan.rotateCCW();
                canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
                                 pos.x() - tan.x(), pos.y() - tan.y(), paint);
            }
        }
    }

    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width) {
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);

        paint.setColor(SKELETON_COLOR);
        canvas->drawPath(path, paint);
        draw_points(canvas, path, SKELETON_COLOR, true);

        draw_ribs(canvas, path, width, 0xFF00FF00);

        SkPath fill;

        SkPaint p;
        p.setStyle(SkPaint::kStroke_Style);
        p.setStrokeWidth(width);
        p.getFillPath(path, &fill);

        paint.setColor(WIREFRAME_COLOR);
        canvas->drawPath(fill, paint);
        draw_points(canvas, fill, WIREFRAME_COLOR, false);
    }

    virtual void onDrawContent(SkCanvas* canvas) {
        SkPath path;
        SkScalar width = fWidth;

        path.moveTo(fPts[0]);
        path.cubicTo(fPts[1], fPts[2], fPts[3]);
        draw_stroke(canvas, path, width);

        path.reset();
        path.moveTo(fPts[4]);
        path.quadTo(fPts[5], fPts[6]);
        draw_stroke(canvas, path, width);

        SkScalar rad = 32;
        SkRect r;
        r.set(&fPts[7], 2);
        path.reset();
        SkRRect rr;
        rr.setRectXY(r, rad, rad);
        path.addRRect(rr);
        draw_stroke(canvas, path, width);

        path.reset();
        SkRRect rr2;
        rr.inset(width/2, width/2, &rr2);
        path.addRRect(rr2, SkPath::kCCW_Direction);
        rr.inset(-width/2, -width/2, &rr2);
        path.addRRect(rr2, SkPath::kCW_Direction);
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setColor(0x40FF8844);
        canvas->drawPath(path, paint);

        fWidth += fDWidth;
        if (fDWidth > 0 && fWidth > 100) {
            fDWidth = -fDWidth;
        } else if (fDWidth < 0 && fWidth < 10) {
            fDWidth = -fDWidth;
        }
        this->inval(NULL);
    }

    class MyClick : public Click {
    public:
        int fIndex;
        MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
    };

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
                                              unsigned modi) SK_OVERRIDE {
        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
            if (hittest(fPts[i], x, y)) {
                return new MyClick(this, (int)i);
            }
        }
        return this->INHERITED::onFindClickHandler(x, y, modi);
    }

    virtual bool onClick(Click* click) {
        int index = ((MyClick*)click)->fIndex;
        fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
                           SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
        this->inval(NULL);
        return true;
    }

private:
    typedef SkView INHERITED;
};

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

static SkView* F0() { return new RotateCirclesView; }
static SkViewRegister gR0(F0);
static SkView* F1() { return new TestCirclesView; }
static SkViewRegister gR1(F1);
static SkView* F2() { return new TestStrokeView; }
static SkViewRegister gR2(F2);