/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPathBuilder.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"

namespace skiagm {

// This GM tests a grab-bag of non-closed paths. All these paths look like
// closed rects, but they don't call path.close(). Depending on the stroke
// settings these slightly different paths give widely different results.
class NonClosedPathsGM: public GM {
public:
    NonClosedPathsGM() {}

    enum ClosureType {
        TotallyNonClosed,  // The last point doesn't coincide with the first one in the contour.
                           // The path looks not closed at all.

        FakeCloseCorner,   // The last point coincides with the first one at a corner.
                           // The path looks closed, but final rendering has 2 ends with cap.

        FakeCloseMiddle,   // The last point coincides with the first one in the middle of a line.
                           // The path looks closed, and the final rendering looks closed too.

        kClosureTypeCount
    };

protected:

    SkString onShortName() override {
        return SkString("nonclosedpaths");
    }

    // 12 * 18 + 3 cases, every case is 100 * 100 pixels.
    SkISize onISize() override {
        return SkISize::Make(1220, 1920);
    }

    // Use rect-like geometry for non-closed path, for right angles make it
    // easier to show the visual difference of lineCap and lineJoin.
    static SkPath MakePath(ClosureType type) {
        SkPathBuilder path;
        if (FakeCloseMiddle == type) {
            path.moveTo(30, 50);
            path.lineTo(30, 30);
        } else {
            path.moveTo(30, 30);
        }
        path.lineTo(70, 30);
        path.lineTo(70, 70);
        path.lineTo(30, 70);
        path.lineTo(30, 50);
        if (FakeCloseCorner == type) {
            path.lineTo(30, 30);
        }
        return path.detach();
    }

    // Set the location for the current test on the canvas
    static void SetLocation(SkCanvas* canvas, int counter, int lineNum) {
        SkScalar x = SK_Scalar1 * 100 * (counter % lineNum) + 10 + SK_Scalar1 / 4;
        SkScalar y = SK_Scalar1 * 100 * (counter / lineNum) + 10 + 3 * SK_Scalar1 / 4;
        canvas->translate(x, y);
    }

    void onDraw(SkCanvas* canvas) override {
        // Stroke widths are:
        // 0(may use hairline rendering), 10(common case for stroke-style)
        // 40 and 50(>= geometry width/height, make the contour filled in fact)
        constexpr int kStrokeWidth[] = {0, 10, 40, 50};
        int numWidths = SK_ARRAY_COUNT(kStrokeWidth);

        constexpr SkPaint::Style kStyle[] = {
            SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style
        };

        constexpr SkPaint::Cap kCap[] = {
            SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap
        };

        constexpr SkPaint::Join kJoin[] = {
            SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
        };

        constexpr ClosureType kType[] = {
            TotallyNonClosed, FakeCloseCorner, FakeCloseMiddle
        };

        int counter = 0;
        SkPaint paint;
        paint.setAntiAlias(true);

        // For stroke style painter and fill-and-stroke style painter
        for (size_t type = 0; type < kClosureTypeCount; ++type) {
            for (size_t style = 0; style < SK_ARRAY_COUNT(kStyle); ++style) {
                for (size_t cap = 0; cap < SK_ARRAY_COUNT(kCap); ++cap) {
                    for (size_t join = 0; join < SK_ARRAY_COUNT(kJoin); ++join) {
                        for (int width = 0; width < numWidths; ++width) {
                            canvas->save();
                            SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);

                            SkPath path = MakePath(kType[type]);

                            paint.setStyle(kStyle[style]);
                            paint.setStrokeCap(kCap[cap]);
                            paint.setStrokeJoin(kJoin[join]);
                            paint.setStrokeWidth(SkIntToScalar(kStrokeWidth[width]));

                            canvas->drawPath(path, paint);
                            canvas->restore();
                            ++counter;
                        }
                    }
                }
            }
        }

        // For fill style painter
        paint.setStyle(SkPaint::kFill_Style);
        for (size_t type = 0; type < kClosureTypeCount; ++type) {
            canvas->save();
            SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);

            SkPath path = MakePath(kType[type]);

            canvas->drawPath(path, paint);
            canvas->restore();
            ++counter;
        }
    }

private:
    using INHERITED = GM;
};

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

DEF_GM(return new NonClosedPathsGM;)

}  // namespace skiagm