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

#include <iostream>
#include "include/core/SkStream.h"
#include "modules/skottie/include/Skottie.h"
#include "modules/skottie/include/SkottieProperty.h"
#include "tests/Test.h"

using namespace skottie;

namespace {

class FakeScalarExpressionEvaluator : public ExpressionEvaluator<float> {
public:
    float evaluate(float t) override { return 7.0f; }
};

class FakeVectorExpressionEvaluator : public ExpressionEvaluator<std::vector<float>> {
public:
    std::vector<float> evaluate(float t) override { return {0.1f, 0.2f, 0.3f, 1.0f}; }
};

class FakeStringExpressionEvaluator : public ExpressionEvaluator<SkString> {
public:
    SkString evaluate(float t) override { return SkString("Hello, world!"); }
};

class FakeExpressionManager : public ExpressionManager {
public:
    sk_sp<ExpressionEvaluator<float>> createNumberExpressionEvaluator(
            const char expression[]) override {
        return sk_make_sp<FakeScalarExpressionEvaluator>();
    }

    sk_sp<ExpressionEvaluator<SkString>> createStringExpressionEvaluator(
            const char expression[]) override {
        return sk_make_sp<FakeStringExpressionEvaluator>();
    }

    sk_sp<ExpressionEvaluator<std::vector<float>>> createArrayExpressionEvaluator(
            const char expression[]) override {
        return sk_make_sp<FakeVectorExpressionEvaluator>();
    }
};

class FakePropertyObserver : public PropertyObserver {
public:
    void onOpacityProperty(const char node_name[],
                           const LazyHandle<OpacityPropertyHandle>& opacity_handle) override {
        opacity_handle_ = opacity_handle();
    }

    void onTransformProperty(const char node_name[],
                             const LazyHandle<TransformPropertyHandle>& transform_handle) override {
        transform_handle_ = transform_handle();
    }

    void onColorProperty(const char node_name[],
                         const LazyHandle<ColorPropertyHandle>& color_handle) override {
        color_handle_ = color_handle();
    }

    void onTextProperty(const char node_name[],
                        const LazyHandle<TextPropertyHandle>& text_handle) override {
        text_handle_ = text_handle();
    }

    std::unique_ptr<OpacityPropertyHandle> opacity_handle_;
    std::unique_ptr<TransformPropertyHandle> transform_handle_;
    std::unique_ptr<ColorPropertyHandle> color_handle_;
    std::unique_ptr<TextPropertyHandle> text_handle_;
};
}  // namespace

