/*
 * Copyright (C) 2007 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.manifest.model;

import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;

import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import junit.framework.TestCase;

public class UiElementNodeTest extends TestCase {

    private UiElementNode ui;
    private ElementDescriptor mManifestDesc;
    private ElementDescriptor mAppDesc;
    private ElementDescriptor mUsesSdkDesc;

    @Override
    protected void setUp() throws Exception {
        mAppDesc = new ElementDescriptor("application", new ElementDescriptor[] {
                new ElementDescriptor("provider"),
                new ElementDescriptor("activity", new ElementDescriptor[] {
                    new ElementDescriptor("intent-filter")
                }),
            }, Mandatory.MANDATORY_LAST);

        mUsesSdkDesc = new ElementDescriptor("uses-sdk", new ElementDescriptor[] {},
                Mandatory.MANDATORY);

        mManifestDesc = new ElementDescriptor("manifest", new ElementDescriptor[] {
                mAppDesc,
                mUsesSdkDesc,
                new ElementDescriptor("permission")
            }, Mandatory.MANDATORY);

        ui = new UiElementNode(mManifestDesc);

        super.setUp();
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        // pass
    }

    /**
     * Check initialization values for ui node
     */
    public void testInit() {
        assertSame(mManifestDesc, ui.getDescriptor());
        assertNull(ui.getUiParent());
        assertEquals(0, ui.getUiChildren().size());
        assertEquals(0, ui.getAllUiAttributes().size());
    }

    /**
     * We declared the descriptors as having a "mandatory last" application element
     * and a mandatory non-last uses-sdk element. This means if we create an empty
     * UiModel, we should get these two created, with the application element after
     * the uses-sdk.
     */
    public void testMandatoryOrder() {
        // Add the mandatory nodes with no XML backing, do it explicitly in the wrong order.
        assertEquals(0, ui.getUiChildren().size());
        ui.appendNewUiChild(mAppDesc);
        ui.appendNewUiChild(mUsesSdkDesc);

        assertEquals(2, ui.getUiChildren().size());
        assertSame(mAppDesc,     ui.getUiChildren().get(0).getDescriptor());
        assertSame(mUsesSdkDesc, ui.getUiChildren().get(1).getDescriptor());

        // Parse an XML with just a manifest.
        MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE,
            new MockXmlNode[] {
                new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null)
            });

        ui.loadFromXmlNode(root);

        // We should get 2 children, the 2 mandatory nodes but this time with uses-sdk
        // before application since it's a mandatory-last so it "moves" to the end if possible.
        assertEquals(2, ui.getUiChildren().size());
        assertSame(mUsesSdkDesc, ui.getUiChildren().get(0).getDescriptor());
        assertNull(ui.getUiChildren().get(0).getXmlNode());
        assertSame(mAppDesc, ui.getUiChildren().get(1).getDescriptor());
        assertNotNull(ui.getUiChildren().get(1).getXmlNode());
    }

    /**
     * loadFrom() does nothing if the root node doesn't match what's expected
     */
    public void testLoadFrom_InvalidRoot() {
        assertEquals(0, ui.getUiChildren().size());
        MockXmlNode root = new MockXmlNode(null /* namespace */, "blah", Node.ELEMENT_NODE, null);
        ui.loadFromXmlNode(root);
        assertEquals(0, ui.getUiChildren().size());
    }

    /**
     * UiElementNode.loadFrom should be used to populate an empty ui node from an
     * existing XML node tree.
     */
    public void testLoadFrom_NewTree_1_Node() {
        MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE,
            new MockXmlNode[] {
                new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null)
            });

        // get /manifest
        ui.loadFromXmlNode(root);
        assertEquals("manifest", ui.getDescriptor().getXmlName());
        assertEquals(1, ui.getUiChildren().size());
        assertEquals(0, ui.getAllUiAttributes().size());

        // get /manifest/application
        Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator();
        UiElementNode application = ui_child_it.next();
        assertEquals("application", application.getDescriptor().getXmlName());
        assertEquals(0, application.getUiChildren().size());
        assertEquals(0, application.getAllUiAttributes().size());
    }


    public void testLoadFrom_NewTree_2_Nodes() {
        MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE,
            new MockXmlNode[] {
                new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null),
                new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE, null),
            });

        // get /manifest
        ui.loadFromXmlNode(root);
        assertEquals("manifest", ui.getDescriptor().getXmlName());
        assertEquals(2, ui.getUiChildren().size());
        assertEquals(0, ui.getAllUiAttributes().size());

        // get /manifest/application
        Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator();
        UiElementNode application = ui_child_it.next();
        assertEquals("application", application.getDescriptor().getXmlName());
        assertEquals(0, application.getUiChildren().size());
        assertEquals(0, application.getAllUiAttributes().size());
        assertEquals(0, application.getUiSiblingIndex());

        // get /manifest/permission
        UiElementNode first_permission = ui_child_it.next();
        assertEquals("permission", first_permission.getDescriptor().getXmlName());
        assertEquals(0, first_permission.getUiChildren().size());
        assertEquals(0, first_permission.getAllUiAttributes().size());
        assertEquals(1, first_permission.getUiSiblingIndex());
    }

    public void testLoadFrom_NewTree_N_Nodes() {
        MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE,
            new MockXmlNode[] {
                new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE,
                    new MockXmlNode[] {
                        new MockXmlNode(null /* namespace */, "activity", Node.ELEMENT_NODE,
                            null),
                        new MockXmlNode(null /* namespace */, "activity", Node.ELEMENT_NODE,
                            new MockXmlNode[] {
                                new MockXmlNode(null /* namespace */, "intent-filter",
                                        Node.ELEMENT_NODE, null),
                            }),
                        new MockXmlNode(null /* namespace */, "provider", Node.ELEMENT_NODE,
                                null),
                        new MockXmlNode(null /* namespace */, "provider", Node.ELEMENT_NODE,
                                null),
                    }),
                new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE,
                        null),
                new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE,
                        null),
            });

        // get /manifest
        ui.loadFromXmlNode(root);
        assertEquals("manifest", ui.getDescriptor().getXmlName());
        assertEquals(3, ui.getUiChildren().size());
        assertEquals(0, ui.getAllUiAttributes().size());

        // get /manifest/application
        Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator();
        UiElementNode application = ui_child_it.next();
        assertEquals("application", application.getDescriptor().getXmlName());
        assertEquals(4, application.getUiChildren().size());
        assertEquals(0, application.getAllUiAttributes().size());

        // get /manifest/application/activity #1
        Iterator<UiElementNode> app_child_it = application.getUiChildren().iterator();
        UiElementNode first_activity = app_child_it.next();
        assertEquals("activity", first_activity.getDescriptor().getXmlName());
        assertEquals(0, first_activity.getUiChildren().size());
        assertEquals(0, first_activity.getAllUiAttributes().size());

        // get /manifest/application/activity #2
        UiElementNode second_activity = app_child_it.next();
        assertEquals("activity", second_activity.getDescriptor().getXmlName());
        assertEquals(1, second_activity.getUiChildren().size());
        assertEquals(0, second_activity.getAllUiAttributes().size());

        // get /manifest/application/activity #2/intent-filter #1
        Iterator<UiElementNode> activity_child_it = second_activity.getUiChildren().iterator();
        UiElementNode intent_filter = activity_child_it.next();
        assertEquals("intent-filter", intent_filter.getDescriptor().getXmlName());
        assertEquals(0, intent_filter.getUiChildren().size());
        assertEquals(0, intent_filter.getAllUiAttributes().size());

        // get /manifest/application/provider #1
        UiElementNode first_provider = app_child_it.next();
        assertEquals("provider", first_provider.getDescriptor().getXmlName());
        assertEquals(0, first_provider.getUiChildren().size());
        assertEquals(0, first_provider.getAllUiAttributes().size());

        // get /manifest/application/provider #2
        UiElementNode second_provider = app_child_it.next();
        assertEquals("provider", second_provider.getDescriptor().getXmlName());
        assertEquals(0, second_provider.getUiChildren().size());
        assertEquals(0, second_provider.getAllUiAttributes().size());

        // get /manifest/permission #1
        UiElementNode first_permission = ui_child_it.next();
        assertEquals("permission", first_permission.getDescriptor().getXmlName());
        assertEquals(0, first_permission.getUiChildren().size());
        assertEquals(0, first_permission.getAllUiAttributes().size());

        // get /manifest/permission #1
        UiElementNode second_permission = ui_child_it.next();
        assertEquals("permission", second_permission.getDescriptor().getXmlName());
        assertEquals(0, second_permission.getUiChildren().size());
        assertEquals(0, second_permission.getAllUiAttributes().size());
    }


    public void testlookupNamespacePrefix() throws Exception {
        // Setup
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.newDocument();
        Element rootElement = document.createElement("root");
        Attr attr = document.createAttributeNS(XmlnsAttributeDescriptor.XMLNS_URI,
                "xmlns:customPrefix");
        attr.setValue(ANDROID_URI);
        rootElement.getAttributes().setNamedItemNS(attr);
        document.appendChild(rootElement);
        Element root = document.getDocumentElement();
        root.appendChild(document.createTextNode("    "));
        Element foo = document.createElement("foo");
        root.appendChild(foo);
        root.appendChild(document.createTextNode("    "));
        Element bar = document.createElement("bar");
        root.appendChild(bar);
        Element baz = document.createElement("baz");
        root.appendChild(baz);

        String prefix = UiElementNode.lookupNamespacePrefix(baz, ANDROID_URI);
        assertEquals("customPrefix", prefix);
    }
}
