#include "Manager.h"
#include "Client.h"
#include <X11/keysym.h>
#include <gtk/gtkmain.h>

int WindowManager::loop() {
    XEvent ev;
    m_looping = True;
    while(m_looping) {
        if(gtk_events_pending())
            gtk_main_iteration();
        if(XPending(display()) > 0) {
            XNextEvent(display(), &ev);
            m_currentTime = CurrentTime;
            switch (ev.type) {
                case ButtonPress:
                    eventButton(&ev.xbutton);
                    break;
                case ButtonRelease:
                    break;
                case MapRequest:
                    eventMapRequest(&ev.xmaprequest);
                    break;
                case ConfigureRequest:
                    eventConfigureRequest(&ev.xconfigurerequest);
                    updateClientList();
                    break;
                case UnmapNotify:
                    eventUnmap(&ev.xunmap);
                    updateClientList();
                    break;
                case CreateNotify:
                    eventCreate(&ev.xcreatewindow);
                    updateClientList();
                    break;
                case DestroyNotify:
                    eventDestroy(&ev.xdestroywindow);
                    updateClientList();
                    break;
                case ClientMessage:
                    eventClient(&ev.xclient);
                    break;
                case ColormapNotify:
                    eventColormap(&ev.xcolormap);
                    break;
                case PropertyNotify:
                    eventProperty(&ev.xproperty);
                    break;
                case SelectionClear:
                case SelectionNotify:
                case SelectionRequest:
                    break;
                case EnterNotify:
                case LeaveNotify:
                    eventEnter(&ev.xcrossing);
                    break;
                case ReparentNotify:
                    eventReparent(&ev.xreparent);
                    break;
                case FocusIn:
                    eventFocusIn(&ev.xfocus);
                    break;
                case Expose:
                    eventExposure(&ev.xexpose);
                    break;
                case MotionNotify:
                    if (auto_raise && m_focusChanging) {
                        if (!m_focusPointerMoved) m_focusPointerMoved = True;
                        else m_focusPointerNowStill = False;
                    }
                    break;
                case ConfigureNotify:
                case FocusOut:
                case MapNotify:
                case MappingNotify:
                    updateClientList();
                    break;
                default:
                    fprintf(stderr, "wmG: unsupported event type %d\n", ev.type);
                    break;
            }
        }
	else
	    usleep(50);
    }
    release();
    return m_returnCode;
}

void WindowManager::checkDelaysForFocus() {
    if(!auto_raise)
        return;
    int t = timestamp(True);
    if(m_focusPointerMoved) {
        if(t < m_focusTimestamp || t - m_focusTimestamp > CONFIG_POINTER_STOPPED_DELAY) {
            if(m_focusPointerNowStill) {
                m_focusCandidate->focusIfAppropriate(True);
            } else
	        m_focusPointerNowStill = True;
        }
    } else {
        if(t < m_focusTimestamp || t - m_focusTimestamp > auto_raise_delay) {
            m_focusCandidate->focusIfAppropriate(True);
        }
    }
}

void WindowManager::considerFocusChange(Client *c, Window w, Time timestamp) {
    if(!auto_raise)
        return;
    if(m_focusChanging) {
        stopConsideringFocus();
    }
    m_focusChanging = True;
    m_focusTimestamp = timestamp;
    m_focusCandidate = c;
    m_focusCandidateWindow = w;
    m_focusPointerMoved = False;
    m_focusPointerNowStill = False;
    m_focusCandidate->selectOnMotion(m_focusCandidateWindow, True);
}

void WindowManager::stopConsideringFocus() {
    if(!auto_raise)
        return;
    m_focusChanging = False;
    if(m_focusChanging && m_focusCandidateWindow) {
        m_focusCandidate->selectOnMotion(m_focusCandidateWindow, False);
    }
}

void Client::focusIfAppropriate(Boolean ifActive) {
    if(!auto_raise)
        return;
    if(!m_managed || !isNormal())
        return;
    if(!ifActive && isActive())
        return;
    Window rw, cw;
    int rx, ry, cx, cy;
    unsigned int k;
    XQueryPointer(display(), root(), &rw, &cw, &rx, &ry, &cx, &cy, &k);
    if(hasWindow(cw)) {
        activate();
        mapRaised();
        m_windowManager->stopConsideringFocus();
    }
}

