/**
 * 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::popup_visible
 *
 * @param uuid
 * @param isVisible
 */
STDMETHODIMP CNativeControls::popup_visible(BSTR uuid, BOOL isVisible, POINT point)
{
    logger->debug(L"CNativeControls::popup_visible"
                  L" -> " + wstring(uuid) +
                  L" -> " + boost::lexical_cast<wstring>(isVisible) +
                  L" -> " + boost::lexical_cast<wstring>(point.x) +
                  L" -> " + boost::lexical_cast<wstring>(point.y) +
                  L" -> " + boost::lexical_cast<wstring>(m_frame));

    // TODO
    wchar_t buf[MAX_PATH];
    ::GetClassName(m_frame, buf, MAX_PATH);
    if (m_frame == NULL) {
        logger->error(L"!!!! CNativeControls::popup_visible WARNING m_frame is null !!!");
    } else {
        logger->debug(L"CNativeControls::popup_visible m_frame -> " + wstring(buf));
    }

    // get popup
    PopupWindow::pointer popup = m_popupWindows[uuid];
    if (!popup) { 
        logger->debug(L"CNativeControls::popup_visible has no popup"
                      L" -> " + wstring(uuid));
        return E_FAIL;
    }

    // get screen metrics
    DWORD screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
    if (point.x > (int)(screenWidth / 2)) {
        popup->alignment = PopupWindow::left;
    } else {
        popup->alignment = PopupWindow::right;
    }

    // update popup position & visibility
    RECT rect;
    popup->GetClientRect(&rect);
    int width  = rect.right - rect.left;
    int height = rect.bottom - rect.top;
    int origin = point.x;
    if (popup->alignment == PopupWindow::left) {
        origin = origin - (width - (PopupWindow::TAB_SIZE * 3)) - 3;
    } else {
        origin = origin - (PopupWindow::TAB_SIZE * 1) - 3;
    }
    popup->MoveWindow(origin, 
                      point.y, 
                      width, 
                      height);
    popup->hiddenBrowser.buttonPosition = point;
    popup->ShowWindow(SW_SHOW);
    
    return S_OK;
}
/**
 * INativeControls: load
 */
STDMETHODIMP CNativeControls::load(BSTR _uuid, BSTR _extensionPath, unsigned int instanceId, ULONG hwnd)
{
  HRESULT hr = E_FAIL;

  wstring uuid = _uuid;
  bfs::wpath extensionPath = bfs::wpath(_extensionPath);
  m_frame = (HWND)hwnd;

  HWND target = nullptr;

  logger->debug(L"CNativeControls::load"
    L" -> " + uuid +
    L" -> " + extensionPath.wstring() +
    L" -> " + boost::lexical_cast<wstring>(instanceId)+
    L" -> " + boost::lexical_cast<wstring>(m_frame));

  for (;;) {
    // load manifest
    auto scriptExtensions = ScriptExtensions::pointer(new ScriptExtensions(extensionPath));
    BreakOnNull(scriptExtensions, hr);
    if (!scriptExtensions->manifest) {
      logger->error(L"Failed to read manifest file: " + scriptExtensions->pathManifest.wstring() + L" at: " + extensionPath.wstring());
      break;
    }

    m_extensionPaths[uuid] = extensionPath;
    m_extensionManifests[uuid] = scriptExtensions->manifest;

    // get toolbar HWND from IEFrame HWND
    HWND toolbar = WindowsMessage::GetToolbar(m_frame, &toolbar, &target);
    if (!toolbar || !target) {
      logger->error(L"CNativeControls::load failed to get ToolbarWindow32");
      break;
    }

    // inject proxy lib into frame process
    m_frameProxy = new FrameProxy(L"NativeControls", _AtlModule.moduleHandle, toolbar, target);
    BreakOnNull(m_frameProxy, hr);

    // create popup
    PopupWindow::pointer popup = m_popupWindows[uuid];
    if (!popup) {
      logger->debug(L"CNativeControls::load creating popup -> " + wstring(uuid));

      // get default url for popup 
      Manifest::pointer manifest = m_extensionManifests[uuid];
      if (!manifest) {
        logger->error(L"CNativeControls::load no manifest for extension -> " + wstring(uuid));
        break;
      }

      bfs::wpath path = m_extensionPaths[uuid] / manifest->browser_action.default_popup;
      wstring url = L"file://" + path.wstring();

      // get a popup parent that doesn't competely tie IE's knickers into a knot
      HWND parent = m_frame;
      parent = ::FindWindowEx(parent, NULL, L"Frame Tab", NULL);
      if (!parent) {
        // In IE7 there is no intermediate "Frame Tab" window. If we didn't find
        // one try getting TabWindowClass directly from under m_frame.
        parent = m_frame;
      }

      parent = ::FindWindowEx(parent, NULL, L"TabWindowClass", NULL);
      if (!parent) {
        logger->error(L"CNativeControls::load failed: TabWindowClass");
        break;
      }

      POINT point = {0, 0};
      popup = PopupWindow::pointer(new PopupWindow(uuid, point, url));
      HWND hwnd = popup->Create(parent, CRect(point.x, point.y, point.x + PopupWindow::DEFAULT_WIDTH, point.y + PopupWindow::DEFAULT_HEIGHT), CComBSTR((L"Forge Popup " + wstring(uuid)).c_str()));
      if (!hwnd) {
        logger->error(L"CNativeControls::load failed to create popup");
        break;
      }
      m_popupWindows[uuid] = popup;
    }

    hr = S_OK; // if we are here - all went ok
    break;
  }

  return hr;
}
/**
 * INativeControls: load
 */
