/**
 * JBoss, Home of Professional Open Source
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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 org.jboss.webbeans.environment.se.example.roadmapmodel;

import org.jboss.webbeans.log.Log;
import org.jboss.webbeans.log.Logging;

import java.util.HashSet;
import java.util.Set;

import javax.context.ApplicationScoped;
import javax.event.Observes;

/**
 * Collects all roads created.
 * @author Peter Royle
 */

// TODO: make a more appropriate scope, such that there can be several 
// simulations running at once, all in different scopes.
@ApplicationScoped
public class RoadMap
{
    final Log log = Logging.getLog( RoadMap.class );
    final Set<Road> roads = new HashSet<Road>(  );
    final Set<Intersection> intersections = new HashSet<Intersection>(  );
    private int width = 0;
    private int height = 0;

    public Set<Road> getRoads(  )
    {
        return roads;
    }

    public Set<Intersection> getIntersections(  )
    {
        return intersections;
    }

    public void calcIntersections( @Observes
    RoadEvent newRoadEvent )
    {
        Road newRoad = newRoadEvent.getRoad(  );

        if ( newRoad.getOrientation(  ).equals( Orientation.HORIZONTAL ) )
        {
            if ( newRoad.getEnd(  ) > this.width )
            {
                this.width = newRoad.getEnd(  );
            }
        } else
        {
            if ( newRoad.getEnd(  ) > this.height )
            {
                this.height = newRoad.getEnd(  );
            }
        }

        synchronized ( this.roads )
        {
            log.trace( "Adding new road: #0. Calculating intersections", newRoad );

            for ( Road existingRoad : this.roads )
            {
                if ( ! existingRoad.getOrientation(  ).equals( newRoad.getOrientation(  ) ) )
                {
                    log.trace( "Calculating intersections with #0", existingRoad );

                    Road horz = null;
                    Road vert = null;

                    if ( newRoad.getOrientation(  ).equals( Orientation.HORIZONTAL ) )
                    {
                        horz = newRoad;
                        vert = existingRoad;
                    } else
                    {
                        horz = existingRoad;
                        vert = newRoad;
                    }

                    final boolean horzPassesVert =
                        ( horz.getStart(  ) <= vert.getOffset(  ) ) && ( horz.getEnd(  ) >= vert.getOffset(  ) );
                    final boolean vertPassesHorz =
                        ( vert.getStart(  ) <= horz.getOffset(  ) ) && ( vert.getEnd(  ) >= horz.getOffset(  ) );

                    if ( horzPassesVert && vertPassesHorz )
                    {
                        Intersection intersection = new Intersection( horz, vert );
                        horz.getIntersections(  ).add( intersection );
                        vert.getIntersections(  ).add( intersection );
                        this.intersections.add( intersection );

                        log.trace( "Adding intersection ${0}", intersection );
                    } else
                    {
                        log.trace( "Roads do not intersect." );
                    }
                } else
                {
                    log.trace( "Skipping intersection calculation with #0 - same orientation", existingRoad );
                }
            }

            this.roads.add( newRoad );
        }
    }

    public int getHeight(  )
    {
        return height;
    }

    public int getWidth(  )
    {
        return width;
    }
}
