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

#include "chrome/browser/fullscreen.h"

#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#include <algorithm>
#include <vector>

#include "base/basictypes.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/rect.h"

namespace {

// TODO (jianli): Merge with gtk_util::EnumerateTopLevelWindows.
void EnumerateAllChildWindows(ui::EnumerateWindowsDelegate* delegate,
                              XID window) {
  std::vector<XID> windows;

  if (!ui::GetXWindowStack(window, &windows)) {
    // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
    // to old school enumeration of all X windows.
    XID root, parent, *children;
    unsigned int num_children;
    int status = XQueryTree(ui::GetXDisplay(), window, &root, &parent,
                            &children, &num_children);
    if (status) {
      for (long i = static_cast<long>(num_children) - 1; i >= 0; i--)
        windows.push_back(children[i]);
      XFree(children);
    }
  }

  std::vector<XID>::iterator iter;
  for (iter = windows.begin(); iter != windows.end(); iter++) {
    if (delegate->ShouldStopIterating(*iter))
      return;
  }
}

// To find the top-most window:
// 1) Enumerate all top-level windows from the top to the bottom.
// 2) For each window:
//    2.1) If it is hidden, continue the iteration.
//    2.2) If it is managed by the Window Manager (has a WM_STATE property).
//         Return this window as the top-most window.
//    2.3) Enumerate all its child windows. If there is a child window that is
//         managed by the Window Manager (has a WM_STATE property). Return this
//         child window as the top-most window.
//    2.4) Otherwise, continue the iteration.

class WindowManagerWindowFinder : public ui::EnumerateWindowsDelegate {
 public:
  WindowManagerWindowFinder() : window_(None) { }

  XID window() const { return window_; }

 protected:
  virtual bool ShouldStopIterating(XID window) {
    if (ui::PropertyExists(window, "WM_STATE")) {
      window_ = window;
      return true;
    }
    return false;
  }

 private:
  XID window_;

  DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowFinder);
};

class TopMostWindowFinder : public ui::EnumerateWindowsDelegate {
 public:
  TopMostWindowFinder()
      : top_most_window_(None) {}

  XID top_most_window() const { return top_most_window_; }

 protected:
   virtual bool ShouldStopIterating(XID window) {
     if (!ui::IsWindowVisible(window))
       return false;
     if (ui::PropertyExists(window, "WM_STATE")) {
      top_most_window_ = window;
      return true;
    }
     WindowManagerWindowFinder child_finder;
     EnumerateAllChildWindows(&child_finder, window);
     XID child_window = child_finder.window();
     if (child_window == None)
       return false;
     top_most_window_ = child_window;
     return true;
   }

 private:
  XID top_most_window_;

  DISALLOW_COPY_AND_ASSIGN(TopMostWindowFinder);
};

bool IsTopMostWindowFullScreen() {
  // Find the topmost window.
  TopMostWindowFinder finder;
  EnumerateAllChildWindows(&finder, ui::GetX11RootWindow());
  XID window = finder.top_most_window();
  if (window == None)
    return false;

  // Make sure it is not the desktop window.
  static Atom desktop_atom = gdk_x11_get_xatom_by_name_for_display(
      gdk_display_get_default(), "_NET_WM_WINDOW_TYPE_DESKTOP");

  std::vector<Atom> atom_properties;
  if (ui::GetAtomArrayProperty(window,
                               "_NET_WM_WINDOW_TYPE",
                               &atom_properties) &&
      std::find(atom_properties.begin(), atom_properties.end(), desktop_atom)
          != atom_properties.end())
    return false;

  // If it is a GDK window, check it using gdk function.
  GdkWindow* gwindow = gdk_window_lookup(window);
  if (gwindow && window != GDK_ROOT_WINDOW())
    return gdk_window_get_state(gwindow) == GDK_WINDOW_STATE_FULLSCREEN;

  // Otherwise, do the check via xlib function.
  return ui::IsX11WindowFullScreen(window);
}

}

bool IsFullScreenMode() {
  gdk_error_trap_push();
  bool result = IsTopMostWindowFullScreen();
  bool got_error = gdk_error_trap_pop();
  return result && !got_error;
}