/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: FindVirtualEnd_Simple.java
 * Written by: Andreas Uebelhoer, Alexander Bieles, Emre Selegin (Team 6)
 *
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.routing.experimentalLeeMoore1;

import com.sun.electric.tool.routing.experimentalLeeMoore1.LeeMoore.Tupel;

/**
 *
 * @author Sonny
 */
public class FindVirtualEnd_Simple implements FindVirtualEndInterface {

    private static boolean X_DIRECTION; //const
    private static boolean Y_DIRECTION; //const
    private static int MAXLAYER;

    public FindVirtualEnd_Simple(boolean xdir, boolean ydir, int l) {
        X_DIRECTION = xdir;
        Y_DIRECTION = ydir;
        MAXLAYER = l;
    }

    public synchronized ConnectionPoints findVirtualEnd(RoutingPart rp, int id) {
        Tupel start = rp.start;
        Tupel ende = rp.end;

        /*current algo:
         * go to the boundary in this direction, which is shorter to the end_point
         * at the boundary try to find a free point to use
         * try this for all layers
         */
        boolean routingDirection;
        if (Math.abs(getMiddlePoint(start).getX_InsideRoutingArray() - getMiddlePoint(ende).getX_InsideRoutingArray())
                >= Math.abs(getMiddlePoint(start).getY_InsideRoutingArray() - getMiddlePoint(ende).getY_InsideRoutingArray())) {
            routingDirection = X_DIRECTION;
            //route in Xdirection because its longer (on the x sided boundary)
            if ((start.getX_InsideRoutingArray() <= ende.getX_InsideRoutingArray())) {
                //we have to go to the right, because the start is on the left side of the end
                for (int l = 0; l < MAXLAYER; l++) {
                    if (!isCorrectOddEvenLayerForRoutingDirection(l, routingDirection)) {
                        continue;
                    }
                    //Tupel vEnd = new Tupel(getHighIndexIn_X(this.id), 0, l, false);
                    for (int y = getLowIndexIn_Y(id) + 1; y < getHighIndexIn_Y(id) - 1; y += 10) {
                        //try to find a free position on the right boundary
                        Tupel vEnd = new Tupel(getHighIndexIn_X(id), y, l, false);
                        if (isFree(vEnd)) {
                            return makeReturnArg(vEnd, routingDirection, 1);
                        }
                    }
                }
            } else { //((start.getX_InsideRoutingArray() > ende.getX_InsideRoutingArray()) ) {
                //we have to go to the left, because ... s.o.
                for (int l = 0; l < MAXLAYER; l++) {
                    if (!isCorrectOddEvenLayerForRoutingDirection(l, routingDirection)) {
                        continue;
                    }
                    //Tupel vEnd = new Tupel(getLowIndexIn_X(this.id), 0, l, false);
                    for (int y = getLowIndexIn_Y(id) + 1; y < getHighIndexIn_Y(id) - 1; y += 10) {
                        //try to find a free position on the left boundary
                        Tupel vEnd = new Tupel(getLowIndexIn_X(id), y, l, false);
                        if (isFree(vEnd)) {
                            return makeReturnArg(vEnd, routingDirection, -1);
                        }
                    }
                }
            }
        } else {
            routingDirection = Y_DIRECTION;
            //route in Ydirection, because its longer
            if ((start.getY_InsideRoutingArray() <= ende.getY_InsideRoutingArray())) {
                //we have to go to the top, because ... s.o.
                for (int l = 0; l < MAXLAYER; l++) {
                    if (!isCorrectOddEvenLayerForRoutingDirection(l, routingDirection)) {
                        continue;
                    }
                    //Tupel vEnd = new Tupel(0, getHighIndexIn_Y(this.id), l, false);
                    for (int x = getLowIndexIn_X(id) + 1; x < getHighIndexIn_X(id) - 1; x += 10) {
                        //try to find a free position on the top boundary
                        Tupel vEnd = new Tupel(x, getHighIndexIn_Y(id), l, false);
                        if (isFree(vEnd)) {
                            return makeReturnArg(vEnd, routingDirection, 1);
                        }
                    }
                }
            } else { //((start.getY_InsideRoutingArray() > ende.getY_InsideRoutingArray()) ) {
                //we have to go down, because ... s.o.
                for (int l = 0; l < MAXLAYER; l++) {
                    if (!isCorrectOddEvenLayerForRoutingDirection(l, routingDirection)) {
                        continue;
                    }
                    //Tupel vEnd = new Tupel(0, getLowIndexIn_Y(this.id), l, false);
                    for (int x = getLowIndexIn_X(id) + 1; x < getHighIndexIn_X(id) - 1; x += 10) {
                        //try to find a free position on the bottom boundary
                        Tupel vEnd = new Tupel(x, getLowIndexIn_Y(id), l, false);
                        if (isFree(vEnd)) {
                            return makeReturnArg(vEnd, routingDirection, -1);
                        }
                    }
                }
            }
        }
        //TODO: no virtual connecting point was found in the desired routing direction, so try to go another to another side of your region
        return new ConnectionPoints();
    }

    private Tupel getMiddlePoint(Tupel end) {
        return WorkerThread.getMiddlePoint(end);
    }

    private boolean isCorrectOddEvenLayerForRoutingDirection(int layer, boolean direction) {
        //direction==true => X-Dir
        //layers are counted from 0, so we have add 1 to get the first layer index 1
        if (direction == X_DIRECTION && (layer + 1) % 2 == 1) {
            return true; //odd layers route in x-direction
        }
        if (direction == Y_DIRECTION && (layer + 1) % 2 == 0) {
            return true; //even layer shall route in y-direction
        }
        return false;
    }

    private ConnectionPoints makeReturnArg(Tupel pkt, boolean direction, int range) {
        if (direction == X_DIRECTION) {
            //X-dir
            return new ConnectionPoints(new Tupel(pkt.getX_InsideRoutingArray(), pkt.getY_InsideRoutingArray(), pkt.getLayer(), false), new Tupel(pkt.getX_InsideRoutingArray() + range, pkt.getY_InsideRoutingArray(), pkt.getLayer(), false));
        } else {
            //Y-direction
            return new ConnectionPoints(new Tupel(pkt.getX_InsideRoutingArray(), pkt.getY_InsideRoutingArray(), pkt.getLayer(), false), new Tupel(pkt.getX_InsideRoutingArray(), pkt.getY_InsideRoutingArray() + range, pkt.getLayer(), false));
        }
    }

    private boolean isFree(Tupel t) {
        if (t.getX_InsideRoutingArray() >= 0 && t.getX_InsideRoutingArray() < WorkerThread.size_x
                && t.getY_InsideRoutingArray() >= 0 && t.getY_InsideRoutingArray() < WorkerThread.size_y
                && t.getLayer() >= 0 && t.getLayer() <= MAXLAYER) {
            return WorkerThread.regionBoundaries[t.getX_InsideRoutingArray()][t.getY_InsideRoutingArray()][t.getLayer()];
        } else {
            //point not inside the grid
            return false;
        }
    }

    private static int getHighIndexIn_X(int rank) {
        return WorkPool.getHighIndexIn_X(rank);
    }

    private static int getHighIndexIn_Y(int rank) {
        return WorkPool.getHighIndexIn_Y(rank);
    }

    private static int getLowIndexIn_X(int rank) {
        return WorkPool.getLowIndexIn_X(rank);
    }

    private static int getLowIndexIn_Y(int rank) {
        return WorkPool.getLowIndexIn_Y(rank);
    }
}
