/*
 * 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.netbeans.modules.progress.spi;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JLabel;
import org.openide.modules.ConstructorDelegate;
import org.openide.modules.PatchFor;
import org.openide.util.*;

/**
 * Resurrects the method removed from InternalHandle. Unline other *Compat
 * classes, we need to bridge all requests to some real implementation. The
 * is theoretically able to instantiate an InternalHandle directly, without
 * using a factory. Still the Handle should work somehow, so a Swing-based delegate
 * is created (old clients are used to that). The InternalHandle itself
 * is changed so if it has a delegate, delegates everything instead of executing
 * its own code. That way, the direct instance of InternalHandle works in the
 * current NetBeans environment - forwards everything to a proper implementation.
 * <p/>
 * Sadly, this module cannot depend on api.progress.nb, as it would create a
 * circular dependency between api.progress.nb and api.progress, so all the work
 * must be done using reflection.
 * 
 * @author sdedic
 */
@PatchFor(InternalHandle.class)
public class InternalHandleCompat {
    static final Class uiClazz;
    static final Constructor ctor;
    static final Method component;
    static final Method detailLabel;
    static final Method mainLabel;
    static {
        try {
            uiClazz = Class.forName("org.netbeans.modules.progress.spi.UIInternalHandle",
                    true, Lookup.getDefault().lookup(ClassLoader.class));
            ctor = uiClazz.getDeclaredConstructor(
                    String.class,
                    Cancellable.class,
                    Boolean.TYPE,
                    javax.swing.Action.class);
            component = uiClazz.getMethod("extractComponent");
            detailLabel = uiClazz.getMethod("extractDetailLabel");
            mainLabel = uiClazz.getMethod("extractMainLabel");
        } catch (ClassNotFoundException ex) {
            throw new IllegalStateException(ex);
        } catch (NoSuchMethodException ex) {
            throw new IllegalStateException(ex);
        } catch (SecurityException ex) {
            throw new IllegalStateException(ex);
        }
    }
    
    /**
     * Delegate from InternalHandle, duplicate, but provides better access.
     */
    private InternalHandle delegate;
    
    public synchronized JComponent extractComponent() {
        try {
            return (JComponent)component.invoke(delegate);
        } catch (IllegalAccessException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        } catch (InvocationTargetException ex) {
            Exceptions.printStackTrace(ex);
        }
        return null;
    }

    public synchronized JLabel extractDetailLabel() {
        try {
            return (JLabel)detailLabel.invoke(delegate);
        } catch (IllegalAccessException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        } catch (InvocationTargetException ex) {
            Exceptions.printStackTrace(ex);
        }
        return null;
    }

    public synchronized JLabel extractMainLabel() {
        try {
            return (JLabel)mainLabel.invoke(delegate);
        } catch (IllegalAccessException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        } catch (InvocationTargetException ex) {
            Exceptions.printStackTrace(ex);
        }
        return null;
    }
    
    @ConstructorDelegate(delegateParams = { 1, 2, 3 })
    public static void create(InternalHandleCompat c, 
            String displayName, Cancellable cancel, boolean userInitiated, 
            Action action) {
        InternalHandle ih = (InternalHandle)(Object)c;
        if (ih.getClass() != InternalHandle.class) {
            return;
        }
        try {
            c.delegate  = (InternalHandle) ctor.newInstance(displayName, cancel, userInitiated, action);
            ih.del = c.delegate;
        } catch (InstantiationException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalAccessException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        } catch (InvocationTargetException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    
    /**
     * Backports data from public constructor. This method is called from the public
     * constructor of InternalHandle, so the delegate is initialized even when
     * using 'legal' but direct creation 
     * @param s display name
     * @param c cancel detection callback
     * @param u true if user-initiated handle.
     */
    protected void compatInit(String s, Cancellable c, boolean u) {
        create(this, s, c, u, null);
    }
}
