
/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkPath.h"
#include "include/core/SkPoint3.h"
#include "include/core/SkRRect.h"
#include "include/utils/SkShadowUtils.h"
#include "tools/Resources.h"
#include "tools/viewer/Slide.h"

////////////////////////////////////////////////////////////////////////////
// Sample to compare the Material Design shadow reference to our results

class ShadowRefSlide : public Slide {
    SkPath         fRRectPath;
    sk_sp<SkImage> fReferenceImage;

    bool      fShowAmbient;
    bool      fShowSpot;
    bool      fUseAlt;
    bool      fShowObject;

public:
    ShadowRefSlide()
        : fShowAmbient(true)
        , fShowSpot(true)
        , fUseAlt(false)
        , fShowObject(true) {
        fName = "ShadowReference";
    }

    void load(SkScalar w, SkScalar h) override {
        fRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-130, -128.5, 130, 128.5), 4, 4));
        fReferenceImage = GetResourceAsImage("images/shadowreference.png");
    }

    bool onChar(SkUnichar uni) override {
            bool handled = false;
            switch (uni) {
                case 'W':
                    fShowAmbient = !fShowAmbient;
                    handled = true;
                    break;
                case 'S':
                    fShowSpot = !fShowSpot;
                    handled = true;
                    break;
                case 'T':
                    fUseAlt = !fUseAlt;
                    handled = true;
                    break;
                case 'O':
                    fShowObject = !fShowObject;
                    handled = true;
                    break;
                default:
                    break;
            }
            if (handled) {
                return true;
            }
            return false;
    }

    void draw(SkCanvas* canvas) override {
        this->drawBG(canvas);
        const SkScalar kDP = 4;  // the reference image is 4x bigger than it is displayed on
                                 // on the web page, so we need to reflect that here and
                                 // multiply the heights and light params accordingly
        const SkScalar kLightWidth = kDP*400;
        const SkScalar kAmbientAlpha = 0.03f;
        const SkScalar kSpotAlpha = 0.35f;

        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setColor(SK_ColorWHITE);

        SkPoint3 lightPos = { 175, -800, kDP * 600 };
        SkScalar xPos = 230;
        SkScalar yPos = 254.25f;
        SkRect clipRect = SkRect::MakeXYWH(45, 75, 122, 250);
        SkPoint clipDelta = SkPoint::Make(320, 0);
        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, kDP * 2);

        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

        lightPos.fX += 320;
        xPos += 320;
        clipRect.offset(clipDelta);
        zPlaneParams.fZ = kDP * 3;
        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

        lightPos.fX += 320;
        xPos += 320;
        clipRect.offset(clipDelta);
        zPlaneParams.fZ = kDP * 4;
        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

        lightPos.fX += 320;
        xPos += 320;
        clipRect.offset(clipDelta);
        zPlaneParams.fZ = kDP * 6;
        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

        lightPos.fX += 320;
        xPos += 320;
        clipRect.offset(clipDelta);
        zPlaneParams.fZ = kDP * 8;
        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

        lightPos.fX += 320;
        xPos += 320;
        clipRect.offset(clipDelta);
        zPlaneParams.fZ = kDP * 16;
        canvas->save();
        canvas->clipRect(clipRect);
        canvas->translate(xPos, yPos);
        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
                               lightPos, kLightWidth, kSpotAlpha);
        canvas->restore();

    }

private:
    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFFFFFFF);
        canvas->drawImage(fReferenceImage, 10, 30);
    }

    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
                          const SkPoint3& zPlaneParams,
                          const SkPaint& paint, SkScalar ambientAlpha,
                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
        if (!fShowAmbient) {
            ambientAlpha = 0;
        }
        if (!fShowSpot) {
            spotAlpha = 0;
        }
        uint32_t flags = 0;
        if (fUseAlt) {
            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
        }

        SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 0, 0, 0);
        SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 0);
        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
                                  lightPos, lightWidth,
                                  ambientColor, spotColor, flags);

        if (fShowObject) {
            canvas->drawPath(path, paint);
        } else {
            SkPaint strokePaint;

            strokePaint.setColor(paint.getColor());
            strokePaint.setStyle(SkPaint::kStroke_Style);

            canvas->drawPath(path, strokePaint);
        }
    }
};

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

DEF_SLIDE( return new ShadowRefSlide(); )
