/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/


package org.openoffice.accessibility;

import com.sun.star.accessibility.AccessibleRole;
import com.sun.star.accessibility.XAccessible;
import com.sun.star.accessibility.XAccessibleContext;
import com.sun.star.awt.XExtendedToolkit;
import com.sun.star.awt.XTopWindow;
import com.sun.star.awt.XTopWindowListener;
import com.sun.star.awt.XWindow;
import com.sun.star.comp.loader.FactoryHelper;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XInitialization;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.registry.*;
import com.sun.star.uno.*;

import org.openoffice.java.accessibility.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.accessibility.Accessible;


public class AccessBridge {
    // 
    protected static java.util.Hashtable topWindowMap = new java.util.Hashtable();

    private static java.awt.Window getTopWindowImpl(XAccessible xAccessible) {
        // Because it can not be garantied that 
        // WindowsAccessBridgeAdapter.registerTopWindow() is called 
        // before windowOpened(), we have to make this operation 
        // atomic.
        synchronized (topWindowMap) {
            String oid = UnoRuntime.generateOid(xAccessible);
            java.awt.Window w = (java.awt.Window) topWindowMap.get(oid);

            if (w == null) {
                w = AccessibleObjectFactory.getTopWindow(xAccessible);

                if (w != null) {
                    topWindowMap.put(oid, w);
                }
            }

            return w;
        }
    }

    protected static java.awt.Window getTopWindow(XAccessible xAccessible) {
        if (xAccessible != null) {
            XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext();
            if (xAccessibleContext != null) {

                // Toolkit reports the VCL peer windows as toplevels. These have an
                // accessible parent which represents the native frame window
                switch(xAccessibleContext.getAccessibleRole()) {
                    case AccessibleRole.ROOT_PANE:
                    case AccessibleRole.POPUP_MENU:
                        return getTopWindow(xAccessibleContext.getAccessibleParent());
                        
                    case AccessibleRole.WINDOW:
                    case AccessibleRole.FRAME:
                    case AccessibleRole.DIALOG:
                    case AccessibleRole.ALERT:
                        return getTopWindowImpl(xAccessible);
                        
                    default:
                        break;
                }
            }
        }

        return null;
    }

    protected static java.awt.Window removeTopWindow(XAccessible xAccessible) {
        if (xAccessible != null) {
            XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext();
            if (xAccessibleContext != null) {

                // Toolkit reports the VCL peer windows as toplevels. These have an
                // accessible parent which represents the native frame window
                switch(xAccessibleContext.getAccessibleRole()) {
                    case AccessibleRole.ROOT_PANE:
                    case AccessibleRole.POPUP_MENU:
                        return removeTopWindow(xAccessibleContext.getAccessibleParent());
                        
                    case AccessibleRole.WINDOW:
                    case AccessibleRole.FRAME:
                    case AccessibleRole.DIALOG:
                        return (java.awt.Window) topWindowMap.remove(UnoRuntime.generateOid(xAccessible));
                        
                    default:
                        break;
                }
            }
        }

        return null;
    }

    public static XSingleServiceFactory __getServiceFactory(String implName,
        XMultiServiceFactory multiFactory, XRegistryKey regKey) {
        XSingleServiceFactory xSingleServiceFactory = null;

        if (implName.equals(AccessBridge.class.getName())) {
            // Initialize toolkit to register at Java <-> Windows access bridge
            java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit();

            xSingleServiceFactory = FactoryHelper.getServiceFactory(_AccessBridge.class,
                    _AccessBridge._serviceName, multiFactory, regKey);
        }

        return xSingleServiceFactory;
    }

    static public class _AccessBridge implements XTopWindowListener,
        XInitialization, XComponent {
        static final String _serviceName = "com.sun.star.accessibility.AccessBridge";
        XComponentContext xComponentContext;

        public _AccessBridge(XComponentContext xComponentContext) {
            this.xComponentContext = xComponentContext;
        }

        /*
        * XInitialization
        */
        public void initialize(java.lang.Object[] arguments) {
            try {
                // FIXME: Currently there is no way to determine if key event forwarding is needed or not,
                // so we have to do it always ..
                XExtendedToolkit unoToolkit = (XExtendedToolkit) AnyConverter.toObject(new Type(
                            XExtendedToolkit.class), arguments[0]);

                if (unoToolkit != null) {
                    // FIXME this should be done in VCL
                    unoToolkit.addTopWindowListener(this);

                    String os = (String) System.getProperty("os.name");
                    
                    // Try to initialize the WindowsAccessBridgeAdapter
                    if (os.startsWith("Windows")) {
                        WindowsAccessBridgeAdapter.attach(xComponentContext);
                    } else {
                        unoToolkit.addKeyHandler(new KeyHandler());
                    }
                } else if (Build.DEBUG) {
                    System.err.println(
                        "argument 0 is not of type XExtendedToolkit.");
                }
            } catch (com.sun.star.lang.IllegalArgumentException e) {
                // FIXME: output
            }
        }

        /*
        * XTopWindowListener
        */
        public void windowOpened(com.sun.star.lang.EventObject event) {
            XAccessible xAccessible = (XAccessible) UnoRuntime.queryInterface(XAccessible.class,
                    event.Source);
            java.awt.Window w = getTopWindow(xAccessible);
        }

        public void windowActivated(com.sun.star.lang.EventObject event) {
        }

        public void windowDeactivated(com.sun.star.lang.EventObject event) {
        }

        public void windowMinimized(com.sun.star.lang.EventObject event) {
        }

        public void windowNormalized(com.sun.star.lang.EventObject event) {
        }

        public void windowClosing(com.sun.star.lang.EventObject event) {
        }

        public void windowClosed(com.sun.star.lang.EventObject event) {
            XAccessible xAccessible = (XAccessible) UnoRuntime.queryInterface(XAccessible.class,
                    event.Source);

            java.awt.Window w = removeTopWindow(xAccessible);

            if (w != null) {
                w.dispose();
            }
        }

        public void disposing(com.sun.star.lang.EventObject event) {
        }
	
        /*
        * XComponent
        */
        
        public void addEventListener(com.sun.star.lang.XEventListener listener) {
        }
        
        public void removeEventListener(com.sun.star.lang.XEventListener listener) {
        }
        
        public void dispose() {
            try {
                java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().invokeAndWait(
                    new Runnable() {
                        public void run() {
                        }
                    } );
            } catch (java.lang.InterruptedException e) {
            } catch (java.lang.reflect.InvocationTargetException e) {
            }
        }
    }
}
