' -*- mode: text; tab-width: 4; -*-
'
' The contents of this file are subject to the Mozilla Public
' License Version 1.1 (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.mozilla.org/MPL/
' 
' Software distributed under the License is distributed on an "AS
' IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
' implied. See the License for the specific language governing
' rights and limitations under the License.
' 
' The Original Code is State Machine Compiler (SMC).
' 
' The Initial Developer of the Original Code is Charles W. Rapp.
' Portions created by Charles W. Rapp are
' Copyright (C) 2000 - 2003 Charles W. Rapp.
' All Rights Reserved.
' 
' Contributor(s): 
'
' State Machine
'  Stoplight
'
' Description
'  This state machine changes the stop lights.
'
' RCS ID
' $Id$
'
' CHANGE LOG
' $Log$

Public NotInheritable Class Stoplight

    '------------------------------------------------------------
    ' Member data.
    '

    Private _positions As Point()
    Private _size As Dimension
    Private _canvas As TrafficCanvas
    Private _fsm As StoplightContext

    ' Keep track of each light's current color.
    Private _lights As Color()()

    ' Remember when the timer was started and how long the timer
    ' is running.
    Private _timerStart As Timestamp
    Private _timerDuration As Integer
    Private _stoplightTimer As Timer
    Private _nsLightDuration As Integer
    Private _ewLightDuration As Integer
    Private _yellowDuration As Integer

    '------------------------------------------------------------
    ' Shared data.
    '

    Public Shared NORTH As Integer = 0
    Public Shared EAST As Integer = 1
    Public Shared SOUTH As Integer = 2
    Public Shared WEST As Integer = 3

    Private Shared LAMP_DIAMETER = 6
    Private Shared LAMP_SPACE = 3
    Private Shared GREEN = 0
    Private Shared YELLOW = 1
    Private Shared RED = 2

    '------------------------------------------------------------
    ' Member methods.
    '

    Public Sub New(ByVal canvasWidth As Integer, _
                   ByVal canvasHeight As Integer, _
                   ByRef canvas As TrafficCanvas)

       ' Remember on which canvas this stop light is drawn.
       _canvas = canvas

       ' Calculate the stop light's width and  height. The width
       ' is equal to the individual lamp's diameter plus space on
       ' either side.
       _size = _
           New Dimension( _
               (LAMP_DIAMETER + (LAMP_SPACE * 2)), _
               ((LAMP_DIAMETER * 3) + (LAMP_SPACE * 4)))

        ' Now calculate the stop light's coordinates based on
        ' the following diagram:
        '
        ' y[0]         +---+
        '              | o | g
        '              | o | y
        '              | o | r y g
        ' y[1] +-------+---+-------+
        '      | o o o | c | o o o |
        ' y[2] +-------+---+-------+
        '        g y r | o |
        '            y | o |
        '            g | o |
        ' y[3]         +---+
        '     x[0]   x[1] x[2]    x[3]
        '
        _positions = new Point(4)
        _positions(0) = _
            new Point( _
                (((int) (canvasWidth / 2)) - _
                    ((int) (_size.width / 2)) - _size.height), _
                (((int) (canvasHeight / 2)) - _
                    ((int) (_size.width / 2)) - _size.height))
        _positions(1) = _
            new Point( _
                (_positions(0).x + _size.height), _
                (_positions(0).y + _size.height))
        _positions(2) = _
            new Point((_positions(1).x + _size.width), _
                      (_positions(1).y + _size.width))
        _positions(3) = _
            new Point((_positions(2).x + _size.height), _
                      (_positions(2).y + _size.height))

        ' There are four lights: north, east, south and west.
        ' Each light has four bulbs: green, yellow and red.
        _lights = New Color(4)(3)
        InitLights()

        ' Set the initial light durations.
        _nsLightDuration = 8000
        _ewLightDuration = 10000
        _yellowDuration = 4000

        ' Create the stop light's state machine.
        _fsm = New StoplightContext(this)

        ' Uncomment to see debug output.
        ' _fsm.setDebugFlag(true)
    End Sub

    Public Function GetLightsColor(int direction) As Color

        Dim found As Boolean
        Dim i As Integer
        Dim retval As Color

        ' Go through the light's bulbs looking for
        ' one that is *not* black.
        i = 0
        found = False
        While found = False And i < 3

            If _lights(direction)(i).getRGB() != _
                   Color.black.getRGB() _
            Then
                retval = _lights(direction)(i)
                found = True
            End If

            i += 1
        End While

        Return retval
    End Function

    Public Sub GetLightDimensions( _
       ByVal direction As Point, _
       ByVal lightLocation As  Point, _
       ByVal lightSize As Dimension)

        ' If the vehicle is heading north, it will pass under
        ' the east light.
        If direction.x = _canvas.NORTH.x And _
           direction.y = _canvas.NORTH.y _
        Then
            lightLocation.x = _positions(2).x
            lightLocation.y = _positions(1).y
            lightSize.width = _size.height
            lightSize.height = _size.width
        
        ' If the vehicle is heading east, it will pass under the
        ' south light.
        ElseIf direction.x = _canvas.EAST.x And _
               direction.y = _canvas.EAST.y _
        Then
            lightLocation.x = _positions(1).x
            lightLocation.y = _positions(2).y
            lightSize.width = _size.width
            lightSize.height = _size.height

        ' If the vehicle is heading south, it will pass under
        ' the west light.
        ElseIf direction.x = _canvas.SOUTH.x And _
               direction.y = _canvas.SOUTH.y _
        Then
            lightLocation.x = _positions(0).x
            lightLocation.y = _positions(1).y
            lightSize.width = _size.height
            lightSize.height = _size.width

        ' If the vehicle is heading west, it will pass under the
        ' north light.
        ElseIf direction.x = _canvas.WEST.x And _
               direction.y = _canvas.WEST.y _
        Then
            lightLocation.x = _positions(1).x
            lightLocation.y = _positions(0).y
            lightSize.width = _size.width
            lightSize.height = _size.height
        End If

        Return
    End Sub

    Public Function GetLightDuration(ByVal light As Integer) _
        As Integer

        Dim duration As Integer

        If light = TrafficCanvas.NS_LIGHT _
        Then
            duration = _nsLightDuration

        ElseIf light = TrafficCanvas.EW_LIGHT _
        Then
            duration _ewLightDuration

        Else
            duration = _yellowDuration
        End If

        Return duration
    End Function

    Public Sub SetLightDuration(ByVal light As Integer, _
                                ByVal duration As Integer)

        If light = TrafficCanvas.NS_LIGHT _
        Then
            _nsLightDuration = duration
        ElseIf light = TrafficCanvas.EW_LIGHT _
        Then
            _ewLightDuration = duration
        Else
            _yellowDuration = duration
        End If

        Return
    End Sub

    Public Sub StartDemo()

        _fsm.Start()
        Return
    End Sub

    Public Sub PauseDemo()

        _fsm.Pause()
        Return
    End Sub

    Public Sub ContinueDemo()

        _fsm.Continue()
        Return
    End Sub

    Public Sub StopDemo()

        _fsm.Stop()
        Return
    End Sub

    Public Sub Saint(ByRef g As Graphics)

        Dim g2 As Graphics2D = (Graphics2D) g

        ' Draw the four stop light boxes. Start with the
        ' northern light and go clockwise.
        g2.setPaint(Color.black)
        g2.fill(new Rectangle2D.Double(_positions(1).x, _
                                       _positions(0).y, _
                                       _size.width, _
                                       _size.height))
        g2.fill(new Rectangle2D.Double(_positions(2).x, _
                                       _positions(1).y, _
                                       _size.height, _
                                       _size.width))
        g2.fill(new Rectangle2D.Double(_positions(1).x, _
                                       _positions(2).y, _
                                       _size.width, _
                                       _size.height))
        g2.fill(new Rectangle2D.Double(_positions(0).x, _
                                       _positions(1).y, _
                                       _size.height, _
                                       _size.width))

        ' Now draw the lamps within the stop lights.
        ' Again, start with the northern light.
        g2.setPaint(_lights(NORTH)(GREEN))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(1).x + LAMP_SPACE), _
                (_positions(0).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(NORTH)(YELLOW))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(1).x + LAMP_SPACE), _
                (_positions(0).y + _
                    (LAMP_SPACE * 2) + LAMP_DIAMETER), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(NORTH)(RED))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(1).x + LAMP_SPACE), _
                (_positions(0).y + _
                    (LAMP_SPACE * 3) + (LAMP_DIAMETER * 2)), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))

        ' Eastern light.
        g2.setPaint(_lights(EAST)(RED))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(2).x + LAMP_SPACE), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(EAST)(YELLOW))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(2).x + _
                    (LAMP_SPACE * 2) + LAMP_DIAMETER), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(EAST)(GREEN))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(2).x + _
                    (LAMP_SPACE * 3) + (LAMP_DIAMETER * 2)), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))

        ' Southern light.
        g2.setPaint(_lights(SOUTH)(RED))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(1).x + LAMP_SPACE), _
                (_positions(2).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(SOUTH)(YELLOW))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(1).x + LAMP_SPACE), _
                (_positions(2).y + _
                    (LAMP_SPACE * 2) + LAMP_DIAMETER), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(SOUTH)(GREEN))
        g2.fill( _
            New Ellipse2D.Double( _
               (_positions(1).x + LAMP_SPACE), _
               (_positions(2).y + _
                   (LAMP_SPACE * 3) + (LAMP_DIAMETER * 2)), _
               LAMP_DIAMETER, _
               LAMP_DIAMETER))

        ' Western light.
        g2.setPaint(_lights(WEST)(GREEN))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(0).x + LAMP_SPACE), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(WEST)(YELLOW))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(0).x + _
                    (LAMP_SPACE * 2) + LAMP_DIAMETER), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
        g2.setPaint(_lights(WEST)(RED))
        g2.fill( _
            New Ellipse2D.Double( _
                (_positions(0).x + _
                    (LAMP_SPACE * 3) + (LAMP_DIAMETER * 2)), _
                (_positions(1).y + LAMP_SPACE), _
                LAMP_DIAMETER, _
                LAMP_DIAMETER))
    End Sub

    Public Sub HandleTimeout()

        ' Get rid of the current timer.
        _stoplightTimer = Nothing

        _fsm.Timeout()
        Return
    End Sub

    //-----------------------------------------------------------
    // State Machine Actions
    //

    Public Sub InitLights()

        Dim i As Integer
        Dim j As Integer

        ' The lights are initially all black.
        For i = 0 to 3 By 1

            For j = 0 to 3 By 1

                _lights(i)(j) = Color.black
            End For
        End For

        Return
    End Sub

    Public Sub TurnLight(ByVal light As String, _
                         ByVal color As String)

        If light.Equals("EWLIGHT") = True _
        Then
            If color.Equals("RED") = True _
            Then
                _lights(EAST)(GREEN) = Color.black
                _lights(EAST)(YELLOW) = Color.black
                _lights(EAST)(RED) = Color.red
                _lights(WEST)(GREEN) = Color.black
                _lights(WEST)(YELLOW) = Color.black
                _lights(WEST)(RED) = Color.red

            ElseIf color.Equals("GREEN") = True _
            Then
                _lights(EAST)(GREEN) = Color.green
                _lights(EAST)(YELLOW) = Color.black
                _lights(EAST)(RED) = Color.black
                _lights(WEST)(GREEN) = Color.green
                _lights(WEST)(YELLOW) = Color.black
                _lights(WEST)(RED) = Color.black

            ElseIf color.Equals("YELLOW") = True _
            Then
                _lights(EAST)(GREEN) = Color.black
                _lights(EAST)(YELLOW) = Color.yellow
                _lights(EAST)(RED) = Color.black
                _lights(WEST)(GREEN) = Color.black
                _lights(WEST)(YELLOW) = Color.yellow
                _lights(WEST)(RED) = Color.black
            End If

        ElseIf light.Equals("NSLIGHT") = True _
        Then
            If color.Equals("RED") = True _
            Then
                _lights(NORTH)(GREEN) = Color.black
                _lights(NORTH)(YELLOW) = Color.black
                _lights(NORTH)(RED) = Color.red
                _lights(SOUTH)(GREEN) = Color.black
                _lights(SOUTH)(YELLOW) = Color.black
                _lights(SOUTH)(RED) = Color.red

            ElseIf color.Equals("GREEN") = True _
            Then
                _lights(NORTH)(GREEN) = Color.green
                _lights(NORTH)(YELLOW) = Color.black
                _lights(NORTH)(RED) = Color.black
                _lights(SOUTH)(GREEN) = Color.green
                _lights(SOUTH)(YELLOW) = Color.black
                _lights(SOUTH)(RED) = Color.black

            ElseIf color.Equals("YELLOW") = True _
            Then
                _lights(NORTH)(GREEN) = Color.black
                _lights(NORTH)(YELLOW) = Color.yellow
                _lights(NORTH)(RED) = Color.black
                _lights(SOUTH)(GREEN) = Color.black
                _lights(SOUTH)(YELLOW) = Color.yellow
                _lights(SOUTH)(RED) = Color.black
            End If
        End If

        Return
    End Sub

    Public Sub InformCanvas(ByVal greenlight As String)

        // Tell the owning canvas that the light has changed.
        // Also tell the canvas if a light is now green.
        If greenlight.Equals("YELLOW") = True _
        Then
            _canvas.lightChanged(TrafficCanvas.YELLOW_LIGHT)

        ElseIf greenlight.Equals("EastWest") = True _
        Then
            _canvas.lightChanged(TrafficCanvas.EW_LIGHT)

        Else
            _canvas.lightChanged(TrafficCanvas.NS_LIGHT)
        End If

        Return
    End Sub

    Public Void SetTimer(ByVal direction As String)

        Dim duration As Integer

        If direction.Equals("NorthSouth") = True _
        Then
            duration = _nsLightDuration

        ElseIf direction.Equals("EastWest") = True _
        Then
            duration = _ewLightDuration

        Else
            duration = _yellowDuration
        End If

        SetTimer(duration)

        Return
    End Sub

    Public Sub SetTimer(ByVal duration As Integer)

        ' Remember when this timer was set and how long it
        ' lasts. This information is needed for pausing and
        ' continuing.
        _timerStart = new java.util.Date()
        _timerDuration = duration

        ' If the timer already exists, then get rid of it.
        If _stoplightTimer != null _
        Then
            ' If the timer is running, then stop it, reset
            ' its timeout value and run it again.
            If _stoplightTimer.isRunning() = True _
            Then
                _stoplightTimer.stop()
            End If

            _stoplightTimer = Nothing
        End If

        _stoplightTimer = _
            New Timer(_timerDuration, _
                      New StoplightTimeoutListener(Me))
        _stoplightTimer.setRepeats(False)
        _stoplightTimer.start()

        Return
    End Sub

    Public Sub PauseTimer()

        ' Figure out how much time is left on the timer and
        ' use that value when continuing the timer.
        Dim currTime As DateTime = New DateTime()

        ' Stop the timer now and do the calculations after.
        If Not IsNothing(_stoplightTimer) And _
           _stoplightTimer.isRunning() = True _
        Then
            _stoplightTimer.stop()
        End If

        _timerDuration = _
            _timerDuration - _
                (int) (currTime.getTime() - _
                _timerStart.getTime())
        If _timerDuration < 0 _
        Then
            _timerDuration = 0
        End If
        _timerStart = Nothing

        Return
    End Sub

    Public Sub ContinueTimer()

        ' Pick up where we left off by restarting the timer.
        SetTimer(_timerDuration)

        Return
    End Sub

    Public void StopTimer()

        ' Stop the timer and clear the timer data.
        If Not IsNothing(_stoplightTimer) And _
           _stoplightTimer.isRunning() = True _
        Then
            _stoplightTimer.stop()
        End If

        _stoplightTimer = Nothing
        _timerDuration = 0
        _timerStart = Nothing

        Return
    End Sub

    //
    // end of State Machine Actions
    //-----------------------------------------------------------

End Class
