/************************************************************************************
MRailSim - a model railway simulation program - http://mrailsim.sourceforge.net/
Copyright (C) 2004,2007 Bernd Arnold
This program 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.
This program 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 net.sf.mrailsim.signals;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import net.sf.mrailsim.rails.Node;
import net.sf.mrailsim.rails.Track;
import net.sf.mrailsim.rails.TrackChangeListener;
public class MainSignal extends Signal {
private ArrayList<PreSignal> preSignals;
private boolean manualStop = false;
private boolean automaticStop = false;
private AutomaticModule autoModule = null;
abstract class AutomaticModule implements TrackChangeListener {
}
class LookaheadModule extends AutomaticModule {
ArrayList<Track> observedTracks;
int depth;
LookaheadModule( int depth ) {
observedTracks = new ArrayList<Track>();
this.depth = depth;
try {
addLookaheads( depth, getBoundTrack(), getBoundNode() );
} catch (Exception e) {
// TODO Handle right
e.printStackTrace();
}
for ( Track t : observedTracks ) {
t.addChangeListener( this );
}
}
private void addLookaheads( int amount, Track trackStart, Node nodeStart ) throws Exception {
// Don't go any further if this track was already assigned
if ( observedTracks.indexOf( trackStart ) >= 0 ) {
return;
}
observedTracks.add( trackStart );
amount--;
if ( amount == 0 ) {
return;
}
HashMap<Node, Track> list = null;
list = trackStart.getPossibleNextTracksWithNodes( nodeStart );
Set<Node> nodes = list.keySet();
Iterator<Node> iterator = nodes.iterator();
while ( iterator.hasNext() ) {
Node node = iterator.next();
Track track = list.get( node );
addLookaheads( amount, track, node );
}
}
public void updateFromTrack( Track t ) {
boolean freeRide = true;
ArrayList<Track> list;
try {
list = getBoundTrack().getNextTracks( getBoundNode(), depth - 1, true );
for ( Track item : list ) {
if ( item.isOccupied() ) {
freeRide = false;
break;
}
}
} catch (Exception e) {
// May also happen when the way is not completely switched
// For example, when the signal looks ten tracks ahead and there
// are switches included, so changing the position of the first switch
// may end in a dead switch until the dead switch is changed too
freeRide = false;
}
setAutomaticStop( ! freeRide );
}
}
public MainSignal( long id ) {
super( id );
preSignals = new ArrayList<PreSignal>();
}
public void update() {
if ( autoModule != null ) {
autoModule.updateFromTrack( null );
}
}
public void setBound( Track boundTrack, Node boundNode ) {
super.setBound( boundTrack, boundNode );
boundTrack.addMainSignal( this );
}
public void makeLookaheadSignal( int depth ) {
autoModule = new LookaheadModule( depth );
}
public void registerPreSignal( PreSignal preSignal ) {
// Only add pre signal if not in list
if ( ! preSignals.contains( preSignal ) ) {
preSignals.add( preSignal );
}
preSignal.updateFromMain( display );
}
private void hasChanged() {
for ( PreSignal item : preSignals ) {
item.updateFromMain( display );
}
}
public void draw( Graphics g ) {
if ( coordinates != null ) {
g.setColor( Color.BLACK );
g.fillRect( coordinates.getX(), coordinates.getY(), 12, 12 );
if ( display == SignalDisplay.GREEN ) {
g.setColor( Color.GREEN );
} else if ( display == SignalDisplay.RED ) {
g.setColor( Color.RED );
} else {
g.setColor( Color.BLACK );
}
g.fillOval( coordinates.getX() + 1, coordinates.getY() + 1, 10, 10 );
}
}
public void toggleManualStop() {
setManualStop( ! manualStop );
}
public SignalDisplay getDisplay() {
if ( manualStop) {
return SignalDisplay.RED;
}
else return super.getDisplay();
}
public String toString() {
String out = String.format( "Signal: %d\n",
id );
out += String.format( "Current display: %s\n" ,
display );
out += "Signal type: main signal\n";
out += String.format( "Automatic (track occupied) stop: %s\n",
automaticStop );
out += String.format( "Manual stop: %s\n",
manualStop );
if ( getBoundTrack() != null && getBoundNode() != null ) {
out += String.format( "Bound to: track %d, node %d\n",
getBoundTrack().getId(), getBoundNode().getId() );
} else {
out += "Bound to: -\n";
}
return out;
}
void setAutomaticStop( boolean newStop ) {
SignalDisplay oldDisplay = display;
automaticStop = newStop;
display = SignalDisplay.GREEN;
if ( automaticStop || manualStop ) {
display = SignalDisplay.RED;
}
if ( oldDisplay != display ) {
hasChanged();
}
}
void setManualStop( boolean newStop ) {
SignalDisplay oldDisplay = display;
manualStop = newStop;
display = SignalDisplay.GREEN;
if ( automaticStop || manualStop ) {
display = SignalDisplay.RED;
}
if ( oldDisplay != display ) {
hasChanged();
}
}
public void handleMouseClick() {
toggleManualStop();
}
}