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

#ifdef SK_BUILD_FOR_WIN
// windows doesn't have roundf
inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
#endif

#ifdef SK_DEBUG
static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
                     size_t count, int32_t runs[]) {
    SkIRect r;
    r.set(left, top, right, bottom);
    
    rgn->debugSetRuns(runs, count);
    SkASSERT(rgn->getBounds() == r);
}

static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
    static int32_t dataA[] = {
        0x00000001, 0x000001dd,
        0x00000001, 0x0000000c, 0x0000000d, 0x00000025,
        0x7fffffff, 0x000001de, 0x00000001, 0x00000025,
        0x7fffffff, 0x000004b3, 0x00000001, 0x00000026,
        0x7fffffff, 0x000004b4, 0x0000000c, 0x00000026,
        0x7fffffff, 0x00000579, 0x00000000, 0x0000013a,
        0x7fffffff, 0x000005d8, 0x00000000, 0x0000013b,
        0x7fffffff, 0x7fffffff
    };
    make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);

    static int32_t dataB[] = {
        0x000000b6, 0x000000c4,
        0x000000a1, 0x000000f0, 0x7fffffff, 0x000000d6,
        0x7fffffff, 0x000000e4, 0x00000070, 0x00000079,
        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000e6,
        0x7fffffff, 0x000000f4, 0x00000070, 0x00000079,
        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000f6,
        0x7fffffff, 0x00000104, 0x000000a1, 0x000000b0,
        0x7fffffff, 0x7fffffff
    };
    make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
    
    rc->op(*ra, *rb, SkRegion::kUnion_Op);
}
#endif

static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
    dst->fLeft = (int)::roundf(src.fLeft * scale);
    dst->fTop = (int)::roundf(src.fTop * scale);
    dst->fRight = (int)::roundf(src.fRight * scale);
    dst->fBottom = (int)::roundf(src.fBottom * scale);
}

static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
    SkRegion tmp;
    SkRegion::Iterator iter(src);

    for (; !iter.done(); iter.next()) {
        SkIRect r;
        scale_rect(&r, iter.rect(), scale);
        tmp.op(r, SkRegion::kUnion_Op);
    }
    dst->swap(tmp);
}

static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
                      const SkPaint& paint) {
    SkRegion scaled;
    scale_rgn(&scaled, rgn, 0.5f);
    
    SkRegion::Iterator  iter(rgn);

    for (; !iter.done(); iter.next())
    {
        SkRect    r;
        r.set(iter.rect());
        canvas->drawRect(r, paint);
    }
}

class RegionView : public SampleView {
public:
	RegionView() {
        fBase.set(100, 100, 150, 150);
        fRect = fBase;
        fRect.inset(5, 5);
        fRect.offset(25, 25);
        this->setBGColor(0xFFDDDDDD);
    }

    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
        rgn->setRect(fBase);
        SkIRect r = fBase;
        r.offset(75, 20);
        rgn->op(r, SkRegion::kUnion_Op);
        rgn->op(fRect, op);
    }


protected:
    // overrides from SkEventSink
    virtual bool onQuery(SkEvent* evt) {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "Regions");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }
    
    void drawOrig(SkCanvas* canvas, bool bg) {
        SkRect      r;
        SkPaint     paint;
        
        paint.setStyle(SkPaint::kStroke_Style);
        if (bg)
            paint.setColor(0xFFBBBBBB);
        
        r.set(fBase);
        canvas->drawRect(r, paint);
        r.set(fRect);
        canvas->drawRect(r, paint);
    }
    
    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
        SkRegion    rgn;

        this->build_rgn(&rgn, op);
        
        {
            SkRegion tmp, tmp2(rgn);
            
            tmp = tmp2;
            tmp.translate(5, -3);
            
            {
                char    buffer[1000];
                size_t  size = tmp.flatten(NULL);
                SkASSERT(size <= sizeof(buffer));
                size_t  size2 = tmp.flatten(buffer);
                SkASSERT(size == size2);
                
                SkRegion    tmp3;
                size2 = tmp3.unflatten(buffer);
                SkASSERT(size == size2);
                
                SkASSERT(tmp3 == tmp);
            }

            rgn.translate(20, 30, &tmp);
            SkASSERT(rgn.isEmpty() || tmp != rgn);
            tmp.translate(-20, -30);
            SkASSERT(tmp == rgn);
        }

        this->drawOrig(canvas, true);

        SkPaint paint;
        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
        paint_rgn(canvas, rgn, paint);

        paint.setStyle(SkPaint::kStroke_Style);
        paint.setColor(color);
        paint_rgn(canvas, rgn, paint);
    }
    
    void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
        SkRegion    rgn;
        SkPath      path;

        this->build_rgn(&rgn, op);
        rgn.getBoundaryPath(&path);

        this->drawOrig(canvas, true);

        SkPaint paint;

        paint.setStyle(SkPaint::kFill_Style);
        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
        canvas->drawPath(path, paint);
        paint.setColor(color);
        paint.setStyle(SkPaint::kStroke_Style);
        canvas->drawPath(path, paint);
    }
    
    virtual void onDrawContent(SkCanvas* canvas) {
#ifdef SK_DEBUG
        if (true) {
            SkRegion a, b, c;
            test_union_bug_1505668(&a, &b, &c);
            
            if (false) {    // draw the result of the test
                SkPaint paint;
                
                canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
                paint.setColor(SK_ColorRED);
                paint_rgn(canvas, a, paint);
                paint.setColor(0x800000FF);
                paint_rgn(canvas, b, paint);
                paint.setColor(SK_ColorBLACK);
                paint.setStyle(SkPaint::kStroke_Style);
             //   paint_rgn(canvas, c, paint);
                return;
            }
        }
#endif

        static const struct {
            SkColor         fColor;
            const char*     fName;
            SkRegion::Op    fOp;
        } gOps[] = {
            { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
            { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
            { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
            { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
        };

        SkPaint textPaint;
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(SK_Scalar1*24);

        this->drawOrig(canvas, false);
        canvas->save();
            canvas->translate(SkIntToScalar(200), 0);
            this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
        canvas->restore();
        
        canvas->translate(0, SkIntToScalar(200));

        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);

            this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);

            canvas->save();
            canvas->translate(0, SkIntToScalar(200));
            this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
            canvas->restore();
            
            canvas->translate(SkIntToScalar(200), 0);
        }
    }
    
    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
    }
    
    virtual bool onClick(Click* click) {
        fRect.offset(click->fICurr.fX - click->fIPrev.fX,
                     click->fICurr.fY - click->fIPrev.fY);
        this->inval(NULL);
        return true;
    }
    
private:
    SkIRect    fBase, fRect;
    
    typedef SampleView INHERITED;
};

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

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