package core;

import application.fxobjects.cell.Cell;
import application.fxobjects.cell.Edge;
import application.fxobjects.cell.layout.GraphLayout;
import application.fxobjects.cell.tree.LeafCell;
import application.fxobjects.cell.graph.RectangleCell;
import application.fxobjects.cell.graph.TriangleCell;

import application.fxobjects.cell.tree.MiddleCell;
import core.graph.cell.CellType;
import core.graph.cell.EdgeType;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;
import net.sourceforge.olduvai.treejuxtaposer.drawer.Tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Model containing all nodes and edges.
 */
public class Model {

    private Cell graphParent;

    private List<Cell> allCells;
    private List<Cell> addedCells;

    private List<Edge> allEdges;
    private List<Edge> addedEdges;

    private Map<Integer, Cell> cellMap; // <id,cell>

    private List<HashMap<Integer, Node>> levelMaps;

    private Tree tree;

    private Rectangle2D screenSize;
    private int maxWidth;
    private int maxHeight;

    /**
     * Class constructor.
     */
    public Model() {
        graphParent = new RectangleCell(1, "");

        this.maxWidth = 0;
        this.screenSize = Screen.getPrimary().getVisualBounds();
        this.maxHeight = (int) screenSize.getHeight();

        // clear model, create lists
        clear();
    }

    /**
     * Remove all cells and edges.
     */
    public void clear() {
        allCells = new ArrayList<>();
        addedCells = new ArrayList<>();

        allEdges = new ArrayList<>();
        addedEdges = new ArrayList<>();

        cellMap = new HashMap<>();

        levelMaps = new ArrayList<>();

        tree = new Tree();
    }

    /**
     * Remove all added cells and edges.
     */
    public void clearAddedLists() {
        addedCells.clear();
        addedEdges.clear();
    }

    /**
     * Get the phylogenetic tree.
     *
     * @return The phylogenetic tree.
     */
    public Tree getTree() {
        return tree;
    }

    /**
     * Set the phylogenetic tree.
     *
     * @param tree The phylogenetic tree.
     */
    public void setTree(Tree tree) {
        this.tree = tree;
    }

    /**
     * Get a list of added cells.
     *
     * @return A list of added cells.
     */
    public List<Cell> getAddedCells() {
        return addedCells;
    }

    /**
     * Get a list of all cells.
     *
     * @return A list of all cells.
     */
    public List<Cell> getAllCells() {
        return allCells;
    }

    /**
     * Get a list of added edges.
     *
     * @return A list of all added edges.
     */
    public List<Edge> getAddedEdges() {
        return addedEdges;
    }

    /**
     * Get a list of all edges.
     *
     * @return A list of all edges.
     */
    public List<Edge> getAllEdges() {
        return allEdges;
    }

    /**
     * Method to add a Cell (Node).
     *
     * @param id   the id, which represents the sequence.
     * @param text The text of a cell.
     * @param type The type of cell.
     * @return True for testing purposes.
     */
    public Boolean addCell(int id, String text, CellType type) {
        switch (type) {
            case RECTANGLE:
                RectangleCell rectangleCell = new RectangleCell(id, text);
                addCell(rectangleCell);
                break;
            case TRIANGLE:
                TriangleCell circleCell = new TriangleCell(id, text);
                addCell(circleCell);
                break;
            case TREELEAF:
                LeafCell leafCell = new LeafCell(id, text);
                addCell(leafCell);
                break;
            case TREEMIDDLE:
                MiddleCell middleCell = new MiddleCell(id);
                addCell(middleCell);
                break;
            default:
                throw new UnsupportedOperationException("Unsupported type: " + type);
        }

        return false;
    }

    /**
     * Method to add a Cell (Node).
     *
     * @param cell The cell (Node) to add.
     * @return True for testing purposes.
     */
    public Boolean addCell(Cell cell) {
        if (!cellMap.containsKey(cell.getCellId())) {
            addedCells.add(cell);

            cellMap.put(cell.getCellId(), cell);
        }
        return true;
    }

    /**
     * Return a list of level maps.
     *
     * @return A list of level maps.
     */
    public List<HashMap<Integer, Node>> getLevelMaps() {
        return this.levelMaps;
    }

    /**
     * Set a list of level maps.
     *
     * @param levelMaps A list of level maps.
     */
    public void setLevelMaps(List<HashMap<Integer, Node>> levelMaps) {
        this.levelMaps = levelMaps;
    }

    /**
     * Method to add an Edge to the model.
     *
     * @param sourceId From.
     * @param targetId To.
     * @param width    The width of the edge.
     * @param type     The type of edge.
     * @return True for testing purposes.
     */
    public Boolean addEdge(int sourceId, int targetId, int width, EdgeType type) {
        Cell sourceCell = cellMap.get(sourceId);
        Cell targetCell = cellMap.get(targetId);

        if (sourceCell != null && targetCell != null) {
            Edge edge = new Edge(sourceCell, targetCell, width, type);
            addedEdges.add(edge);
        }

        return true;
    }

    /**
     * Attach all cells which don't have a parent to graphParent.
     *
     * @param cellList List of cells without a parent.
     */
    public void attachOrphansToGraphParent(List<Cell> cellList) {

        for (Cell cell : cellList) {
            if (cell.getCellParents().size() == 0) {
                graphParent.addCellChild(cell);
            }
        }

    }

    /**
     * Remove the graphParent reference if it is set
     *
     * @param cellList List of cells to be removed from the graph
     */
    public void disconnectFromGraphParent(List<Cell> cellList) {

        for (Cell cell : cellList) {
            graphParent.removeCellChild(cell);
        }
    }

    /**
     * Add and remove cells from the allCells list.
     */
    public void merge() {
        // cells
        allCells.addAll(addedCells);
        addedCells.clear();

        // edges
        allEdges.addAll(addedEdges);
        addedEdges.clear();
    }

    public void setLayout() {
        this.screenSize = Screen.getPrimary().getVisualBounds();
        GraphLayout layout = new GraphLayout(this, 20,
                (int) (screenSize.getHeight() - 25) / 2);

        //System.out.println(("KUTKACHEL" + addedCells.size()));
        layout.execute();
        maxWidth = (int) layout.getMaxWidth();
    }

    public int getWidth() {
        return maxWidth;
    }

    public int getHeight() {
        return maxHeight;
    }
}
@@ -1245,128 +1245,8 @@
 = 0;
-%0A        this.screenSize = Screen.getPrimary().getVisualBounds();%0A        this.maxHeight = (int) screenSize.getHeight();
 %0A%0A  
@@ -6511,34 +6511,8 @@
 ));%0A
-        layout.execute();%0A
     
@@ -6550,24 +6550,51 @@
 MaxWidth();%0A
+        layout.execute();%0A%0A
     %7D%0A%0A    p

Test fix.