/**
 * INativeControls: NativeControls::button_setURL
 *
 * @param uuid
 */
STDMETHODIMP CNativeControls::button_setURL(BSTR uuid, BSTR url, IDispatch *success, IDispatch *error)
{
  HRESULT hr;

  logger->debug(L"CNativeControls::button_setURL"
    L" -> " + wstring(uuid) +
    L" -> " + wstring(url) +
    L" -> " + boost::lexical_cast<wstring>(success)+
    L" -> " + boost::lexical_cast<wstring>(error));

  PopupWindow::pointer popup = m_popupWindows[uuid];
  if (!popup) {
    logger->error(L"CNativeControls::button_setURL fatal error no popup window for extension -> " + wstring(uuid));
    return CComDispatchDriver(error).Invoke1((DISPID)0, &CComVariant(L"{ 'message' : 'fatal error' }"));
  }

  bfs::wpath path = _AtlModule.modulePath / bfs::wpath(L"src") / bfs::wpath(url);
  logger->debug(L"CNativeControls::button_setURL path: " + path.wstring());

  hr = popup->Navigate(CComBSTR(path.wstring().c_str()));
  if (FAILED(hr)) {
    logger->error(L"CNativeControls::button_setURL error -> " + wstring(uuid) + L" -> " + path.wstring());
    return CComDispatchDriver(error).Invoke1((DISPID)0, &CComVariant(L"{ 'message' : 'failed to set URL' }"));
  }

  // reset onClick
  button_listeners[uuid] = NULL;

  return CComDispatchDriver(success).Invoke0((DISPID)0);
}
/**
 * INativeControls: NativeControls::button_setIcon
 *
 * @param uuid
 */
STDMETHODIMP CNativeControls::button_setIcon(BSTR _uuid, BSTR _url, 
                                             IDispatch *success, 
                                             IDispatch *error)
{
    wstring uuid(_uuid), url(_url);

    logger->debug(L"CNativeControls::button_setIcon"
                  L" -> " + uuid +
                  L" -> " + url +
                  L" -> " + boost::lexical_cast<wstring>(success) +
                  L" -> " + boost::lexical_cast<wstring>(error));

    if (!m_frameProxy) {
        logger->debug(L"CNativeControls::button_setIcon invalid FrameProxy");
        return S_OK;
    }

    // strip protocol from filesystem URLs
    wstring protocol(L"file:///");
    if (url.find(protocol) == 0) {
        url = url.erase(0, protocol.size());
    }
    protocol = L"file://";
    if (url.find(protocol) == 0) {
        url = url.erase(0, protocol.size());
    }
    bfs::wpath path(url);

    // check for relative paths
    if (!path.has_root_path()) {
        path = _AtlModule.modulePath / bfs::wpath(L"src") / path;        
    } 
    
    // check if it exists
    if (!bfs::exists(path)) {
        logger->debug(L"CNativeControls::button_setIcon invalid path for icon"
                      L" -> " + path.wstring());
        return CComDispatchDriver(error)
            .Invoke1((DISPID)0, 
                     &CComVariant(L"{ 'message' : 'invalid path for icon' }"));
    }

    // dispatch command via FrameProxy
    button_setIconCommand command(uuid.c_str(), path.wstring().c_str());
    m_frameProxy->Write(command, sizeof(button_setIconCommand));
    
    return CComDispatchDriver(success).Invoke0((DISPID)0);
}
/**
 * INativeControls: NativeControls::button_setBadgeBackgroundColor
 *
 * @param uuid
 */
