package de.tud.kom.p2psim.impl.topology.movement.modularosm.movementIconVisualisation;

import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModelViz;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView;
import de.tudarmstadt.maki.simonstrator.api.*;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Visualises the different nodes as icons instead of these lame dots.
 * The icons are configured entirely in the config files. To use this visualisation
 * the first thing to do is specify this {@link ModularMovementIconVis} as visualisation for
 * your modular movement model. To add icons to the visualisation the class {@link IconMapping} is used within the tags.
 * You have two tags available for different kind of icons:<BR><BR>
 * <p>
 * 1. Class icons: These icons are used dependent on the class of the components the host includes. You can
 * add a class icon by using the &lt;ClassIcon> tag you config and setting the fully qualified classname
 * of you components as object. If a host has multiple components with different specified icons, the first one will be used.<BR><BR>
 * <p>
 * 2. State icons: These icons are used dependent on the state of a host and take precedence over the class icons. You
 * can add them via the &lt;StateIcon> tag. The object should be a String representing the state. When
 * using these types of icons, you need to set a {@link StateSelector} for this class in the config.<BR><BR>
 * <p>
 * For example usage, see the movement_social.xml config for the resourceAllocation overlay.
 *
 * @author Clemens Krug
 */
public class ModularMovementIconVis extends ModularMovementModelViz implements EventHandler
{
    private HashMap<Class, String> pathMap;
    private HashMap<Class, Image> classIconMap;

    private HashMap<String, String> stateMap;
    private HashMap<String, Image> stateIconMap;

    private StateSelector stateSelector;
    private boolean init = false;

    public ModularMovementIconVis()
    {
        init();
        pathMap = new HashMap<>();
        classIconMap = new HashMap<>();

        stateMap = new HashMap<>();
        stateIconMap = new HashMap<>();


        // scheduling initalization!
        de.tudarmstadt.maki.simonstrator.api.Event.scheduleImmediately(this, null, 0);
    }

    private void initialize()
    {
        assert !init : "ModularMovementIconVis: Was already initialized!";

        VisualizationTopologyView.VisualizationInjector.getTopologyView().setShowNodes(false);

        for (Map.Entry<Class, String> e : pathMap.entrySet())
        {
            try
            {
                Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
                classIconMap.put(e.getKey(), icon);
            } catch (IOException ioe)
            {
                Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Could not load icon from path %s", e.getValue());
            }
        }

        for (Map.Entry<String, String> e : stateMap.entrySet())
        {
            assert stateSelector != null : "There must be a state selector specified when using state icons!";

            try
            {
                Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
                stateIconMap.put(e.getKey(), icon);
            } catch (IOException ioe)
            {
                Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Could not load icon from path %s", e.getValue());
            }
        }

        init = true;
    }

    @Override
    public void paint(Graphics g)
    {
        super.paintSuper(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        if (showAttractionPoints)
        {
            super.drawAttractionPoints(g2);
        }

        if (showNodePositions)
        {
            for (SimLocationActuator comp : movementModel
                    .getAllLocationActuators())
            {
                drawNodePosition(g2, comp);
            }
        }
    }


    @Override
    protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp)
    {
        boolean iconNotFound = true;
        Host host = comp.getHost();
        Point2D pt = comp.getRealPosition().asPoint();
        pt = new Point2D.Double(VisualizationTopologyView.VisualizationInjector.scaleValue(pt.getX()), VisualizationTopologyView.VisualizationInjector.scaleValue(pt.getY()));
        g2.setColor(Color.GRAY);

//            Check if the component has a specified state, as this would have precedence over the classIcon.
        if (stateSelector != null)
        {
            String state = stateSelector.getState(comp);
            if (stateIconMap.containsKey(state))
            {
                Image icon = stateIconMap.get(state);
                int width = icon.getWidth(null);
                int height = icon.getHeight(null);
                g2.drawImage(icon, (int) pt.getX() - width / 2, (int) pt.getY() - height / 2, null);
                return;
            }
        }

        // If not, check if there's a class icon available
        for (Map.Entry<Class, Image> e : classIconMap.entrySet())
        {
            try
            {
                host.getComponent(e.getKey());
                Image icon = e.getValue();
                int width = icon.getWidth(null);
                int height = icon.getHeight(null);
                g2.drawImage(icon, (int) pt.getX() - width / 2, (int) pt.getY() - height / 2, null);
                iconNotFound = false;
                break;
            } catch (ComponentNotAvailableException nae)
            {
                iconNotFound = true;
            }
        }

        // If not, use standard visualisation
        if (iconNotFound) super.drawNodePosition(g2, comp);
    }

    @Override
    public void eventOccurred(Object content, int type)
    {
        if (type == 0)
        {
            initialize();
        }
    }

    public void setClassIcon(IconMapping classIcon)
    {
        try
        {
            pathMap.put(Class.forName(classIcon.getObject()), classIcon.getIconpath());
        } catch (ClassNotFoundException e)
        {
            Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Class %s not found!", classIcon.getObject());
        }
    }

    public void setStateIcon(IconMapping stateIcon)
    {
        stateMap.put(stateIcon.getObject(), stateIcon.getIconpath());
    }

    public void setStateSelector(StateSelector selector)
    {
        this.stateSelector = selector;
    }
}
