package de.tud.kom.p2psim.impl.topology.views.visualization.ui;

import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Vector;

/**
 * The actual frame/view displaying the node data. Simply a frame that holds a table
 * displaying the info.
 */
public class DebugNodeView extends JFrame
{
    private JPanel panel;
    private JTable table;
    private DefaultTableModel model;
    private long hostId;

    public DebugNodeView(long hostId)
    {
        this.hostId = hostId;
        setTitle("Host: " + hostId);

        SwingUtilities.invokeLater(this::createGUI);
    }

    /**
     * Initialise the UI
     */
    private void createGUI()
    {
        panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.setPreferredSize(new Dimension(300,300));

        Map<Class, HashMap<String, Object>> nodeData = NodeDebugMonitor.getNodeData(INodeID.get(hostId));
        Vector tableData = convertToTableVector(nodeData);

        Vector<String> columns = new Vector<>();
        columns.add("Klasse");
        columns.add("Beschreibung");
        columns.add("Wert");

        model = new DefaultTableModel(tableData, columns);
        table = new JTable(model);
        JScrollPane scrollPane = new JScrollPane(table);
        table.setFillsViewportHeight(true);
        panel.add(scrollPane, BorderLayout.NORTH);

        getContentPane().add(panel);

        pack();
        setVisible(true);

    }

    /**
     * Updates an entry for a node
     * @param subject The class which provided the info
     * @param entry the entry/key
     * @param value the value
     */
    public void update(Class subject, String entry, String value)
    {
        int rowIndex = findEntry(subject.getSimpleName(), entry);
        //If the entry already existed it needs to be updates.
        if(rowIndex >= 0)
        {
            //2 is the position of the value in the vector.
            ((Vector) model.getDataVector().get(rowIndex)).set(2, value);
            model.fireTableDataChanged();
        }
        //If the entry didn't exist, a new row is inserted.
        else
        {
            String[] data = {subject.getSimpleName(), entry, value};
            model.addRow(data);
        }
    }

    /**
     * Finds the row in the data vector which holds the specified entry
     * @param subject the providing class
     * @param entry the entry
     * @return the index of the row holding the entry
     */
    private int findEntry(String subject, String entry)
    {
        Vector data = model.getDataVector();

        for(int i = 0; i < data.size(); i++)
        {
            Vector v = (Vector) data.get(i);
            if(v.get(0).equals(subject) && v.get(1).equals(entry))
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * Converts a hashmap with the per node information as provided by the {@link NodeDebugMonitor}
     * to a data vector which can be used in a table model.
     * @param nodeData the data which should be converted.
     * @return a vector holding all the data which can be used for table models.
     */
    private Vector<Vector<String>> convertToTableVector(Map<Class, HashMap<String, Object>> nodeData)
    {
        Vector<Vector<String>> tableData = new Vector<>();

        for (Map.Entry<Class, HashMap<String, Object>> classData : nodeData.entrySet())
        {
            String classname = classData.getKey().getSimpleName();
            for(Map.Entry<String, Object> entry : classData.getValue().entrySet())
            {
                String value;
                if (entry.getValue() == null) value = "NULL";
                else value = entry.getValue().toString();

                Vector<String> entryVector = new Vector<>();
                entryVector.add(classname);
                entryVector.add(entry.getKey());
                entryVector.add(value);

                tableData.add(entryVector);
            }
        }
        return tableData;
    }

}
