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

#include "gm.h"

#include "SkLightingShader.h"
#include "SkPoint3.h"
#include "SkShader.h"

// Create a hemispherical normal map
static SkBitmap make_hemi_normalmap(int texSize) {
    SkBitmap hemi;
    hemi.allocN32Pixels(texSize, texSize);

    sk_tool_utils::create_hemi_normal_map(&hemi, SkIRect::MakeWH(texSize, texSize));
    return hemi;
}

// Create a truncated pyramid normal map
static SkBitmap make_frustum_normalmap(int texSize) {
    SkBitmap frustum;
    frustum.allocN32Pixels(texSize, texSize);

    sk_tool_utils::create_frustum_normal_map(&frustum, SkIRect::MakeWH(texSize, texSize));
    return frustum;
}

// Create a tetrahedral normal map
static SkBitmap make_tetra_normalmap(int texSize) {
    SkBitmap tetra;
    tetra.allocN32Pixels(texSize, texSize);

    sk_tool_utils::create_tetra_normal_map(&tetra, SkIRect::MakeWH(texSize, texSize));
    return tetra;
}

namespace skiagm {

// This GM exercises lighting shaders.
class LightingShaderGM : public GM {
public:
    LightingShaderGM() {
        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));

        SkLightingShader::Lights::Builder builder;

        builder.add(SkLight(SkColor3f::Make(1.0f, 1.0f, 1.0f),
                            SkVector3::Make(1.0f, 0.0f, 0.0f)));
        builder.add(SkLight(SkColor3f::Make(0.2f, 0.2f, 0.2f)));

        fLights.reset(builder.finish());
    }

protected:
    enum NormalMap {
        kHemi_NormalMap,
        kFrustum_NormalMap,
        kTetra_NormalMap,

        kLast_NormalMap = kTetra_NormalMap
    };

    static const int kNormalMapCount = kLast_NormalMap+1;

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

    SkISize onISize() override {
        return SkISize::Make(kGMSize, kGMSize);
    }

    void onOnceBeforeDraw() override {
        fDiffuse = sk_tool_utils::create_checkerboard_bitmap(
                                                        kTexSize, kTexSize,
                                                        sk_tool_utils::color_to_565(0x0),
                                                        sk_tool_utils::color_to_565(0xFF804020),
                                                        8);

        fNormalMaps[kHemi_NormalMap]    = make_hemi_normalmap(kTexSize);
        fNormalMaps[kFrustum_NormalMap] = make_frustum_normalmap(kTexSize);
        fNormalMaps[kTetra_NormalMap]   = make_tetra_normalmap(kTexSize);
    }

    void drawRect(SkCanvas* canvas, const SkRect& r, NormalMap mapType) {

        SkRect bitmapBounds = SkRect::MakeIWH(fDiffuse.width(), fDiffuse.height());

        SkMatrix matrix;
        matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit);
    
        const SkMatrix& ctm = canvas->getTotalMatrix();

        // TODO: correctly pull out the pure rotation
        SkVector invNormRotation = { ctm[SkMatrix::kMScaleX], ctm[SkMatrix::kMSkewY] };

        SkAutoTUnref<SkShader> fShader(SkLightingShader::Create(
                                                        fDiffuse,
                                                        fNormalMaps[mapType],
                                                        fLights,
                                                        invNormRotation, &matrix, &matrix));

        SkPaint paint;
        paint.setShader(fShader);

        canvas->drawRect(r, paint);
    }

    void onDraw(SkCanvas* canvas) override {
        SkMatrix m;
        SkRect r;

        {
            r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize));
            this->drawRect(canvas, r, kHemi_NormalMap);

            canvas->save();
            m.setRotate(45.0f, r.centerX(), r.centerY());
            m.postTranslate(kGMSize/2.0f - kTexSize/2.0f, 0.0f);
            canvas->setMatrix(m);
            this->drawRect(canvas, r, kHemi_NormalMap);
            canvas->restore();
        }

        {
            r.offset(kGMSize - kTexSize, 0);
            this->drawRect(canvas, r, kFrustum_NormalMap);

            canvas->save();
            m.setRotate(45.0f, r.centerX(), r.centerY());
            m.postTranslate(0.0f, kGMSize/2.0f - kTexSize/2.0f);
            canvas->setMatrix(m);
            this->drawRect(canvas, r, kFrustum_NormalMap);
            canvas->restore();
        }

        {
            r.offset(0, kGMSize - kTexSize);
            this->drawRect(canvas, r, kTetra_NormalMap);

            canvas->save();
            m.setRotate(45.0f, r.centerX(), r.centerY());
            m.postTranslate(-kGMSize/2.0f + kTexSize/2.0f, 0.0f);
            canvas->setMatrix(m);
            this->drawRect(canvas, r, kTetra_NormalMap);
            canvas->restore();
        }

        {
            r.offset(kTexSize - kGMSize, 0);
            this->drawRect(canvas, r, kHemi_NormalMap);

            canvas->save();
            m.setRotate(45.0f, r.centerX(), r.centerY());
            m.postTranslate(0.0f, -kGMSize/2.0f + kTexSize/2.0f);
            canvas->setMatrix(m);
            this->drawRect(canvas, r, kHemi_NormalMap);
            canvas->restore();
        }
    }

private:
    static const int kTexSize = 128;
    static const int kGMSize  = 512;

    SkBitmap                fDiffuse;
    SkBitmap                fNormalMaps[kNormalMapCount];

    SkAutoTUnref<const SkLightingShader::Lights>  fLights;

    typedef GM INHERITED;
};

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

DEF_GM(return new LightingShaderGM;)
}
