/*
 * 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 "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkHighContrastFilter.h"
#include "tools/ToolUtils.h"
#include "tools/fonts/FontToolUtils.h"

#include <stdio.h>
#include <string.h>

using InvertStyle = SkHighContrastConfig::InvertStyle;

static SkScalar kSize   = 200;
static SkColor  kColor1 = SkColorSetARGB(0xff, 0xff, 0xff, 0);
static SkColor  kColor2 = SkColorSetARGB(0xff, 0x82, 0xff, 0);

static void draw_label(SkCanvas* canvas, const SkHighContrastConfig& config) {
    char labelBuffer[256];
    const char* invertStr =
        (config.fInvertStyle == InvertStyle::kInvertBrightness ?
             "InvBright" :
            (config.fInvertStyle == InvertStyle::kInvertLightness ?
                 "InvLight" : "NoInvert"));

    snprintf(labelBuffer, sizeof(labelBuffer), "%s%s contrast=%.1f",
             config.fGrayscale ? "Gray " : "",
             invertStr,
             config.fContrast);

    SkFont font = ToolUtils::DefaultPortableFont();
    font.setSize(0.075f);
    font.setEdging(SkFont::Edging::kAntiAlias);

    size_t len = strlen(labelBuffer);

    SkScalar width = font.measureText(labelBuffer, len, SkTextEncoding::kUTF8);
    canvas->drawSimpleText(labelBuffer, len, SkTextEncoding::kUTF8, 0.5f - width / 2, 0.16f, font, SkPaint());
}

static void draw_scene(SkCanvas* canvas, const SkHighContrastConfig& config) {
    SkRect bounds = SkRect::MakeLTRB(0.0f, 0.0f, 1.0f, 1.0f);
    SkPaint xferPaint;
    xferPaint.setColorFilter(SkHighContrastFilter::Make(config));
    canvas->saveLayer(&bounds, &xferPaint);

    SkPaint paint;
    bounds = SkRect::MakeLTRB(0.1f, 0.2f, 0.9f, 0.4f);
    paint.setARGB(0xff, 0x66, 0x11, 0x11);
    canvas->drawRect(bounds, paint);

    SkFont font = ToolUtils::DefaultPortableFont();
    font.setSize(0.15f);
    font.setEdging(SkFont::Edging::kAlias);

    paint.setARGB(0xff, 0xbb, 0x77, 0x77);
    canvas->drawString("A", 0.15f, 0.35f, font, paint);

    bounds = SkRect::MakeLTRB(0.1f, 0.8f, 0.9f, 1.0f);
    paint.setARGB(0xff, 0xcc, 0xcc, 0xff);
    canvas->drawRect(bounds, paint);

    paint.setARGB(0xff, 0x88, 0x88, 0xbb);
    canvas->drawString("Z", 0.75f, 0.95f, font, paint);

    bounds = SkRect::MakeLTRB(0.1f, 0.4f, 0.9f, 0.6f);
    SkPoint     pts[] = { { 0, 0 }, { 1, 0 } };
    SkColor     colors[] = { SK_ColorWHITE, SK_ColorBLACK };
    SkScalar    pos[] = { 0.2f, 0.8f };
    paint.setShader(SkGradientShader::MakeLinear(
        pts, colors, pos,
        std::size(colors), SkTileMode::kClamp));
    canvas->drawRect(bounds, paint);

    bounds = SkRect::MakeLTRB(0.1f, 0.6f, 0.9f, 0.8f);
    SkColor colors2[] = { SK_ColorGREEN, SK_ColorWHITE };
    paint.setShader(SkGradientShader::MakeLinear(
        pts, colors2, pos,
        std::size(colors2), SkTileMode::kClamp));
    canvas->drawRect(bounds, paint);

    canvas->restore();
}

class HighContrastFilterGM : public skiagm::GM {
protected:
    void onOnceBeforeDraw() override {
        SkColor  g1Colors[] = { kColor1, SkColorSetA(kColor1, 0x20) };
        SkColor  g2Colors[] = { kColor2, SkColorSetA(kColor2, 0x20) };
        SkPoint  g1Points[] = { { 0, 0 }, { 0,     100 } };
        SkPoint  g2Points[] = { { 0, 0 }, { kSize, 0   } };
        SkScalar pos[] = { 0.2f, 1.0f };

        SkHighContrastConfig fConfig;
        fFilter = SkHighContrastFilter::Make(fConfig);
        fGr1 = SkGradientShader::MakeLinear(
            g1Points, g1Colors, pos, std::size(g1Colors),
            SkTileMode::kClamp);
        fGr2 = SkGradientShader::MakeLinear(
            g2Points, g2Colors, pos, std::size(g2Colors),
            SkTileMode::kClamp);
    }

    SkString getName() const override { return SkString("highcontrastfilter"); }

    SkISize getISize() override { return SkISize::Make(800, 420); }

    void onDraw(SkCanvas* canvas) override {
        SkHighContrastConfig configs[] = {
            { false, InvertStyle::kNoInvert, 0.0f },
            { false, InvertStyle::kInvertBrightness, 0.0f },
            { false, InvertStyle::kInvertLightness, 0.0f },
            { false, InvertStyle::kInvertLightness, 0.2f },
            { true, InvertStyle::kNoInvert, 0.0f },
            { true, InvertStyle::kInvertBrightness, 0.0f },
            { true, InvertStyle::kInvertLightness, 0.0f },
            { true, InvertStyle::kInvertLightness, 0.2f },
        };

        for (size_t i = 0; i < std::size(configs); ++i) {
            SkScalar x = kSize * (i % 4);
            SkScalar y = kSize * (i / 4);
            canvas->save();
            canvas->translate(x, y);
            canvas->scale(kSize, kSize);
            draw_scene(canvas, configs[i]);
            draw_label(canvas, configs[i]);
            canvas->restore();
        }
    }

private:
    sk_sp<SkColorFilter>    fFilter;
    sk_sp<SkShader>         fGr1, fGr2;

    using INHERITED = skiagm::GM;
};

DEF_GM(return new HighContrastFilterGM;)
