/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
*/
package jpa.tools.swing;

import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.SingularAttribute;
import javax.swing.JPanel;


/**
 * Graphical View of a JPA 2.0 Metamodel.
 * The view is isomorphic to the {@link Metamodel meta-model} defined by JPA 2.0 specification.
 * Hence the view is organized in terms of corresponding views of {@link EntityTypeView entity}
 * and their {@link AttributeView attributes}.
 * <br>
 * This view also draws linkage with A*-algorithm between the derived primary key attributes that
 * reference other entity types.
 *
 * @author Pinaki Poddar
 *
 */
public class MetamodelView extends JPanel implements Maze {
    private static final long serialVersionUID = 1L;
    private static final int GRID = 8;
    int hgap = 40;
    int vgap = 40;

    /**
     * Creates a panel where each {@link EntityTypeView} is placed in a FlowLayout.
     * Only the entities of the model are ordered based on their primary key
     * dependencies, if any.
     */
    @SuppressWarnings("unchecked")
    public MetamodelView(Metamodel model) {
        super(true);
        FlowLayout layout = (FlowLayout)getLayout();
        layout.setHgap(80);
        layout.setVgap(80);
//        getInsets(new Insets(100,100,100,100));
        Set<EntityType<?>> types = model.getEntities();
        List<EntityType<?>> sorted = new ArrayList<>(types);
        Collections.sort(sorted, new MetamodelHelper.EntityComparator());
        for (EntityType type : sorted) {
            EntityTypeView view = new EntityTypeView(type);
            add(view);
        }
    }

    EntityTypeView<?> findViewByType(EntityType<?> type) {
        if (type == null)
            return null;
        for (Component c : getComponents()) {
            if (c instanceof EntityTypeView) {
                EntityTypeView<?> view = (EntityTypeView<?>)c;
                if (view.getEntityType().equals(type)) {
                    return view;
                }
            }
        }
        return null;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        for (Component c : getComponents()) {
            if (c instanceof EntityTypeView == false) {
                continue;
            }
            EntityTypeView<?> view = (EntityTypeView<?>)c;
            for (SingularAttribute<?,?> id : MetamodelHelper.getIdAttributes(view.getEntityType())) {
                EntityTypeView<?> target = findViewByType(MetamodelHelper.getParentType(id));
                if (target == null)
                    continue;

                PathFinder runner = new PathFinder(this);
                Point start = getConnectLocation(view, id);
                Point finish = target.getLocation();
                List<Point> path = runner.findPath(start.x/GRID, start.y/GRID,
                        finish.x/GRID, finish.y/GRID);
                if (path.isEmpty())
                    continue;
                Point p1 = path.get(0);
                Point p2 = null;
                for (int i = 1; i < path.size(); i++) {
                    p2 = path.get(i);
                    g.drawLine(p1.x*GRID, p1.y*GRID, p2.x*GRID, p2.y*GRID);
                    p1 = p2;
                }
                g.setColor(Color.BLACK);
                int r = 4;
                g.fillOval(p1.x*GRID -r, p1.y*GRID - r, 2*r, 2*r);

            }
        }
    }

    /**
     * Gets the position of the attribute in the entity view relative to this panel.
     */
    Point getConnectLocation(EntityTypeView<?> a, Attribute<?,?> attr) {
        Point p1 = a.getLocation();
        Point p2 = a.getPosition(attr);
        return new Point(p1.x + p2.x, p1.y + p2.y);
    }

    // contract for the maze
    @Override
    public boolean isReachable(int x, int y) {
        for (Component view : getComponents()) {
            Rectangle r = view.getBounds();
            int px = x*GRID;
            int py = y*GRID;
            if (r.contains(px, py))
                return false;
        }
        return true;
    }
}
