// Copyright (c) 2010 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.

// TODO(slightlyoff): Add any required LICENSE block changes for MSFT code
// inclusion.

// ole_document_impl.h : IOleDocument implementation
//
// This file is a modified version of the OleDocument.h file, which is
// part of the ActiveDoc MSDN sample. The modifications are largely
// conversions to Google coding guidelines. Below if the original header
// from the file.

// This is a part of the Active Template Library.
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef CHROME_FRAME_OLE_DOCUMENT_IMPL_H_
#define CHROME_FRAME_OLE_DOCUMENT_IMPL_H_

// TODO(sanjeevr): Revisit this impl file and cleanup dependencies
#include <atlbase.h>
#include <docobj.h>

#include "base/logging.h"

//////////////////////////////////////////////////////////////////////////////
// IOleDocumentImpl
template <class T>
class ATL_NO_VTABLE IOleDocumentImpl : public IOleDocument {
 public:
  STDMETHOD(CreateView)(IOleInPlaceSite* in_place_site,
                        IStream* stream,
                        DWORD reserved ,
                        IOleDocumentView** new_view) {
    DVLOG(1) << __FUNCTION__;
    if (new_view == NULL)
      return E_POINTER;
    T* t = static_cast<T*>(this);
    // If we've already created a view then we can't create another as we
    // currently only support the ability to create one view
    if (t->m_spInPlaceSite)
      return E_FAIL;
    IOleDocumentView* view;
    t->GetUnknown()->QueryInterface(IID_IOleDocumentView,
                                    reinterpret_cast<void**>(&view));
    // If we support IOleDocument we should support IOleDocumentView
    ATLENSURE(view != NULL);
    // If they've given us a site then use it
    if (in_place_site != NULL)
      view->SetInPlaceSite(in_place_site);
    // If they have given us an IStream pointer then use it to
    // initialize the view
    if (stream != NULL)
      view->ApplyViewState(stream);
    // Return the view
    *new_view = view;
    return S_OK;
  }

  STDMETHOD(GetDocMiscStatus)(DWORD* status) {
    DVLOG(1) << __FUNCTION__;
    if (NULL == status)
      return E_POINTER;
    *status = DOCMISC_NOFILESUPPORT;
    return S_OK;
  }

  STDMETHOD(EnumViews)(IEnumOleDocumentViews** enum_views,
                       IOleDocumentView** view) {
    DVLOG(1) << __FUNCTION__;
    if (view == NULL)
      return E_POINTER;
    T* t = static_cast<T*>(this);
    // We only support one view
    return t->_InternalQueryInterface(IID_IOleDocumentView,
                                      reinterpret_cast<void**>(view));
  }
};

//////////////////////////////////////////////////////////////////////////////
// IOleDocumentViewImpl

template <class T>
class ATL_NO_VTABLE IOleDocumentViewImpl : public IOleDocumentView {
 public:
  STDMETHOD(SetInPlaceSite)(IOleInPlaceSite* in_place_site) {
    DVLOG(1) << __FUNCTION__;
    T* t = static_cast<T*>(this);
    if (t->m_spInPlaceSite) {
      // If we already have a site get rid of it
      UIActivate(FALSE);
      HRESULT hr = t->InPlaceDeactivate();
      if (FAILED(hr))
        return hr;
      DCHECK(!t->m_bInPlaceActive);
    }
    if (in_place_site != NULL) {
      t->m_spInPlaceSite.Release();
      in_place_site->QueryInterface(
          IID_IOleInPlaceSiteWindowless,
          reinterpret_cast<void **>(&t->m_spInPlaceSite));
      if (!t->m_spInPlaceSite) {
        // TODO(sanjeevr): This is a super-hack because m_spInPlaceSite
        // is an IOleInPlaceSiteWindowless pointer and we are setting
        // an IOleInPlaceSite pointer into it. The problem is that ATL
        // (CComControlBase) uses this in a schizophrenic manner based
        // on the m_bWndLess flag. Ouch, ouch, ouch! Find a way to clean
        // this up.
        // Disclaimer: I did not invent this hack, it exists in the MSDN
        // sample from where this code has been derived and it also exists
        // in ATL itself (look at atlctl.h line 938).
        t->m_spInPlaceSite =
            reinterpret_cast<IOleInPlaceSiteWindowless*>(in_place_site);
      }
    }
    return S_OK;
  }

