// Copyright 2013 The Flutter 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 "impeller/core/formats.h"

#include <sstream>

#include "impeller/base/strings.h"
#include "impeller/base/validation.h"
#include "impeller/core/texture.h"

namespace impeller {

constexpr bool StoreActionNeedsResolveTexture(StoreAction action) {
  switch (action) {
    case StoreAction::kDontCare:
    case StoreAction::kStore:
      return false;
    case StoreAction::kMultisampleResolve:
    case StoreAction::kStoreAndMultisampleResolve:
      return true;
  }
}

bool Attachment::IsValid() const {
  if (!texture || !texture->IsValid()) {
    VALIDATION_LOG << "Attachment has no texture.";
    return false;
  }

  if (StoreActionNeedsResolveTexture(store_action)) {
    if (!resolve_texture || !resolve_texture->IsValid()) {
      VALIDATION_LOG << "Store action needs resolve but no valid resolve "
                        "texture specified.";
      return false;
    }
  }

  if (resolve_texture) {
    if (store_action != StoreAction::kMultisampleResolve &&
        store_action != StoreAction::kStoreAndMultisampleResolve) {
      VALIDATION_LOG << "A resolve texture was specified, but the store action "
                        "doesn't include multisample resolve.";
      return false;
    }

    if (texture->GetTextureDescriptor().storage_mode ==
            StorageMode::kDeviceTransient &&
        store_action == StoreAction::kStoreAndMultisampleResolve) {
      VALIDATION_LOG
          << "The multisample texture cannot be transient when "
             "specifying the StoreAndMultisampleResolve StoreAction.";
    }
  }

  auto storage_mode = resolve_texture
                          ? resolve_texture->GetTextureDescriptor().storage_mode
                          : texture->GetTextureDescriptor().storage_mode;

  if (storage_mode == StorageMode::kDeviceTransient) {
    if (load_action == LoadAction::kLoad) {
      VALIDATION_LOG << "The LoadAction cannot be Load when attaching a device "
                        "transient " +
                            std::string(resolve_texture ? "resolve texture."
                                                        : "texture.");
      return false;
    }
    if (store_action != StoreAction::kDontCare) {
      VALIDATION_LOG << "The StoreAction must be DontCare when attaching a "
                        "device transient " +
                            std::string(resolve_texture ? "resolve texture."
                                                        : "texture.");
      return false;
    }
  }

  return true;
}

std::string TextureUsageMaskToString(TextureUsageMask mask) {
  std::vector<TextureUsage> usages;
  if (mask & static_cast<TextureUsageMask>(TextureUsage::kShaderRead)) {
    usages.push_back(TextureUsage::kShaderRead);
  }
  if (mask & static_cast<TextureUsageMask>(TextureUsage::kShaderWrite)) {
    usages.push_back(TextureUsage::kShaderWrite);
  }
  if (mask & static_cast<TextureUsageMask>(TextureUsage::kRenderTarget)) {
    usages.push_back(TextureUsage::kRenderTarget);
  }
  std::stringstream stream;
  stream << "{ ";
  for (size_t i = 0; i < usages.size(); i++) {
    stream << TextureUsageToString(usages[i]);
    if (i != usages.size() - 1u) {
      stream << ", ";
    }
  }
  stream << " }";
  return stream.str();
}

std::string AttachmentToString(const Attachment& attachment) {
  std::stringstream stream;
  if (attachment.texture) {
    stream << "Texture=("
           << TextureDescriptorToString(
                  attachment.texture->GetTextureDescriptor())
           << "),";
  }
  if (attachment.resolve_texture) {
    stream << "ResolveTexture=("
           << TextureDescriptorToString(
                  attachment.resolve_texture->GetTextureDescriptor())
           << "),";
  }
  stream << "LoadAction=" << LoadActionToString(attachment.load_action) << ",";
  stream << "StoreAction=" << StoreActionToString(attachment.store_action);
  return stream.str();
}

std::string ColorAttachmentToString(const ColorAttachment& color) {
  std::stringstream stream;
  stream << AttachmentToString(color) << ",";
  stream << "ClearColor=(" << ColorToString(color.clear_color) << ")";
  return stream.str();
}

std::string DepthAttachmentToString(const DepthAttachment& depth) {
  std::stringstream stream;
  stream << AttachmentToString(depth) << ",";
  stream << "ClearDepth=" << SPrintF("%.2f", depth.clear_depth);
  return stream.str();
}

std::string StencilAttachmentToString(const StencilAttachment& stencil) {
  std::stringstream stream;
  stream << AttachmentToString(stencil) << ",";
  stream << "ClearStencil=" << stencil.clear_stencil;
  return stream.str();
}

}  // namespace impeller