void WindowManager::eventConfigureRequest(XConfigureRequestEvent *e) {
    XWindowChanges wc;
    Client *c = windowToClient(e->window);
    e->value_mask &= ~CWSibling;
    if(c && e->window == c->handle())
        c->eventConfigureRequest(e);
    else {
        wc.x = e->x;
        wc.y = e->y;
        wc.width  = e->width;
        wc.height = e->height;
        wc.border_width = 0;
        wc.sibling = None;
        wc.stack_mode = Above;
        e->value_mask &= ~CWStackMode;
        e->value_mask |= CWBorderWidth;
        XConfigureWindow(display(), e->window, e->value_mask, &wc);
    }
}

void Client::eventConfigureRequest(XConfigureRequestEvent *e) {
    XWindowChanges wc;
    Boolean raise = False;
    e->value_mask &= ~CWSibling;
    if(e->value_mask & CWX)
        m_x = e->x;
    if(e->value_mask & CWY)
        m_y = e->y;
    if(e->value_mask & CWWidth)
        m_w = e->width;
    if(e->value_mask & CWHeight)
        m_h = e->height;
    if(e->value_mask & CWBorderWidth)
        m_bw = e->border_width;
    if(e->value_mask & CWStackMode) {
        if(e->detail == Above)
	    raise = True;
        e->value_mask &= ~CWStackMode;
    }
    if(parent() != root() && m_window == e->window) {
        m_border->configure(m_x, m_y, m_w, m_h, e->value_mask, e->detail, isActive());
        sendConfigureNotify();
    }
    if(m_managed && parent() != root()) {
        wc.x = m_border->xIndent();
        wc.y = m_border->yIndent();
    } else {
        wc.x = e->x;
        wc.y = e->y;
    }
    wc.width = e->width;
    wc.height = e->height;
    wc.border_width = 0;
    wc.sibling = None;
    wc.stack_mode = Above;
    e->value_mask &= ~CWStackMode;
    e->value_mask |= CWBorderWidth;
    XConfigureWindow(display(), e->window, e->value_mask, &wc);
    if(raise && parent() != root()) {
        if(auto_raise) {
            m_windowManager->stopConsideringFocus();
            if(!m_stubborn) {
                Time popTime = windowManager()->timestamp(True);
                if(m_lastPopTime > 0L && popTime > m_lastPopTime && popTime - m_lastPopTime < 2000) {
                    m_stubborn = True;
                    m_lastPopTime = 0L;
                } else {
                    m_lastPopTime = popTime;
                }
                mapRaised();
            }
        } else {
            mapRaised();
            if(click_to_focus)
	        activate();
        }
    }
}

void WindowManager::eventMapRequest(XMapRequestEvent *e) {
    Client *c = windowToClient(e->window);
    if(c)
        c->eventMapRequest(e);
}

void Client::eventMapRequest(XMapRequestEvent *e) {
    switch(m_state) {
        case WithdrawnState:
            if(parent() == root()) {
                manage(False);
                return;
            }
            if(m_hasBorder == True && layer == 4)
                m_border->reparent();
            if(auto_raise)
	        m_windowManager->stopConsideringFocus();
            XAddToSaveSet(display(), m_window);
            XMapWindow(display(), m_window);
            mapRaised();
            setState(NormalState);
            if(click_to_focus)
	        activate();
            break;
        case NormalState:
            XMapWindow(display(), m_window);
            mapRaised();
//            if(click_to_focus)
//	        activate();
            break;
        case IconicState:
            if(auto_raise)
	        m_windowManager->stopConsideringFocus();
            unhide(True);
            break;
    }
}

void WindowManager::eventUnmap(XUnmapEvent *e) {
    Client *c = windowToClient(e->window);
    if(c)
        c->eventUnmap(e);
}

void Client::eventUnmap(XUnmapEvent *e) {
    switch(m_state) {
        case IconicState:
            if(e->send_event) {
                unhide(False);
                withdraw();
            }
            break;
        case NormalState:
            if(isActive())
	        m_windowManager->clearFocus();
            if(!m_reparenting)
	        withdraw();
            break;
    }
    m_reparenting = False;
    m_stubborn = False;
}

void WindowManager::eventCreate(XCreateWindowEvent *e) {
    Client *c = windowToClient(e->window, True);
}