DEF_TEST(Skottie_Expression, r) {
    static constexpr char json[] =
        R"({
             "v": "5.2.1",
             "w": 100,
             "h": 100,
             "fr": 10,
             "ip": 0,
             "op": 100,
             "layers": [
               {
                 "ip": 0,
                 "op": 100,
                 "ty": 1,
                 "nm": "My Layer",
                 "sr": 1,
                 "ks": {
                   "o": {
                     "a": 0,
                     "k": 100,
                     "ix": 11,
                     "x": "fake; return value is specified by the FakeScalarExpressionEvaluator."
                   },
                    "r": {
                        "a": 0,
                        "k": 0,
                        "ix": 10
                    },
                    "p": {
                        "a": 0,
                        "k": [
                            50,
                            50,
                            0
                        ],
                        "ix": 2,
                        "l": 2
                    },
                    "a": {
                        "a": 0,
                        "k": [
                            50,
                            50,
                            0
                        ],
                        "ix": 1,
                        "l": 2,
                        "x": "fake; return value is specified by the FakeArrayExpressionEvaluator."
                    },
                    "s": {
                        "a": 0,
                        "k": [
                            100,
                            100,
                            100
                        ],
                        "ix": 6,
                        "l": 2
                    }
                },
                "ef": [
                {
                    "ty": 21,
                    "nm": "Fill",
                    "np": 9,
                    "mn": "ADBE Fill",
                    "ix": 1,
                    "en": 1,
                    "ef": [
                        {
                            "ty": 10,
                            "nm": "Fill Mask",
                            "mn": "ADBE Fill-0001",
                            "ix": 1,
                            "v": {
                                "a": 0,
                                "k": 0,
                                "ix": 1
                            }
                        },
                        {
                            "ty": 7,
                            "nm": "All Masks",
                            "mn": "ADBE Fill-0007",
                            "ix": 2,
                            "v": {
                                "a": 0,
                                "k": 0,
                                "ix": 2
                            }
                        },
                        {
                            "ty": 2,
                            "nm": "Color",
                            "mn": "ADBE Fill-0002",
                            "ix": 3,
                            "v": {
                                "a": 0,
                                "k": [
                                    1,
                                    0,
                                    0,
                                    1
                                ],
                                "ix": 3,
                                "x": "fake; return value is specified by the FakeArrayExpressionEvaluator."
                            }
                        },
                        {
                            "ty": 7,
                            "nm": "Invert",
                            "mn": "ADBE Fill-0006",
                            "ix": 4,
                            "v": {
                                "a": 0,
                                "k": 0,
                                "ix": 4
                            }
                        },
                        {
                            "ty": 0,
                            "nm": "Horizontal Feather",
                            "mn": "ADBE Fill-0003",
                            "ix": 5,
                            "v": {
                                "a": 0,
                                "k": 0,
                                "ix": 5
                            }
                        },
                        {
                            "ty": 0,
                            "nm": "Vertical Feather",
                            "mn": "ADBE Fill-0004",
                            "ix": 6,
                            "v": {
                                "a": 0,
                                "k": 0,
                                "ix": 6
                            }
                        },
                        {
                            "ty": 0,
                            "nm": "Opacity",
                            "mn": "ADBE Fill-0005",
                            "ix": 7,
                            "v": {
                                "a": 0,
                                "k": 1,
                                "ix": 7
                            }
                        }
                    ]
                }
                ],
                "ao": 0,
                "sw": 100,
                "sh": 100,
                "sc": "#000000",
                "st": 0,
                "bm": 0
               }
             ]
           })";

    SkMemoryStream stream(json, strlen(json));

    auto em = sk_make_sp<FakeExpressionManager>();
    auto observer = sk_make_sp<FakePropertyObserver>();

    auto anim = Animation::Builder()
        .setExpressionManager(em)
        .setPropertyObserver(observer)
        .make(&stream);

    REPORTER_ASSERT(r, anim);

    anim->seekFrameTime(0);

    REPORTER_ASSERT(r, SkScalarNearlyEqual(observer->opacity_handle_->get(), 7.0f));
    SkPoint anchor_point = observer->transform_handle_->get().fAnchorPoint;
    REPORTER_ASSERT(r, SkScalarNearlyEqual(anchor_point.fX, 0.1f));
    REPORTER_ASSERT(r, SkScalarNearlyEqual(anchor_point.fY, 0.2f));
    REPORTER_ASSERT(r, (observer->color_handle_->get() == SkColor4f{0.1f, 0.2f, 0.3f, 1.0f}.toSkColor()));
}

DEF_TEST(Skottie_ExpressionText, r) {
    static constexpr char text_json[] =
    R"({
     "layers":[
        {
           "ty":5,
           "ks":{
              "a":{
                 "k":[
                    0,
                    0
                 ]
              },
              "p":{
                 "k":[
                    128,
                    144
                 ]
              },
              "o":{
                 "k":100
              }
           },
           "ind":0,
           "ip":0,
           "op":2,
           "nm":"TextLayer_0",
           "t":{
              "d":{
                 "k":[
                    {
                       "t":0,
                       "s":{
                          "f": "Helvetica",
                          "s":14,
                          "t":"will be replaced.",
                          "j":0,
                          "ps":[
                             0,
                             0
                          ],
                          "sz":[
                             384,
                             360
                          ],
                          "fc":[
                             0.95686274766921997,
                             0.37254902720451355,
                             0.25490197539329529,
                             1
                          ],
                          "lh":16
                       }
                    }
                 ],
                 "x": "fake; return value is specified by the FakeStringExpressionEvaluator."
              }
           }
        }
     ],
     "ip":0,
     "op":2,
     "fr":25,
     "w":1280,
     "h":720,
     "ddd":false,
     "v":"5.2.2",
     "nm":"skottie_animation",
     "fonts":{
        "list":[
           {
              "fName": "Helvetica",
              "fFamily":"external_font_family",
              "fStyle":"Regular"
           }
        ]
     }
  })";

    SkMemoryStream stream(text_json, strlen(text_json));

    auto em = sk_make_sp<FakeExpressionManager>();
    auto observer = sk_make_sp<FakePropertyObserver>();

    auto anim = Animation::Builder()
                        .setExpressionManager(em)
                        .setPropertyObserver(observer)
                        .make(&stream);

    REPORTER_ASSERT(r, anim);

    anim->seekFrameTime(0);

    REPORTER_ASSERT(r, observer->text_handle_->get().fText == SkString("Hello, world!"));
}
