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

#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
#pragma once

// When generating projects with CMake the CEF_USE_ATL value will be defined
// automatically if using a supported Visual Studio version. Pass -DUSE_ATL=OFF
// to the CMake command-line to disable use of ATL.
// Uncomment this line to manually enable ATL support.
// #define CEF_USE_ATL 1

#if defined(CEF_USE_ATL)

#include <atlcomcli.h>
#include <objidl.h>
#include <stdio.h>

#include "tests/cefclient/browser/osr_dragdrop_events.h"

namespace client {

#define DEFAULT_QUERY_INTERFACE(__Class)                                     \
  HRESULT __stdcall QueryInterface(const IID& iid, void** object) override { \
    *object = nullptr;                                                       \
    if (IsEqualIID(iid, IID_IUnknown)) {                                     \
      IUnknown* obj = this;                                                  \
      *object = obj;                                                         \
    } else if (IsEqualIID(iid, IID_##__Class)) {                             \
      __Class* obj = this;                                                   \
      *object = obj;                                                         \
    } else {                                                                 \
      return E_NOINTERFACE;                                                  \
    }                                                                        \
    AddRef();                                                                \
    return S_OK;                                                             \
  }
#define IUNKNOWN_IMPLEMENTATION        \
  ULONG __stdcall AddRef() override {  \
    return ++ref_count_;               \
  }                                    \
  ULONG __stdcall Release() override { \
    if (--ref_count_ == 0) {           \
      delete this;                     \
      return 0U;                       \
    }                                  \
    return ref_count_;                 \
  }                                    \
                                       \
 protected:                            \
  ULONG ref_count_;

class DropTargetWin : public IDropTarget {
 public:
  static CComPtr<DropTargetWin> Create(OsrDragEvents* callback, HWND hWnd);

  CefBrowserHost::DragOperationsMask StartDragging(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefDragData> drag_data,
      CefRenderHandler::DragOperationsMask allowed_ops,
      int x,
      int y);

  // IDropTarget implementation:
  HRESULT __stdcall DragEnter(IDataObject* data_object,
                              DWORD key_state,
                              POINTL cursor_position,
                              DWORD* effect) override;

  HRESULT __stdcall DragOver(DWORD key_state,
                             POINTL cursor_position,
                             DWORD* effect) override;

  HRESULT __stdcall DragLeave() override;

  HRESULT __stdcall Drop(IDataObject* data_object,
                         DWORD key_state,
                         POINTL cursor_position,
                         DWORD* effect) override;

  DEFAULT_QUERY_INTERFACE(IDropTarget)
  IUNKNOWN_IMPLEMENTATION

 protected:
  DropTargetWin(OsrDragEvents* callback, HWND hWnd)
      : ref_count_(0), callback_(callback), hWnd_(hWnd) {}
  virtual ~DropTargetWin() = default;

 private:
  OsrDragEvents* callback_;
  HWND hWnd_;

  CefRefPtr<CefDragData> current_drag_data_;
};

class DropSourceWin : public IDropSource {
 public:
  static CComPtr<DropSourceWin> Create();

  // IDropSource implementation:
  HRESULT __stdcall GiveFeedback(DWORD dwEffect) override;

  HRESULT __stdcall QueryContinueDrag(BOOL fEscapePressed,
                                      DWORD grfKeyState) override;

  DEFAULT_QUERY_INTERFACE(IDropSource)
  IUNKNOWN_IMPLEMENTATION

 protected:
  explicit DropSourceWin() : ref_count_(0) {}
  virtual ~DropSourceWin() = default;
};

class DragEnumFormatEtc : public IEnumFORMATETC {
 public:
  static HRESULT CreateEnumFormatEtc(UINT cfmt,
                                     FORMATETC* afmt,
                                     IEnumFORMATETC** ppEnumFormatEtc);

  //
  // IEnumFormatEtc members
  //
  HRESULT __stdcall Next(ULONG celt,
                         FORMATETC* pFormatEtc,
                         ULONG* pceltFetched) override;
  HRESULT __stdcall Skip(ULONG celt) override;
  HRESULT __stdcall Reset() override;
  HRESULT __stdcall Clone(IEnumFORMATETC** ppEnumFormatEtc) override;

  //
  // Construction / Destruction
  //
  DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats);
  virtual ~DragEnumFormatEtc();

  static void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source);

  DEFAULT_QUERY_INTERFACE(IEnumFORMATETC)
  IUNKNOWN_IMPLEMENTATION

 private:
  ULONG m_nIndex;           // current enumerator index
  ULONG m_nNumFormats;      // number of FORMATETC members
  FORMATETC* m_pFormatEtc;  // array of FORMATETC objects
};

class DataObjectWin : public IDataObject {
 public:
  static CComPtr<DataObjectWin> Create(FORMATETC* fmtetc,
                                       STGMEDIUM* stgmed,
                                       int count);

  // IDataObject memberS
  HRESULT __stdcall GetDataHere(FORMATETC* pFormatEtc,
                                STGMEDIUM* pmedium) override;
  HRESULT __stdcall QueryGetData(FORMATETC* pFormatEtc) override;
  HRESULT __stdcall GetCanonicalFormatEtc(FORMATETC* pFormatEct,
                                          FORMATETC* pFormatEtcOut) override;
  HRESULT __stdcall SetData(FORMATETC* pFormatEtc,
                            STGMEDIUM* pMedium,
                            BOOL fRelease) override;
  HRESULT __stdcall DAdvise(FORMATETC* pFormatEtc,
                            DWORD advf,
                            IAdviseSink*,
                            DWORD*) override;
  HRESULT __stdcall DUnadvise(DWORD dwConnection) override;
  HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** ppEnumAdvise) override;

  HRESULT __stdcall EnumFormatEtc(DWORD dwDirection,
                                  IEnumFORMATETC** ppEnumFormatEtc) override;
  HRESULT __stdcall GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) override;

  DEFAULT_QUERY_INTERFACE(IDataObject)
  IUNKNOWN_IMPLEMENTATION

 protected:
  int m_nNumFormats;
  FORMATETC* m_pFormatEtc;
  STGMEDIUM* m_pStgMedium;

  static HGLOBAL DupGlobalMem(HGLOBAL hMem);

  int LookupFormatEtc(FORMATETC* pFormatEtc);

  explicit DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count);
  virtual ~DataObjectWin() = default;
};

}  // namespace client

#endif  // defined(CEF_USE_ATL)

#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
