// 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/image/decompressed_image.h"

#include <limits>

#include "flutter/fml/mapping.h"
#include "impeller/base/allocation.h"

namespace impeller {

DecompressedImage::DecompressedImage() = default;

DecompressedImage::DecompressedImage(
    ISize size,
    Format format,
    std::shared_ptr<const fml::Mapping> allocation)
    : size_(size), format_(format), allocation_(std::move(allocation)) {
  if (!allocation_ || !size.IsPositive() || format_ == Format::kInvalid) {
    return;
  }
  is_valid_ = true;
}

DecompressedImage::~DecompressedImage() = default;

bool DecompressedImage::IsValid() const {
  return is_valid_;
}

const ISize& DecompressedImage::GetSize() const {
  return size_;
}

DecompressedImage::Format DecompressedImage::GetFormat() const {
  return format_;
}

const std::shared_ptr<const fml::Mapping>& DecompressedImage::GetAllocation()
    const {
  return allocation_;
}

static size_t GetBytesPerPixel(DecompressedImage::Format format) {
  switch (format) {
    case DecompressedImage::Format::kInvalid:
      return 0u;
    case DecompressedImage::Format::kGrey:
      return 1u;
    case DecompressedImage::Format::kGreyAlpha:
      return 1u;
    case DecompressedImage::Format::kRGB:
      return 3u;
    case DecompressedImage::Format::kRGBA:
      return 4;
  }
  return 0u;
}

DecompressedImage DecompressedImage::ConvertToRGBA() const {
  if (!is_valid_) {
    return {};
  }

  if (format_ == Format::kRGBA) {
    return DecompressedImage{size_, format_, allocation_};
  }

  const auto bpp = GetBytesPerPixel(format_);
  const auto source_byte_size = size_.Area() * bpp;
  if (allocation_->GetSize() < source_byte_size) {
    return {};
  }

  auto rgba_allocation = std::make_shared<Allocation>();
  if (!rgba_allocation->Truncate(size_.Area() * 4u, false)) {
    return {};
  }

  const uint8_t* source = allocation_->GetMapping();
  uint8_t* dest = rgba_allocation->GetBuffer();

  for (size_t i = 0, j = 0; i < source_byte_size; i += bpp, j += 4u) {
    switch (format_) {
      case DecompressedImage::Format::kGrey:
        dest[j + 0] = source[i];
        dest[j + 1] = source[i];
        dest[j + 2] = source[i];
        dest[j + 3] = std::numeric_limits<uint8_t>::max();
        break;
      case DecompressedImage::Format::kGreyAlpha:
        dest[j + 0] = std::numeric_limits<uint8_t>::max();
        dest[j + 1] = std::numeric_limits<uint8_t>::max();
        dest[j + 2] = std::numeric_limits<uint8_t>::max();
        dest[j + 3] = source[i];
        break;
      case DecompressedImage::Format::kRGB:
        dest[j + 0] = source[i + 0];
        dest[j + 1] = source[i + 1];
        dest[j + 2] = source[i + 2];
        dest[j + 3] = std::numeric_limits<uint8_t>::max();
        break;
      case DecompressedImage::Format::kInvalid:
      case DecompressedImage::Format::kRGBA:
        // Should never happen. The necessary checks have already been
        // performed.
        FML_CHECK(false);
        break;
    }
  }

  return DecompressedImage{
      size_, Format::kRGBA,
      std::make_shared<fml::NonOwnedMapping>(
          rgba_allocation->GetBuffer(),      //
          rgba_allocation->GetLength(),      //
          [rgba_allocation](auto, auto) {})  //
  };
}

}  // namespace impeller
