/*
 * Copyright (c) 2000-2009 TeamDev Ltd. All rights reserved.
 * TeamDev PROPRIETARY and CONFIDENTIAL.
 * Use is subject to license terms.
 */
package com.jniwrapper.win32.hook;

import com.jniwrapper.Bool;
import com.jniwrapper.util.Enums;
import com.jniwrapper.util.FlagSet;
import com.jniwrapper.win32.Point;
import com.jniwrapper.win32.Rect;
import com.jniwrapper.win32.hook.data.CREATESTRUCT;
import com.jniwrapper.win32.ui.Wnd;

/**
 * This class describes events of the {@link com.jniwrapper.win32.hook.Hook.Descriptor#CBT} hook.
 */
public abstract class CBTEvent extends HookEventObject
{
    private final Bool allow;

    public CBTEvent(Object source, Bool allow)
    {
        super(source);
        this.allow = allow;
    }

    /**
     * Calling this method determines whether the system allows or prevents the event.
     *
     * @param value true when event shold not be allowed; false otherwise
     */
    public void setAllow(boolean value) {
        if (allow != null)
        {
            allow.setValue(value);
        }
    }

    void dispatch(CBTHookListener listener) {
    }

    /**
     * Represents <code>HCBT_ACTIVATE</code> hook event.
     */
    public static class Activate extends CBTEvent
    {
        private final Wnd activeWindow;
        private final Wnd activatedWindow;
        private final boolean activatedByMouse;

        public Activate(Object source, Bool allow, Wnd activeWindow, Wnd activatedWindow, boolean activatedByMouse)
        {
            super(source, allow);

            this.activeWindow = activeWindow;
            this.activatedWindow = activatedWindow;
            this.activatedByMouse = activatedByMouse;
        }

        /**
         * Handle to the active window.
         *
         * @return window handle
         */
        public Wnd getActiveWindow()
        {
            return activeWindow;
        }

        /**
         * Handle to the window about to be activated.
         *
         * @return window handle
         */
        public Wnd getActivatedWindow()
        {
            return activatedWindow;
        }

        /**
         * Specifies whether the window is being activated as a result of a mouse click.
         *
         * @return true if window was activated by a mouse click; false otherwise.
         */
        public boolean isActivatedByMouse()
        {
            return activatedByMouse;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.activate(this);
        }
    }

    /**
     * Represents <code>HCBT_CLICKSKIPPED</code> hook event.
     */
    public static class ClickSkipped extends CBTEvent
    {
        private final long mouseMessage;
        private final Point cursorCoordinates;
        private final Wnd window;
        private final long hitTestCode;

        public ClickSkipped(Object source, long mouseMessage, Point point, Wnd window, long hitTestCode)
        {
            super(source, null);
            this.mouseMessage = mouseMessage;
            this.cursorCoordinates = point;
            this.window = window;
            this.hitTestCode = hitTestCode;
        }

        /**
         * Specifies the mouse message removed from the system message queue.
         *
         * @return mouse message
         */
        public long getMouseMessage()
        {
            return mouseMessage;
        }

        /**
         * Contains the x- and y-coordinates of the cursor, in screen coordinates.
         *
         * @return cursor coordinates
         */
        public Point getCursorCoordinates()
        {
            return cursorCoordinates;
        }

        /**
         * The window for which the mouse message is intended.
         *
         * @return window
         */
        public Wnd getWindow()
        {
            return window;
        }

        /**
         * Hit-test code
         *
         * @return hit-test code
         */
        public long getHitTestCode()
        {
            return hitTestCode;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.clickSkipped(this);
        }
    }

    /**
     * Represents <code>HCBT_CREATEWND</code> hook event.
     */
    public static class CreateWnd extends CBTEvent
    {
        private final Wnd newWindow;
        private final CREATESTRUCT createstruct;
        private final Wnd insertAfter;

        public CreateWnd(Object source, Bool allow, Wnd newWindow, CREATESTRUCT createstruct, Wnd insertAfter)
        {
            super(source, allow);
            this.newWindow = newWindow;
            this.createstruct = createstruct;
            this.insertAfter = insertAfter;
        }

        /**
         * Returns the handle to the new window.
         *
         * @return window handle
         */
        public Wnd getNewWindow()
        {
            return newWindow;
        }

        /**
         * Returns {@link CREATESTRUCT} structure of newly created window.
         *
         * @return create window structure
         */
        public CREATESTRUCT getCreatestruct()
        {
            return createstruct;
        }

        /**
         * Returns handle to the window whose position in the Z order precedes that of the window being created. Can be null.
         * @return window handle
         */
        public Wnd getInsertAfter()
        {
            return insertAfter;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.createWnd(this);
        }
    }

    /**
     * Represents <code>HCBT_DESTROYWND</code> hook event.
     */
    public static class DestroyWnd extends CBTEvent
    {
        private final Wnd destroyedWindow;

        public DestroyWnd(Object source, Bool allow, Wnd destroyedWindow)
        {
            super(source, allow);
            this.destroyedWindow = destroyedWindow;
        }

        /**
         * Returns the handle to the window about to be destoyed.
         *
         * @return window handle
         */
        public Wnd getDestoyedWindow()
        {
            return destroyedWindow;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.destroyWnd(this);
        }
    }

    /**
     * Represents <code>HCBT_KEYSKIPPED</code> hook event.
     */
    public static class KeySkipped extends CBTEvent
    {
        private final long virtualKeyCode;
        private final long keyInfo;

        public KeySkipped(Object source, long virtualKeyCode, long keyInfo)
        {
            super(source, null);
            this.virtualKeyCode = virtualKeyCode;
            this.keyInfo = keyInfo;
        }

