// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/color/color_transform.h"

#include "base/bind.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/color/color_mixer.h"

namespace ui {

ColorTransform::ColorTransform(Callback callback)
    : callback_(std::move(callback)) {}

ColorTransform::ColorTransform(SkColor color) {
  const auto generator = [](SkColor color, SkColor input_color,
                            const ColorMixer& mixer) { return color; };
  callback_ = base::BindRepeating(generator, color);
}

ColorTransform::ColorTransform(ColorId id) {
  DCHECK_COLOR_ID_VALID(id);
  const auto generator = [](ColorId id, SkColor input_color,
                            const ColorMixer& mixer) {
    return mixer.GetResultColor(id);
  };
  callback_ = base::BindRepeating(generator, id);
}

ColorTransform::ColorTransform(const ColorTransform&) = default;

ColorTransform& ColorTransform::operator=(const ColorTransform&) = default;

ColorTransform::~ColorTransform() = default;

SkColor ColorTransform::Run(SkColor input_color,
                            const ColorMixer& mixer) const {
  return callback_.Run(input_color, mixer);
}

ColorTransform AlphaBlend(ColorTransform foreground_transform,
                          ColorTransform background_transform,
                          SkAlpha alpha) {
  const auto generator = [](ColorTransform foreground_transform,
                            ColorTransform background_transform, SkAlpha alpha,
                            SkColor input_color, const ColorMixer& mixer) {
    return color_utils::AlphaBlend(foreground_transform.Run(input_color, mixer),
                                   background_transform.Run(input_color, mixer),
                                   alpha);
  };
  return base::BindRepeating(generator, std::move(foreground_transform),
                             std::move(background_transform), alpha);
}

ColorTransform BlendForMinContrast(
    ColorTransform foreground_transform,
    ColorTransform background_transform,
    base::Optional<ColorTransform> high_contrast_foreground_transform,
    float contrast_ratio) {
  const auto generator =
      [](ColorTransform foreground_transform,
         ColorTransform background_transform,
         base::Optional<ColorTransform> high_contrast_foreground_transform,
         float contrast_ratio, SkColor input_color, const ColorMixer& mixer) {
        const SkColor foreground_color =
            foreground_transform.Run(input_color, mixer);
        const SkColor background_color =
            background_transform.Run(input_color, mixer);
        const base::Optional<SkColor> high_contrast_foreground =
            high_contrast_foreground_transform.has_value()
                ? base::make_optional(
                      high_contrast_foreground_transform.value().Run(
                          input_color, mixer))
                : base::nullopt;
        return color_utils::BlendForMinContrast(
                   foreground_color, background_color, high_contrast_foreground,
                   contrast_ratio)
            .color;
      };
  return base::BindRepeating(generator, std::move(foreground_transform),
                             std::move(background_transform),
                             std::move(high_contrast_foreground_transform),
                             contrast_ratio);
}

ColorTransform BlendForMinContrastWithSelf(ColorTransform transform,
                                           float contrast_ratio) {
  return BlendForMinContrast(transform, transform, base::nullopt,
                             contrast_ratio);
}

ColorTransform BlendTowardMaxContrast(ColorTransform transform, SkAlpha alpha) {
  const auto generator = [](ColorTransform transform, SkAlpha alpha,
                            SkColor input_color, const ColorMixer& mixer) {
    return color_utils::BlendTowardMaxContrast(
        transform.Run(input_color, mixer), alpha);
  };
  return base::BindRepeating(generator, std::move(transform), alpha);
}

ColorTransform ContrastInvert(ColorTransform transform) {
  const auto generator = [](ColorTransform transform, SkColor input_color,
                            const ColorMixer& mixer) {
    const SkColor foreground = transform.Run(input_color, mixer);
    const SkColor far_endpoint =
        color_utils::GetColorWithMaxContrast(foreground);
    const SkColor near_endpoint =
        color_utils::GetEndpointColorWithMinContrast(foreground);
    const float contrast_ratio =
        color_utils::GetContrastRatio(foreground, far_endpoint);
    return color_utils::BlendForMinContrast(foreground, near_endpoint,
                                            base::nullopt, contrast_ratio)
        .color;
  };
  return base::BindRepeating(generator, std::move(transform));
}

ColorTransform DeriveDefaultIconColor(ColorTransform transform) {
  const auto generator = [](ColorTransform transform, SkColor input_color,
                            const ColorMixer& mixer) {
    return color_utils::DeriveDefaultIconColor(
        transform.Run(input_color, mixer));
  };
  return base::BindRepeating(generator, std::move(transform));
}

ColorTransform FromOriginalColorFromSet(ColorId id, ColorSetId set_id) {
  DCHECK_COLOR_ID_VALID(id);
  DCHECK_COLOR_SET_ID_VALID(set_id);
  const auto generator = [](ColorId id, ColorSetId set_id, SkColor input_color,
                            const ColorMixer& mixer) {
    return mixer.GetOriginalColorFromSet(id, set_id);
  };
  return base::BindRepeating(generator, id, set_id);
}

ColorTransform FromTransformInput() {
  const auto generator = [](SkColor input_color, const ColorMixer& mixer) {
    return input_color;
  };
  return base::BindRepeating(generator);
}

ColorTransform GetColorWithMaxContrast(ColorTransform transform) {
  const auto generator = [](ColorTransform transform, SkColor input_color,
                            const ColorMixer& mixer) {
    return color_utils::GetColorWithMaxContrast(
        transform.Run(input_color, mixer));
  };
  return base::BindRepeating(generator, std::move(transform));
}

ColorTransform GetResultingPaintColor(ColorTransform foreground_transform,
                                      ColorTransform background_transform) {
  const auto generator = [](ColorTransform foreground_transform,
                            ColorTransform background_transform,
                            SkColor input_color, const ColorMixer& mixer) {
    return color_utils::GetResultingPaintColor(
        foreground_transform.Run(input_color, mixer),
        background_transform.Run(input_color, mixer));
  };
  return base::BindRepeating(generator, std::move(foreground_transform),
                             std::move(background_transform));
}

ColorTransform SelectBasedOnDarkInput(
    ColorTransform input_transform,
    ColorTransform output_transform_for_dark_input,
    ColorTransform output_transform_for_light_input) {
  const auto generator = [](ColorTransform input_transform,
                            ColorTransform output_transform_for_dark_input,
                            ColorTransform output_transform_for_light_input,
                            SkColor input_color, const ColorMixer& mixer) {
    const SkColor color = input_transform.Run(input_color, mixer);
    const auto& output_transform = color_utils::IsDark(color)
                                       ? output_transform_for_dark_input
                                       : output_transform_for_light_input;
    return output_transform.Run(input_color, mixer);
  };
  return base::BindRepeating(generator, std::move(input_transform),
                             std::move(output_transform_for_dark_input),
                             std::move(output_transform_for_light_input));
}

ColorTransform SetAlpha(ColorTransform transform, SkAlpha alpha) {
  const auto generator = [](ColorTransform transform, SkAlpha alpha,
                            SkColor input_color, const ColorMixer& mixer) {
    return SkColorSetA(transform.Run(input_color, mixer), alpha);
  };
  return base::BindRepeating(generator, std::move(transform), alpha);
}

}  // namespace ui