void WindowManager::eventDestroy(XDestroyWindowEvent *e) {
    Client *c = windowToClient(e->window);
    if(c) {
        if(auto_raise && m_focusChanging && c == m_focusCandidate) {
            m_focusChanging = False;
        }
        for(int i = m_clients.count()-1; i >= 0; --i) {
            if(m_clients.item(i) == c) {
                m_clients.remove(i);
                break;
            }
        }
        c->release();
        ignoreBadWindowErrors = True;
        XSync(display(), False);
        ignoreBadWindowErrors = False;
    }
}

void WindowManager::eventClient(XClientMessageEvent *e) {
    Client *c = windowToClient(e->window);
    if(e->message_type == Atoms::wm_changeState) {
        if(c && e->format == 32 && e->data.l[0] == IconicState && c != 0) {
            if(c->isNormal())
	        c->hide();
        }
        return;
    }
    if(e->message_type == Atoms::wm_protocols) {
        if(e->data.l[0] == Atoms::wm_takeFocus) {
            c->activate();
            if(raise_on_focus) {
                c->mapRaised();
            }
        }
        return;
    }

}

void WindowManager::eventColormap(XColormapEvent *e) {
    Client *c = windowToClient(e->window);
    int i;
    if(e->c_new) {
        if(c)
	    c->eventColormap(e);
        else {
            for(i = 0; i < m_clients.count(); ++i) {
                m_clients.item(i)->eventColormap(e);
            }
        }
    }
}

void Client::eventColormap(XColormapEvent *e) {
    if(e->window == m_window || e->window == parent()) {
        m_colormap = e->colormap;
        if(isActive())
	    installColormap();
    } else {
        for(int i = 0; i < m_colormapWinCount; ++i) {
            if(m_colormapWindows[i] == e->window) {
                m_windowColormaps[i] = e->colormap;
                if(isActive())
		    installColormap();
                return;
            }
        }
    }
}

void WindowManager::eventProperty(XPropertyEvent *e) {
    Client *c = windowToClient(e->window);
    if(c)
        c->eventProperty(e);
}

void Client::eventProperty(XPropertyEvent *e) {
    Atom a = e->atom;
    Boolean shouldDelete = (e->state == PropertyDelete);
    switch(a) {
        case XA_WM_ICON_NAME:
            if(m_iconName)
	        XFree((char *)m_iconName);
            m_iconName = shouldDelete ? 0 : getProperty(a);
            if(setLabel())
	        rename();
            return;
        case XA_WM_NAME:
            if(m_name)
	        XFree((char *)m_name);
            m_name = shouldDelete ? 0 : getProperty(a);
            if(setLabel())
	        rename();
            return;
        case XA_WM_TRANSIENT_FOR:
            getTransient();
            return;
    }
    if (a == Atoms::wm_colormaps) {
        getColormaps();
        if(isActive())
	    installColormap();
    }
}

void WindowManager::eventReparent(XReparentEvent *e) {
    (void)windowToClient(e->window, True);
}

void WindowManager::eventEnter(XCrossingEvent *e) {
    if(e->type != EnterNotify)
        return;
    while(XCheckMaskEvent(m_display, EnterWindowMask, (XEvent *)e));
    m_currentTime = e->time;
    Client *c = windowToClient(e->window);
    if(c)
        c->eventEnter(e);
}

void Client::eventEnter(XCrossingEvent *e) {
    if(e->type == EnterNotify) {
        if(!isActive() && !click_to_focus) {
            activate();
            if(auto_raise) {
                windowManager()->considerFocusChange(this, m_window, e->time);
            } else if(raise_on_focus) {
                mapRaised();
            }
        }
    }
}

void WindowManager::eventFocusIn(XFocusInEvent *e) {
    if(e->detail != NotifyNonlinearVirtual)
        return;
    Client *c = windowToClient(e->window);
    if(c)
        c->eventFocusIn(e);
}

void Client::eventFocusIn(XFocusInEvent *e) {
    if(m_window == e->window && !isActive()) {
        activate();
        mapRaised();
    }
}

void WindowManager::eventExposure(XExposeEvent *e) {
    if(e->count != 0)
        return;
    Client *c = windowToClient(e->window);
    if(c)
        c->eventExposure(e);
}

void Client::eventExposure(XExposeEvent *e) {
    if (m_border->hasWindow(e->window)) {
        m_border->expose(e);
    }
}


syntax highlighted by Code2HTML, v. 0.9.1