package de.renew.gui.pnml.parser;

import java.awt.Point;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import CH.ifa.draw.figures.TextFigure;
import CH.ifa.draw.framework.ChildFigure;
import CH.ifa.draw.framework.FigureWithID;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.gui.CPNDrawing;
import de.renew.gui.CPNTextFigure;
import de.renew.gui.pnml.converter.Converter;


/**
 * parse Nets
 */
public class NetParser extends ElementParser {
    // Biggest (int)Id found in ids;
    private int _maxId;

    // Mapping between PNML and renew ids (String => Int);
    private final Hashtable<String, Integer> _mapping;
    private Drawing _net;

    public NetParser(Element element) {
        super(element, null);
        _mapping = new Hashtable<>();
        _maxId = 0;
    }

    private void trySetMaxId(Integer id) {
        int value = id;
        if (_maxId < value) {
            _maxId = value;
        }
    }

    public Drawing getNet() {
        return _net;
    }

    private void makeOffset() {
        int offsetX = 0;
        int offsetY = 0;
        FigureEnumeration elements = _net.figures();

        //Find the highest offset
        while (elements.hasMoreElements()) {
            Figure element = elements.nextElement();
            Point pos = element.center();
            if (pos.x < offsetX) {
                offsetX = pos.x - (element.size().width / 2);
            }
            if (pos.y < offsetY) {
                offsetY = pos.y - (element.size().height / 2);
            }
        }

        //move all figures that dont depend on others by the found offset
        if (offsetX < 0 || offsetY < 0) {
            elements = _net.figures();
            offsetX = offsetX * -1;
            offsetY = offsetY * -1;
            while (elements.hasMoreElements()) {
                Figure element = elements.nextElement();
                if (element instanceof ChildFigure childFigure) {
                    if (childFigure.parent() == null) {
                        element.moveBy(offsetX, offsetY);
                    }
                } else {
                    element.moveBy(offsetX, offsetY);
                }
            }
        }
    }

    private void addAnnotations(ObjectParser parser) {
        while (parser.hasMoreAnnotations()) {
            TextFigure annotation = parser.nextAnnotation();
            if (annotation != null) {
                _maxId++;
                annotation.setID(_maxId);
                _net.add(annotation);
            }
        }
    }

    private void work(Iterator<ObjectParser> parsers) {
        while (parsers.hasNext()) {
            ObjectParser objectParser = parsers.next();
            if (!objectParser.hasNumericId()) {
                _maxId++;
                objectParser.setMappedId(_maxId);
                _mapping.put(objectParser.getPNMLId(), _maxId);
            }
            _net.add(objectParser.getFigure());
            addAnnotations(objectParser);
        }
    }

