/*******************************************************************************
 * Copyright (c) 2009, 2018 Red Hat, Inc.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class XMLParser {

    private Map<Integer, HashMap<String,String>> keyValues;
    private List<Integer> idList;
    private int id;
    private int currentlyIn;
    private static final String ATTR_NAME = "name"; //$NON-NLS-1$
    private static final String ATTR_TEXT = "text"; //$NON-NLS-1$
    private static final String noName = "NoName"; //$NON-NLS-1$
    private boolean textMode;
    XMLParser() {
        id = 0;
        currentlyIn = 0;
        if (keyValues != null) {
            keyValues.clear();
        }
        keyValues = new HashMap<>();

        if (idList != null) {
            idList.clear();
        }
        idList = new ArrayList<>();

        textMode = false;
    }

    /**
     * Helper method to call parse on the contents of a file.
     * @param file
     */
    public void parse(File file) {
        parse(getContents(file));
    }


    /**
     * Parses a String according to XML formatting rules. Will return a HashMap indexed by an
     * identification number, where each value is a further HashMap of String, String pairs representing
     * attributes.
     * @param message
     */
    public void parse(String message) {
        String tabstrip = message.replaceAll("\t", ""); //$NON-NLS-1$ //$NON-NLS-2$
        String[] lines = tabstrip.split("\n"); //$NON-NLS-1$

        for (String line : lines) {
            if (line.length() < 1) {
                continue;
            }

            if (line.charAt(0) == '<') {
                //Either an open or close tag
                if (line.charAt(1) == '/') {
                    //Closing tag -- assume properly formed
                    idList.remove((Integer) currentlyIn);
                    currentlyIn = -1;
                    if (idList.size() > 0) {
                        currentlyIn = idList.get(idList.size()-1);
                    }
                    setTextMode(true);

                } else if (line.substring(line.length()-2, line.length() - 1).equals("/>")) { //$NON-NLS-1$
                    //This tag opens and closes in one line
                    id++;
                    String[] tokens = line.split(" "); //$NON-NLS-1$
                    HashMap<String,String> map = new HashMap<>();
                    map.put(ATTR_NAME, tokens[0]);
                    keyValues.put(id,map);
                    textMode = false;
                    addAttributes(currentlyIn, tokens, 1);

                } else {
                    //Open tag
                    idList.add(id);
                    id++;
                    currentlyIn = id;

                    String[] tokens = line.split(" "); //$NON-NLS-1$

                    //Add name variable
                    HashMap<String,String> map = new HashMap<>();
                    map.put(ATTR_NAME, tokens[0]);
                    keyValues.put(id,map);

                    addAttributes(currentlyIn, tokens, 1);
                }
            } else {
                //Attribute addition
                if (currentlyIn < 0 ) {
                    continue;
                }

                if (textMode) {
                    HashMap<String,String> map = keyValues.get(currentlyIn);
                    map.put(ATTR_TEXT, line);
                }

                String[] tokens = line.split(" "); //$NON-NLS-1$
                addAttributes(currentlyIn, tokens, 0);
            }
        }
    }

    /**
     * Turns an array of Strings of form attribute=value into attribute/value pairings for the specified node.
     * Starts looking at token number start.
     * @param tokens
     * @param start
     */
    private void addAttributes(int id, String[] tokens, int start) {
        HashMap<String,String> map = keyValues.get(id);
        int nameless = 0;


        for (int j = start; j < tokens.length; j++) {
            String[] kvPair = tokens[j].split("="); //$NON-NLS-1$
            String value = ""; //$NON-NLS-1$
            String key = ""; //$NON-NLS-1$
            if (kvPair.length < 1) {
                continue;
            }

            if (kvPair.length < 2) {
                value = kvPair[0];
                if (value.charAt(value.length() - 1) == '>') {

                    setTextMode(true);
                    value = value.substring(0, value.length()-1);
                }
                map.put(noName + nameless, value);
                nameless++;
                continue;
            }

            value = kvPair[0];
            key = kvPair[1];
            if (value.charAt(value.length() - 1) == '>') {
                setTextMode(true);
                value = value.substring(0, value.length()-1);
            }

            map.put(key, value);
        }

        keyValues.put(id, map);
    }

    private static String getContents(File file) {
        StringBuilder contents = new StringBuilder();

        try (BufferedReader input = new BufferedReader(new FileReader(file))) {
            String line = null;
            while ((line = input.readLine()) != null) {
                contents.append(line);
                contents.append("\n"); //$NON-NLS-1$
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        return contents.toString();
    }

      private void setTextMode(boolean val) {
          textMode = val;
      }
}
