// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "photo_handler.h"

#include <mfapi.h>
#include <mfcaptureengine.h>
#include <wincodec.h>

#include <cassert>

#include "capture_engine_listener.h"
#include "string_utils.h"

namespace camera_windows {

using Microsoft::WRL::ComPtr;

// Initializes media type for photo capture for jpeg images.
HRESULT BuildMediaTypeForPhotoCapture(IMFMediaType* src_media_type,
                                      IMFMediaType** photo_media_type,
                                      GUID image_format) {
  assert(src_media_type);
  ComPtr<IMFMediaType> new_media_type;

  HRESULT hr = MFCreateMediaType(&new_media_type);
  if (FAILED(hr)) {
    return hr;
  }

  // Clones everything from original media type.
  hr = src_media_type->CopyAllItems(new_media_type.Get());
  if (FAILED(hr)) {
    return hr;
  }

  hr = new_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Image);
  if (FAILED(hr)) {
    return hr;
  }

  hr = new_media_type->SetGUID(MF_MT_SUBTYPE, image_format);
  if (FAILED(hr)) {
    return hr;
  }

  new_media_type.CopyTo(photo_media_type);
  return hr;
}

HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine,
                                    IMFMediaType* base_media_type) {
  assert(capture_engine);
  assert(base_media_type);

  HRESULT hr = S_OK;

  if (photo_sink_) {
    // If photo sink already exists, only update output filename.
    hr = photo_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str());

    if (FAILED(hr)) {
      photo_sink_ = nullptr;
    }

    return hr;
  }

  ComPtr<IMFMediaType> photo_media_type;
  ComPtr<IMFCaptureSink> capture_sink;

  // Get sink with photo type.
  hr =
      capture_engine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, &capture_sink);
  if (FAILED(hr)) {
    return hr;
  }

  hr = capture_sink.As(&photo_sink_);
  if (FAILED(hr)) {
    photo_sink_ = nullptr;
    return hr;
  }

  hr = photo_sink_->RemoveAllStreams();
  if (FAILED(hr)) {
    photo_sink_ = nullptr;
    return hr;
  }

  hr = BuildMediaTypeForPhotoCapture(base_media_type,
                                     photo_media_type.GetAddressOf(),
                                     GUID_ContainerFormatJpeg);

  if (FAILED(hr)) {
    photo_sink_ = nullptr;
    return hr;
  }

  DWORD photo_sink_stream_index;
  hr = photo_sink_->AddStream(
      (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO,
      photo_media_type.Get(), nullptr, &photo_sink_stream_index);
  if (FAILED(hr)) {
    photo_sink_ = nullptr;
    return hr;
  }

  hr = photo_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str());
  if (FAILED(hr)) {
    photo_sink_ = nullptr;
    return hr;
  }

  return hr;
}

HRESULT PhotoHandler::TakePhoto(const std::string& file_path,
                                IMFCaptureEngine* capture_engine,
                                IMFMediaType* base_media_type) {
  assert(!file_path.empty());
  assert(capture_engine);
  assert(base_media_type);

  file_path_ = file_path;

  HRESULT hr = InitPhotoSink(capture_engine, base_media_type);
  if (FAILED(hr)) {
    return hr;
  }

  photo_state_ = PhotoState::kTakingPhoto;

  return capture_engine->TakePhoto();
}

void PhotoHandler::OnPhotoTaken() {
  assert(photo_state_ == PhotoState::kTakingPhoto);
  photo_state_ = PhotoState::kIdle;
}

}  // namespace camera_windows