  STDMETHOD(GetInPlaceSite)(IOleInPlaceSite** in_place_site) {
    DVLOG(1) << __FUNCTION__;
    if (in_place_site == NULL)
      return E_POINTER;
    T* t = static_cast<T*>(this);
    return t->m_spInPlaceSite->QueryInterface(
        IID_IOleInPlaceSite,
        reinterpret_cast<LPVOID *>(in_place_site));
  }

  STDMETHOD(GetDocument)(IUnknown** document) {
    DVLOG(1) << __FUNCTION__;
    if (document == NULL)
      return E_POINTER;
    T* t = static_cast<T*>(this);
    *document = t->GetUnknown();
    (*document)->AddRef();
    return S_OK;
  }

  STDMETHOD(SetRect)(LPRECT view_rect) {
    static bool is_resizing = false;
    if (is_resizing)
      return S_OK;
    is_resizing = true;
    DVLOG(1) << __FUNCTION__ << " " << view_rect->left << ","
             << view_rect->top << "," << view_rect->right << ","
             << view_rect->bottom;
    T* t = static_cast<T*>(this);
    t->SetObjectRects(view_rect, view_rect);
    is_resizing = false;
    return S_OK;
  }

  STDMETHOD(GetRect)(LPRECT view_rect) {
    DVLOG(1) << __FUNCTION__;
    if (view_rect == NULL)
      return E_POINTER;
    T* t = static_cast<T*>(this);
    *view_rect = t->m_rcPos;
    return S_OK;
  }

  STDMETHOD(SetRectComplex)(LPRECT view_rect,
                            LPRECT hscroll_rect,
                            LPRECT vscroll_rect,
                            LPRECT size_box_rect) {
    DVLOG(1) << __FUNCTION__ << " not implemented";
    return E_NOTIMPL;
  }

  STDMETHOD(Show)(BOOL show) {
    DVLOG(1) << __FUNCTION__;
    T* t = static_cast<T*>(this);
    HRESULT hr = S_OK;
    if (show) {
      if (!t->m_bUIActive)
        hr = t->ActiveXDocActivate(OLEIVERB_INPLACEACTIVATE);
    } else {
      hr = t->UIActivate(FALSE);
      ::ShowWindow(t->m_hWnd, SW_HIDE);
    }
    return hr;
  }

  STDMETHOD(UIActivate)(BOOL ui_activate) {
    DVLOG(1) << __FUNCTION__;
    T* t = static_cast<T*>(this);
    HRESULT hr = S_OK;
    if (ui_activate) {
      // We must know the client site first
      if (t->m_spInPlaceSite == NULL)
        return E_UNEXPECTED;
      if (!t->m_bUIActive)
        hr = t->ActiveXDocActivate(OLEIVERB_UIACTIVATE);
    } else {
      // Menu integration is still not complete, so do not destroy
      // IE's menus.  If we call InPlaceMenuDestroy here, menu items such
      // as Print etc will be disabled and we will not get calls to QueryStatus
      // for those commands.
      // t->InPlaceMenuDestroy();
      // t->DestroyToolbar();
      hr = t->UIDeactivate();
    }
    return hr;
  }

  STDMETHOD(Open)() {
    DVLOG(1) << __FUNCTION__ << " not implemented";
    return E_NOTIMPL;
  }

  STDMETHOD(CloseView)(DWORD reserved) {
    DVLOG(1) << __FUNCTION__;
    T* t = static_cast<T*>(this);
    t->Show(FALSE);
    t->SetInPlaceSite(NULL);
    return S_OK;
  }

  STDMETHOD(SaveViewState)(LPSTREAM stream) {
    DVLOG(1) << __FUNCTION__ << " not implemented";
    return E_NOTIMPL;
  }

  STDMETHOD(ApplyViewState)(LPSTREAM stream) {
    DVLOG(1) << __FUNCTION__ << " not implemented";
    return E_NOTIMPL;
  }

  STDMETHOD(Clone)(IOleInPlaceSite* new_in_place_site,
                   IOleDocumentView** new_view) {
    DVLOG(1) << __FUNCTION__ << " not implemented";
    return E_NOTIMPL;
  }

  HRESULT ActiveXDocActivate(LONG verb) {
    return E_NOTIMPL;
  }
};

#endif  // CHROME_FRAME_OLE_DOCUMENT_IMPL_H_