STDMETHODIMP CNativeControls::load(BSTR _uuid, BSTR _extensionPath, unsigned int instanceId, ULONG hwnd)
{
    wstring uuid             = _uuid;
    bfs::wpath extensionPath = bfs::wpath(_extensionPath);
    this->m_frame            = (HWND)hwnd;

    logger->debug(L"CNativeControls::load"
                  L" -> " + uuid +
                  L" -> " + extensionPath.wstring() +
                  L" -> " + boost::lexical_cast<wstring>(instanceId) +
                  L" -> " + boost::lexical_cast<wstring>(m_frame));

    // load manifest
    ScriptExtensions::pointer scriptExtensions =
        ScriptExtensions::pointer(new ScriptExtensions(extensionPath));
    if (!scriptExtensions->manifest) {
        logger->error(L"Failed to read manifest file: " +
                      scriptExtensions->pathManifest.wstring() + 
                      L" at: " + extensionPath.wstring());
        ::MessageBox(NULL,
                     wstring(L"Failed to read manifest. Please check that "
                             L" manifest.json file is present at " +
                             extensionPath.wstring() +
                             L" and properly configured.").c_str(),
                     L"trigger.io",
                     MB_TASKMODAL | MB_ICONEXCLAMATION);
        // TODO halt init and exit gracefully
        return E_FAIL;
    }
    m_extensionPaths[uuid] = extensionPath;
    m_extensionManifests[uuid] = scriptExtensions->manifest;

    // get toolbar HWND from IEFrame HWND
    HWND toolbar, target;
    toolbar = WindowsMessage::GetToolbar(m_frame, &toolbar, &target);
    if (!toolbar || !target) {
            logger->error(L"CNativeControls::load failed to get ToolbarWindow32");
            return E_FAIL;
    }

    // inject proxy lib into frame process
    m_frameProxy = new FrameProxy(L"NativeControls",
                                  _AtlModule.moduleHandle,
                                  toolbar, target);

    // create popup
    PopupWindow::pointer popup = m_popupWindows[uuid];
    if (!popup) { 
        logger->debug(L"CNativeControls::load creating popup"
                      L" -> " + wstring(uuid));

        // get default url for popup 
        Manifest::pointer manifest = m_extensionManifests[uuid];
        if (!manifest) {
            logger->error(L"CNativeControls::load "
                          L"no manifest for extension"
                          L" -> " + wstring(uuid));
            return E_FAIL;
        }
        bfs::wpath path = m_extensionPaths[uuid] / manifest->browser_action.default_popup;
        wstring url = L"file://" + path.wstring();

        // get a popup parent that doesn't competely tie IE's knickers into a knot
        HWND parent = m_frame;
        parent = ::FindWindowEx(parent, NULL, L"Frame Tab", NULL);
        if (!parent) {
            // In IE7 there is no intermediate "Frame Tab" window. If we didn't find
            // one try getting TabWindowClass directly from under m_frame.
            parent = m_frame;
        }
        parent = ::FindWindowEx(parent, NULL, L"TabWindowClass", NULL);
        if (!parent) { logger->error(L"CNativeControls::load failed: TabWindowClass"); return E_FAIL; }
        /*parent = ::FindWindowEx(parent, NULL, L"Shell DocObject View", NULL);
        if (!parent) { logger->error(L"CNativeControls::load failed: Shell DocObject View"); return E_FAIL; }
        parent = ::FindWindowEx(parent, NULL, L"Internet Explorer_Server", NULL);
        if (!parent) { logger->error(L"CNativeControls::load failed: Internet Explorer_Server"); return E_FAIL; }*/
        
        POINT point; point.x = 0; point.y = 0; 
        popup = PopupWindow::pointer(new PopupWindow(uuid, point, url));
        HWND hwnd;
        hwnd = popup->Create(parent,
                             CRect(point.x,
                                   point.y, 
                                   point.x + PopupWindow::DEFAULT_WIDTH,
                                   point.y + PopupWindow::DEFAULT_HEIGHT),
                             CComBSTR((L"Forge Popup " + wstring(uuid)).c_str()));
        if (!hwnd) {
            logger->error(L"CNativeControls::load "
                          L"failed to create popup");
            return E_FAIL;
        }
        m_popupWindows[uuid] = popup;
    }

    return S_OK;
}