    @Override
    public void doParse() {
        _net = new CPNDrawing();
        String netType = getElement().getAttribute("type");

        Converter converter = new Converter(netType);
        setNetConverter(converter.getNetConverter());


        //List of found places
        List<ObjectParser> placeParsers = new LinkedList<>();


        //List of found transitions
        List<ObjectParser> transitionParsers = new LinkedList<>();


        // List of found arcs
        List<ObjectParser> arcParsers = new LinkedList<>();


        // List of found virtual places
        List<ObjectParser> virPlaceParsers = new LinkedList<>();


        // List of found virtual transitions
        List<ObjectParser> virTransitionParsers = new LinkedList<>();

        //Iterator to walk through the places, transitions and arcs
        Iterator<ObjectParser> parsers;

        // The following works regardless of whether the elements are enclosed
        // in page tags or not.
        NodeList places = getElement().getElementsByTagName("place");
        NodeList transitions = getElement().getElementsByTagName("transition");
        NodeList arcs = getElement().getElementsByTagName("arc");
        NodeList toolspecifics = getElement().getElementsByTagName("toolspecific");

        // parse places and transitions
        for (int pos = 0; pos < places.getLength(); pos++) {
            Element place = (Element) places.item(pos);
            PlaceParser placeParser = new PlaceParser(place, getNetConverter());
            placeParsers.add(placeParser);
            placeParser.parse();
            if (placeParser.hasNumericId()) {
                trySetMaxId(placeParser.getMappedId());
            }
        }
        for (int pos = 0; pos < transitions.getLength(); pos++) {
            Element transition = (Element) transitions.item(pos);
            TransitionParser transitionParser = new TransitionParser(transition, getNetConverter());
            transitionParsers.add(transitionParser);
            transitionParser.parse();
            if (transitionParser.hasNumericId()) {
                trySetMaxId(transitionParser.getMappedId());
            }
        }
        for (int pos = 0; pos < arcs.getLength(); pos++) {
            Element arc = (Element) arcs.item(pos);
            ArcParser arcParser = new ArcParser(arc, _mapping, _net, getNetConverter());
            arcParsers.add(arcParser);
            arcParser.parse();
            if (arcParser.hasNumericId()) {
                trySetMaxId(arcParser.getMappedId());
            }
        }

        for (int pos = 0; pos < toolspecifics.getLength(); pos++) {
            Element toolspecific = (Element) toolspecifics.item(pos);
            if (toolspecific.getAttribute("tool").equals("renew")) {
                NodeList virPlaces = toolspecific.getElementsByTagName("VirtualPlace");
                NodeList virTransitions = toolspecific.getElementsByTagName("VirtualTransition");
                if (virPlaces.getLength() > 0) {
                    Element virPlace = (Element) virPlaces.item(0);
                    VirtualPlaceParser virPlaceParser =
                        new VirtualPlaceParser(virPlace, _net, getNetConverter());
                    virPlaceParsers.add(virPlaceParser);
                    virPlaceParser.parse();
                    if (virPlaceParser.hasNumericId()) {
                        trySetMaxId(virPlaceParser.getMappedId());
                    }
                }
                if (virTransitions.getLength() > 0) {
                    Element virTransition = (Element) virTransitions.item(0);
                    VirtualTransitionParser virTransitionParser =
                        new VirtualTransitionParser(virTransition, _net, getNetConverter());
                    virTransitionParsers.add(virTransitionParser);
                    virTransitionParser.parse();
                    if (virTransitionParser.hasNumericId()) {
                        trySetMaxId(virTransitionParser.getMappedId());
                    }
                }

                // Look for toolspecific declarations
                NodeList decls = toolspecific.getElementsByTagName("declaration");
                if (decls.getLength() > 0) {
                    // Only take the first one
                    Element declaration = (Element) decls.item(0);
                    AnnotationParser declParser =
                        new AnnotationParser(declaration, getNetConverter());
                    declParser.parse();
                    TextFigure fig = declParser.getFigure();
                    if (fig != null) {
                        _net.add(fig);
                    }
                }
            }
        }

        NodeList children = getElement().getChildNodes();
        for (int pos = 0; pos < children.getLength(); pos++) {
            Node child = children.item(pos);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) child;
                if (getNetConverter().isAnnotation(element)) {
                    AnnotationParser annotationParser =
                        new AnnotationParser((Element) child, getNetConverter());
                    annotationParser.parse();
                    FigureWithID figure = annotationParser.getFigure();
                    _maxId++;
                    if (figure != null) {
                        figure.setID(_maxId);
                        // hide Annotation (Name)
                        if (figure instanceof CPNTextFigure anno) {
                            if (anno.getType() == CPNTextFigure.NAME) {
                                _net.setName(anno.getText());
                                continue;
                            }
                        }
                        // might be a declaration node
                        _net.add(figure);
                    }
                }
            }
        }

        //Give any place, transition and arc a new int id and add them to the drawing    
        parsers = placeParsers.iterator();
        work(parsers);
        parsers = virPlaceParsers.iterator();
        work(parsers);
        parsers = transitionParsers.iterator();
        work(parsers);
        parsers = virTransitionParsers.iterator();
        work(parsers);
        parsers = arcParsers.iterator();
        work(parsers);

        //Move the figures so all of them are visible
        makeOffset();
    }
}