/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
 *
 * 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 com.android.ide.eclipse.adt.internal.editors.layout.descriptors;

import static com.android.ide.common.layout.LayoutConstants.ANDROID_VIEW_PKG;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_WEBKIT_PKG;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;

import com.android.ide.common.resources.platform.AttributeInfo;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;

import org.eclipse.swt.graphics.Image;

import java.util.Collections;
import java.util.List;

/**
 * {@link ViewElementDescriptor} describes the properties expected for a given XML element node
 * representing a class in an XML Layout file.
 * <p/>
 * These descriptors describe Android views XML elements.
 * <p/>
 * The base class {@link ElementDescriptor} has a notion of "children", that is an XML element
 * can produce another set of XML elements. Because of the flat nature of Android's layout
 * XML files all possible views are children of the document and of themselves (that is any
 * view group can contain any other view). This is an implied contract of this class that is
 * enforces at construction by {@link LayoutDescriptors}. Note that by construction any code
 * that deals with the children hierarchy must also deal with potential infinite loops since views
 * <em>will</em> reference themselves (e.g. a ViewGroup can contain a ViewGroup).
 * <p/>
 * Since Views are also Java classes, they derive from each other. Here this is represented
 * as the "super class", which denotes the fact that a given View java class derives from
 * another class. These properties are also set at construction by {@link LayoutDescriptors}.
 * The super class hierarchy is very different from the descriptor's children hierarchy: the
 * later represents Java inheritance, the former represents an XML nesting capability.
 *
 * @see ElementDescriptor
 */
public class ViewElementDescriptor extends ElementDescriptor {

    /** The full class name (FQCN) of this view. */
    private final String mFullClassName;

    /** The list of layout attributes. Can be empty but not null. */
    private AttributeDescriptor[] mLayoutAttributes;

    /** The super-class descriptor. Can be null. */
    private ViewElementDescriptor mSuperClassDesc;

    /** List of attribute sources, classes that contribute attributes to {@link #mAttributes} */
    private List<String> mAttributeSources;

    /**
     * Constructs a new {@link ViewElementDescriptor} based on its XML name, UI name,
     * the canonical name of the class it represents, its tooltip, its SDK url, its attributes list,
     * its children list and its mandatory flag.
     *
     * @param xml_name The XML element node name. Case sensitive.
     * @param ui_name The XML element name for the user interface, typically capitalized.
     * @param fullClassName The fully qualified class name the {@link ViewElementDescriptor} is
     *          representing.
     * @param tooltip An optional tooltip. Can be null or empty.
     * @param sdk_url An optional SKD URL. Can be null or empty.
     * @param attributes The list of allowed attributes. Can be null or empty.
     * @param layoutAttributes The list of layout attributes. Can be null or empty.
     * @param children The list of allowed children. Can be null or empty.
     * @param mandatory Whether this node must always exist (even for empty models). A mandatory
     *  UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
     *  UI node MUST have an XML node attached and it will cease to exist when the XML node
     *  ceases to exist.
     */
    public ViewElementDescriptor(String xml_name, String ui_name,
            String fullClassName,
            String tooltip, String sdk_url,
            AttributeDescriptor[] attributes, AttributeDescriptor[] layoutAttributes,
            ElementDescriptor[] children, boolean mandatory) {
        super(xml_name, ui_name, tooltip, sdk_url, attributes, children, mandatory);
        mFullClassName = fullClassName;
        mLayoutAttributes = layoutAttributes != null ? layoutAttributes : new AttributeDescriptor[0];
    }

    /**
     * Constructs a new {@link ElementDescriptor} based on its XML name and on the canonical
     * name of the class it represents.
     * The UI name is build by capitalizing the XML name.
     * The UI nodes will be non-mandatory.
     *
     * @param xml_name The XML element node name. Case sensitive.
     * @param fullClassName The fully qualified class name the {@link ViewElementDescriptor} is
     * representing.
     */
    public ViewElementDescriptor(String xml_name, String fullClassName) {
        super(xml_name);
        mFullClassName = fullClassName;
        mLayoutAttributes = null;
    }