STDMETHODIMP CNativeControls::button_setBadgeBackgroundColor(BSTR uuid, 
                                                             BYTE r, BYTE g, BYTE b, BYTE a,
                                                             IDispatch *success, 
                                                             IDispatch *error)
{
  logger->debug(L"CNativeControls::button_setBadgeBackgroundColor"
    L" -> " + wstring(uuid) +
    L" -> " + boost::lexical_cast<wstring>(r)+
    L" -> " + boost::lexical_cast<wstring>(g)+
    L" -> " + boost::lexical_cast<wstring>(b)+
    L" -> " + boost::lexical_cast<wstring>(a)+
    L" -> " + boost::lexical_cast<wstring>(success)+
    L" -> " + boost::lexical_cast<wstring>(error));

  if (!m_frameProxy) {
    logger->debug(L"CNativeControls::button_setTitle invalid FrameProxy");
    return S_OK;
  }

  // dispatch command via FrameProxy
  button_setBadgeBackgroundColorCommand command(wstring(uuid).c_str(), r, g, b, a);
  m_frameProxy->Write(command, sizeof(button_setBadgeBackgroundColorCommand));

  return CComDispatchDriver(success).Invoke0((DISPID)0);
}
/**
 * INativeControls: NativeControls::button_click
 *
 * @param uuid
 */
STDMETHODIMP CNativeControls::button_click(BSTR uuid, POINT point)
{
  HRESULT hr = S_OK;

  logger->debug(L"CNativeControls::button_click"
    L" -> " + wstring(uuid) +
    L" -> " + boost::lexical_cast<wstring>(point.x) +
    L" -> " + boost::lexical_cast<wstring>(point.y) +
    L" -> " + boost::lexical_cast<wstring>(button_listeners.size()));

  Listener listener = button_listeners[uuid];
  if (listener) {
    hr = CComDispatchDriver(listener).Invoke0((DISPID)0);
    if (FAILED(hr))
      logger->error(L"CNativeControls::button_click failed to invoke listener -> " + wstring(uuid) + L" -> " + boost::lexical_cast<wstring>(listener));
    
    return hr;
  }

  logger->debug(L"CNativeControls::button_click no button listener -> " + wstring(uuid));

  hr = popup_visible(uuid, true, point);
  if (FAILED(hr))
    logger->error(L"CNativeControls::button_click failed to activate popup -> " + wstring(uuid));

  return hr;
}
/**
 * Method: NativeMessaging::tabs_active
 *
 * @param uuid
 *
 * @returns Tab
 */
STDMETHODIMP CNativeMessaging::tabs_active(BSTR uuid, IDispatch *callback, UINT *out_tabId)
{
  HRESULT hr = S_OK;
  CComPtr<ITypeInfo> tabT = nullptr;
  CComPtr<IUnknown>  tabI = nullptr;
  for (;;) {
    BreakOnNull(out_tabId, hr);
    *out_tabId = m_activeTab.id;

    BreakOnNull(callback, hr);

    hr = ::CreateDispTypeInfo(&Tab::Interface, LOCALE_SYSTEM_DEFAULT, &tabT);
    BreakOnFailed(hr);
    BreakOnNull(tabT, hr);

    hr = ::CreateStdDispatch(NULL, &m_activeTab, tabT, &tabI);
    BreakOnFailed(hr);
    BreakOnNull(tabI, hr);

    hr = CComDispatchDriver(callback).Invoke1((DISPID)0, &CComVariant(tabI));
    break;
  }

  if (FAILED(hr))
    logger->error(L"CNativeMessaging::tabs_active failed -> " + logger->parse(hr));

  return hr;
}
/**
 * INativeControls: NativeControls::button_setBadge
 *
 * @param uuid
 */
STDMETHODIMP CNativeControls::button_setBadge(BSTR uuid, INT number,
                                              IDispatch *success, 
                                              IDispatch *error)
{
  logger->debug(L"CNativeControls::button_setBadge"
    L" -> " + wstring(uuid) +
    L" -> " + boost::lexical_cast<wstring>(number)+
    L" -> " + boost::lexical_cast<wstring>(success)+
    L" -> " + boost::lexical_cast<wstring>(error));

  if (!m_frameProxy) {
    logger->debug(L"CNativeControls::button_setTitle invalid FrameProxy");
    return S_OK;
  }

  // dispatch command via FrameProxy
  button_setBadgeCommand command(wstring(uuid).c_str(), number);
  m_frameProxy->Write(command, sizeof(button_setBadgeCommand));

  return CComDispatchDriver(success).Invoke0((DISPID)0);
}