//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed 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 de.ugoe.cs.autoquest.plugin.html.commands;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementGroup;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel.Traverser;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocumentSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElement;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElementSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServer;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServerSpec;
import de.ugoe.cs.util.console.GlobalDataContainer;
import de.ugoe.cs.util.console.TextConsole;

/**
 * @author Patrick Harms
 */
public class CMDcondenseHTMLGUIModelTest {

    /**
     * 
     */
    @Before
    public void setUp() {
        new TextConsole();
    }
    
    /**
     * 
     */
    @Test
    public void test_condense_01() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div_0" };
        
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
        
        executeCommand();
        
        String[] assertionSpec =
            { "/server/doc1/html/body/div" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_02() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div_0",
              "/server/doc1/html/body/div_1",
              "/server/doc1/html/body/div_2",
              "/server/doc1/html/body/div_3"};
            
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
            
        executeCommand();
        
        String[] assertionSpec =
            { "/server/doc1/html/body/div",
              "/server/doc1/html/body/div_1",
              "/server/doc1/html/body/div_2",
              "/server/doc1/html/body/div_3" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_03() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div_0",
              "/server/doc1/html/body/div_1",
              "/server/doc2/html/body/div_2",
              "/server/doc2/html/body/div_3"};
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/group_doc1/div",
              "/server/doc1/html/body/group_doc1/div_1",
              "/server/doc1/html/body/group_doc2/div_2",
              "/server/doc1/html/body/group_doc2/div_3" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_04() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div",
              "/server/doc1/html/body/div_1",
              "/server/doc2/html/body/div",
              "/server/doc2/html/body/div_1"};
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div",
              "/server/doc1/html/body/div_1" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_05() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div",
              "/server/doc1/html/body/div_1",
              "/server/doc2/html/body/div",
              "/server/doc2/html/body/div_2"};
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div",
              "/server/doc1/html/body/group_doc1/div_1",
              "/server/doc1/html/body/group_doc2/div_2" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_06() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div/div_1",
              "/server/doc1/html/body/div/div_2",
              "/server/doc1/html/body/div/div_3",
              "/server/doc1/html/body/div/div_4",
              "/server/doc1/html/body/div/div_5",
              "/server/doc1/html/body/div/div_6",
              "/server/doc2/html/body/div/div_1",
              "/server/doc2/html/body/div/div_2",
              "/server/doc2/html/body/div/div_7",
              "/server/doc2/html/body/div/div_8",
              "/server/doc2/html/body/div/div_6",
              "/server/doc3/html/body/div/div_9",
              "/server/doc3/html/body/div/div_10",
              "/server/doc3/html/body/div/div_6",
              "/server/doc3/html/body/div/div_11",
              "/server/doc4/html/body/div/div_12",
              "/server/doc4/html/body/div/div_13",
              "/server/doc5/html/body/div/div_4",
              "/server/doc5/html/body/div/div_11" };
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/div_1",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/div_2",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/group_doc1/div_3",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/group_doc1/div_5",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/group_doc2/div_7",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/group_doc2/div_8",
              "/server/doc1/html/body/div/group_doc1doc2doc3/div_6",
              "/server/doc1/html/body/div/group_doc1doc5/div_4",
              "/server/doc1/html/body/div/group_doc3doc5/div_11",
              "/server/doc1/html/body/div/group_doc3doc5/group_doc3/div_9",
              "/server/doc1/html/body/div/group_doc3doc5/group_doc3/div_10",
              "/server/doc1/html/body/div/group_doc4/div_12",
              "/server/doc1/html/body/div/group_doc4/div_13" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_07() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div/div_1/div_11",
              "/server/doc2/html/body/div/div_2/div_21",
              "/server/doc3/html/body/div/div_1/div_12" };
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div/group_doc1doc3/div_1/group_doc1/div_11",
              "/server/doc1/html/body/div/group_doc1doc3/div_1/group_doc3/div_12",
              "/server/doc1/html/body/div/group_doc2/div_2/div_21" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_08() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div/div_1/div_11/div_111",
              "/server/doc1/html/body/div/div_2/div_21/div_211",
              "/server/doc1/html/body/div/div_3/div_31",
              "/server/doc2/html/body/div/div_1/div_11/div_111",
              "/server/doc2/html/body/div/div_2/div_21/div_211",
              "/server/doc2/html/body/div/div_3/div_31",
              "/server/doc2/html/body/div/div_4/div_41",
              "/server/doc3/html/body/div" };
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div/group_doc1doc2/div_1/div_11/div_111",
              "/server/doc1/html/body/div/group_doc1doc2/div_2/div_21/div_211",
              "/server/doc1/html/body/div/group_doc1doc2/div_3/div_31",
              "/server/doc1/html/body/div/group_doc1doc2/group_doc2/div_4/div_41" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_09() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div/div_1/div_11/div_111",
              "/server/doc1/html/body/div/div_2/div_21/div_211",
              "/server/doc1/html/body/div/div_3/div_31",
              "/server/doc2/html/body/div/div_1/div_12/div_121",
              "/server/doc2/html/body/div/div_2/div_22/div_221",
              "/server/doc2/html/body/div/div_3/div_31",
              "/server/doc2/html/body/div/div_4/div_41",
              "/server/doc3/html/body/div" };
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div/group_doc1doc2/div_1/group_doc1/div_11/div_111",
              "/server/doc1/html/body/div/group_doc1doc2/div_1/group_doc2/div_12/div_121",
              "/server/doc1/html/body/div/group_doc1doc2/div_2/group_doc1/div_21/div_211",
              "/server/doc1/html/body/div/group_doc1doc2/div_2/group_doc2/div_22/div_221",
              "/server/doc1/html/body/div/group_doc1doc2/div_3/div_31",
              "/server/doc1/html/body/div/group_doc1doc2/group_doc2/div_4/div_41" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     * 
     */
    @Test
    public void test_condense_10() throws Exception {
        String[] guiSpec =
            { "/server/doc1/html/body/div/div_1/div_11/div_111",
              "/server/doc1/html/body/div/div_2/div_21/div_211",
              "/server/doc1/html/body/div/div_3/div_31/div_311",
              "/server/doc1/html/body/div/div_4/div_41/div_411",
              "/server/doc1/html/body/div/div_5/div_51/div_511",
              "/server/doc2/html/body/div/div_2/div_21/div_211",
              "/server/doc2/html/body/div/div_3/div_31/div_311",
              "/server/doc2/html/body/div/div_4/div_41/div_411",
              "/server/doc2/html/body/div/div_5/div_51/div_511",
              "/server/doc3/html/body/div/div_3/div_31/div_311",
              "/server/doc3/html/body/div/div_4/div_41/div_411",
              "/server/doc3/html/body/div/div_5/div_51/div_511",
              "/server/doc4/html/body/div/div_4/div_41/div_411",
              "/server/doc4/html/body/div/div_5/div_51/div_511" };
                
        GUIModel guiModel = generateGUIModel(guiSpec);
        GlobalDataContainer.getInstance().addData("sequences_targets", guiModel);
                
        executeCommand();
            
        String[] assertionSpec =
            { "/server/doc1/html/body/div/div_4/div_41/div_411",
              "/server/doc1/html/body/div/div_5/div_51/div_511",
              "/server/doc1/html/body/div/group_doc1doc2doc3/div_3/div_31/div_311",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/div_2/div_21/div_211",
              "/server/doc1/html/body/div/group_doc1doc2doc3/group_doc1doc2/group_doc1/div_1/div_11/div_111" };
        
        assertGUIModel(guiModel, assertionSpec);
    }

    /**
     *
     */
    private GUIModel generateGUIModel(String[] guiSpec)
        throws IllegalArgumentException, GUIModelException
    {
        GUIModel model = new GUIModel(true);
        
        Map<String, HTMLServerSpec> servers = new HashMap<String, HTMLServerSpec>();
        Map<String, HTMLDocumentSpec> documents = new HashMap<String, HTMLDocumentSpec>();
        Map<String, HTMLPageElementSpec> pageElements = new HashMap<String, HTMLPageElementSpec>();
        
        for (String path : guiSpec) {
            String[] elements = path.split("/");
            
            if ("".equals(elements[0])) {
                elements = Arrays.copyOfRange(elements, 1, elements.length);
            }
            
            List<IGUIElementSpec> pathList = new LinkedList<IGUIElementSpec>();
            for (int i = 0; i < elements.length; i++) {
                if (i == 0) {
                    if (!servers.containsKey(elements[i])) {
                        servers.put(elements[i], new HTMLServerSpec(elements[i], 80));
                    }
                    pathList.add(servers.get(elements[i]));
                }
                else if (i == 1) {
                    if (!documents.containsKey(elements[i])) {
                        HTMLDocumentSpec doc = new HTMLDocumentSpec
                            ((HTMLServerSpec) pathList.get(0), elements[i], null, elements[i]);
                        documents.put(elements[i], doc);
                    }
                    pathList.add(documents.get(elements[i]));
                }
                else {
                    if (!pageElements.containsKey(elements[i])) {
                        String[] infos = elements[i].split("_");
                    
                        String tagName = infos[0];
                    
                        int index = 0;
                        String id = null;
                    
                        if (infos.length > 1) {
                            try {
                                index = Integer.parseInt(infos[1]);
                            }
                            catch (Exception e) {
                                id = infos[1];
                                index = -1;
                            }
                        }

                        HTMLPageElementSpec pageElem = new HTMLPageElementSpec
                            ((HTMLDocumentSpec) pathList.get(1), tagName, id, index);
                        pageElements.put(elements[i], pageElem);
                    }

                    pathList.add(pageElements.get(elements[i]));
                }
            }
            
            model.integratePath(pathList, GUIElementFactory.getInstance());
        }
        
        return model;
    }

    /**
     * 
     */
    private void executeCommand() {
        CMDcondenseHTMLGUIModel command = new CMDcondenseHTMLGUIModel();
        
        List<Object> parameters = new LinkedList<Object>();
        parameters.add("sequences");
        
        command.run(parameters);
    }

    /**
     *
     */
    private void assertGUIModel(GUIModel guiModel, String[] assertionSpec) {
        List<String> paths = new LinkedList<String>();
        
        for (String path : assertionSpec) {
            paths.add(path);
        }
        
        GUIModel.Traverser traverser = guiModel.getTraverser();
        
        assertPaths(traverser, "", paths);
        
        assertEquals(0, paths.size());
    }

    /**
     *
     */
    private void assertPaths(Traverser traverser, String path, List<String> paths) {
        if (traverser.hasFirstChild()) {
            IGUIElement childElement = traverser.firstChild();
        
            while (childElement != null) {
                String currentPath = path + "/" + getPathElement(childElement);
                assertPaths(traverser, currentPath, paths);
                childElement = traverser.nextSibling();
            }
        
            traverser.parent();
        }
        else {
            assertTrue("unexpected path: " + path, paths.remove(path));
        }
    }

    /**
     *
     */
    private String getPathElement(IGUIElement childElement) {
        StringBuffer name = new StringBuffer();
        
        if (childElement instanceof HTMLServer) {
            name.append(((HTMLServer) childElement).getHost());
        }
        else if (childElement instanceof HTMLDocument) {
            name.append(((HTMLDocument) childElement).getPath());
        }
        else if (childElement instanceof HTMLPageElement) {
            name.append(((HTMLPageElement) childElement).getTagName());
            
            if (((HTMLPageElement) childElement).getHtmlId() != null) {
                name.append("_");
                name.append(((HTMLPageElement) childElement).getHtmlId());
            }
            else if (((HTMLPageElement) childElement).getIndex() > 0) {
                name.append("_");
                name.append(((HTMLPageElement) childElement).getIndex());
            }
        }
        else if (childElement instanceof GUIElementGroup) {
            name.append(((GUIElementGroup) childElement).getStringIdentifier());
            
            /*for (IGUIElement groupedElement : ((GUIElementGroup) childElement).getGroupedElements())
            {
                name.append(getPathElement(groupedElement));
            }*/
        }
        
        return name.toString();
    }

}