    /**
     * Returns the fully qualified name of the View class represented by this element descriptor
     * e.g. "android.view.View".
     *
     * @return the fully qualified class name, never null
     */
    public String getFullClassName() {
        return mFullClassName;
    }

    /** Returns the list of layout attributes. Can be empty but not null.
     *
     * @return the list of layout attributes, never null
     */
    public AttributeDescriptor[] getLayoutAttributes() {
        return mLayoutAttributes;
    }

    /**
     * Sets the list of layout attribute attributes.
     *
     * @param attributes the new layout attributes, not null
     */
    public void setLayoutAttributes(AttributeDescriptor[] attributes) {
        assert attributes != null;
        mLayoutAttributes = attributes;
    }

    /**
     * Returns a new {@link UiViewElementNode} linked to this descriptor.
     */
    @Override
    public UiElementNode createUiNode() {
        return new UiViewElementNode(this);
    }

    /**
     * Returns the {@link ViewElementDescriptor} of the super-class of this View descriptor
     * that matches the java View hierarchy. Can be null.
     *
     * @return the super class' descriptor or null
     */
    public ViewElementDescriptor getSuperClassDesc() {
        return mSuperClassDesc;
    }

    /**
     * Sets the {@link ViewElementDescriptor} of the super-class of this View descriptor
     * that matches the java View hierarchy. Can be null.
     *
     * @param superClassDesc the descriptor for the super class, or null
     */
    public void setSuperClass(ViewElementDescriptor superClassDesc) {
        mSuperClassDesc = superClassDesc;
    }

    /**
     * Returns an optional icon for the element.
     * <p/>
     * By default this tries to return an icon based on the XML name of the element.
     * If this fails, it tries to return the default element icon as defined in the
     * plugin. If all fails, it returns null.
     *
     * @return An icon for this element or null.
     */
    @Override
    public Image getGenericIcon() {
        IconFactory factory = IconFactory.getInstance();
        String name = mXmlName;
        if (name.indexOf('.') != -1) {
            // If the user uses a fully qualified name, such as
            // "android.gesture.GestureOverlayView" in their XML, we need to look up
            // only by basename
            name = name.substring(name.lastIndexOf('.') + 1);
        }

        Image icon = factory.getIcon(name);
        if (icon == null) {
            icon = AdtPlugin.getAndroidLogo();
        }

        return icon;
    }

    /**
     * Returns the list of attribute sources for the attributes provided by this
     * descriptor. An attribute source is the fully qualified class name of the
     * defining class for some of the properties. The specific attribute source
     * of a given {@link AttributeInfo} can be found by calling
     * {@link AttributeInfo#getDefinedBy()}.
     * <p>
     * The attribute sources are ordered from class to super class.
     * <p>
     * The list may <b>not</b> be modified by clients.
     *
     * @return a non null list of attribute sources for this view
     */
    public List<String> getAttributeSources() {
        return mAttributeSources != null ? mAttributeSources : Collections.<String>emptyList();
    }

    /**
     * Sets the attribute sources for this view. See {@link #getAttributes()}
     * for details.
     *
     * @param attributeSources a non null list of attribute sources for this
     *            view descriptor
     * @see #getAttributeSources()
     */
    public void setAttributeSources(List<String> attributeSources) {
        mAttributeSources = attributeSources;
    }

    /**
     * Returns true if views with the given fully qualified class name need to include
     * their package in the layout XML tag
     *
     * @param fqcn the fully qualified class name, such as android.widget.Button
     * @return true if the full package path should be included in the layout XML element
     *         tag
     */
    public static boolean viewNeedsPackage(String fqcn) {
        return !(fqcn.startsWith(ANDROID_WIDGET_PREFIX)
              || fqcn.startsWith(ANDROID_VIEW_PKG)
              || fqcn.startsWith(ANDROID_WEBKIT_PKG));
    }
}