        /**
         * Returns the virtual-key code.
         *
         * @return virtual-key code
         */
        public long getVirtualKeyCode()
        {
            return virtualKeyCode;
        }

        /**
         * Specifies the repeat count. The value is the number of times the
         * keystroke is repeated as a result of the user's holding down the key.
         *
         * @return the repeat count.
         */
        public int getRepeatCount()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return flagSet.getBits(0, 15);
        }

        /**
         * Specifies the scan code. The value depends on the OEM.
         *
         * @return scan code.
         */
        public int getScanCode()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return flagSet.getBits(16, 23);
        }

        /**
         * Specifies whether the key is an extended key, such as a function key or a
         * key on the numeric keypad.
         *
         * @return true, if the key is an extended key; otherwise, it is false.
         */
        public boolean isExtendedKey()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return flagSet.getBit(24);
        }

        /**
         * Specifies the context code.
         *
         * @return true, if the ALT key is down; otherwise, it is false.
         */
        public boolean isAltPressed()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return flagSet.getBit(29);
        }

        /**
         * Specifies the previous key state.
         *
         * @return true, if the key is down before the message is sent; false if the
         *         key is up.
         */
        public boolean getPreviousState()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return flagSet.getBit(30);
        }

        /**
         * Specifies the transition state.
         *
         * @return true, if the key is being pressed; false if it is being released.
         */
        public boolean getTransitionState()
        {
            FlagSet flagSet = new FlagSet(keyInfo);
            return !flagSet.getBit(31);
        }

        void dispatch(CBTHookListener listener)
        {
            listener.keySkipped(this);
        }
    }

    /**
     * Represents <code>HCBT_MINMAX</code> hook event.
     */
    public static class MinMax extends CBTEvent
    {
        private final Wnd windowHandle;
        private final long lParam;

        public MinMax(Object source, Bool allow, Wnd windowHandle, long lParam)
        {
            super(source, allow);
            this.windowHandle = windowHandle;
            this.lParam = lParam;
        }

        /**
         * Returns the handle to the window being minimized or maximized.
         *
         * @return window handle
         */
        public Wnd getWindowHandle()
        {
            return windowHandle;
        }

        /**
         * Returns show window command, represented by {@link Wnd.ShowWindowCommand} enumeration.
         *
         * @return show window command.
         */
        public Wnd.ShowWindowCommand getOperation()
        {
            int operation = (int) (lParam & 0xFFFF);
            return (Wnd.ShowWindowCommand) Enums.getItem(Wnd.ShowWindowCommand.class, operation);
        }

        void dispatch(CBTHookListener listener)
        {
            listener.minMax(this);
        }
    }

    /**
     * Represents <code>HCBT_MOVESIZE</code> hook event.
     */
    public static class MoveSize extends CBTEvent
    {
        private final Wnd windowHandle;
        private final Rect rect;

        public MoveSize(Object source, Bool allow, Wnd windowHandle, Rect rect)
        {
            super(source, allow);
            this.windowHandle = windowHandle;
            this.rect = rect;
        }

        /**
         * Returns the handle to the window to be moved or sized.
         *
         * @return window handle
         */
        public Wnd getWindowHandle()
        {
            return windowHandle;
        }

        /**
         * The coordinates of the window.
         *
         * @return rect of the window
         */
        public Rect getWindowRect()
        {
            return rect;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.moveSize(this);
        }
    }

    /**
     * Represents <code>QS</code> hook event.
     */
    public static class QS extends CBTEvent
    {
        public QS(Object source)
        {
            super(source, null);
        }

        void dispatch(CBTHookListener listener)
        {
            listener.qs(this);
        }
    }

    /**
     * Represents <code>SETFOCUS</code> hook event.
     */
    public static class SetFocus extends CBTEvent
    {
        private final Wnd windowGainingFocus;
        private final Wnd windowLosingFocus;

        public SetFocus(Object source, Bool allow, Wnd windowGainingFocus, Wnd windowLosingFocus)
        {
            super(source, allow);
            this.windowGainingFocus = windowGainingFocus;
            this.windowLosingFocus = windowLosingFocus;
        }

        /**
         * Specifies the handle to the window gaining the keyboard focus.
         *
         * @return window handle
         */
        public Wnd getWindowGainingFocus()
        {
            return windowGainingFocus;
        }

        /**
         * Specifies the handle to the window losing the keyboard focus.
         *
         * @return window handle
         */
        public Wnd getWindowLosingFocus()
        {
            return windowLosingFocus;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.setFocus(this);
        }
    }

    /**
     * Represents <code>SYSCOMMAND</code> hook event.
     */
    public static class SysCommand extends CBTEvent
    {
        private final long sysCommand;
        private final long info;

        public SysCommand(Object source, Bool allow, long sysCommand, long info)
        {
            super(source, allow);
            this.sysCommand = sysCommand;
            this.info = info;
        }

        /**
         * Specifies a system-command value (SC_) specifying the system command.
         *
         * @return system-command value
         */
        public long getSysCommand()
        {
            return sysCommand;
        }

        /**
         * The low-order word specifies the horizontal position of the cursor, in screen coordinates,
         * if a window menu command is chosen with the mouse. Otherwise, this parameter is not used.
         * <br>
         * The high-order word specifies the vertical position of the cursor, in screen coordinates,
         * if a window menu command is chosen with the mouse. This parameter is –1 if the command is chosen using a
         * system accelerator, or zero if using a mnemonic.
         *
         * @return info
         */
        public long getInfo()
        {
            return info;
        }

        void dispatch(CBTHookListener listener)
        {
            listener.sysCommand(this);
        }
    }
}