import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Pac extends JApplet
{

    private Pacman _pacman;
    private Ghost  _ghosts [ ];
    private Cherry _cherries [ ];
    private Money _moneys [ ];
    private Wall   _wall;


    private JButton _leftButton;
    private JButton _rightButton;
    private JButton _upButton;
    private JButton _downButton;
    private JButton _startAnimationButton;

    private JLabel _scoreLabel;
    private JTextField _scoreTextField;
    private JTextField _gameStatus;

    private int _imagesCreated;

    public void init ( )
    {
        //super ( "Hans Ji's Leisure Development" );
        //setSize ( 600, 500 );

        JPanel buttonPanel = new JPanel ( );
        _leftButton = new JButton ( "Left" );
        _rightButton = new JButton ( "Right" );
        _upButton = new JButton ( "Up" );
        _downButton = new JButton ( "Down" );
        _startAnimationButton = new JButton ( "Start" );
        

        buttonPanel.setLayout ( new BorderLayout ( ) );
        buttonPanel.add ( _leftButton, BorderLayout.WEST );
        buttonPanel.add ( _rightButton, BorderLayout.EAST );
        buttonPanel.add ( _upButton, BorderLayout.NORTH );
        buttonPanel.add ( _downButton, BorderLayout.SOUTH );
        buttonPanel.add ( _startAnimationButton, BorderLayout.CENTER );

 
        Container container = getContentPane ( );
        container.setLayout ( new BorderLayout ( ) );
        container.add ( buttonPanel, BorderLayout.EAST );

        JPanel labelTextFieldPanel = new JPanel ( );

        _scoreLabel = new JLabel ( "Your Score: ");
        _scoreTextField = new JTextField ( "00000" );
        _gameStatus = new JTextField ( 40 );
        _scoreTextField.setEditable ( false );
        labelTextFieldPanel.setLayout ( new FlowLayout ( ) );
        labelTextFieldPanel.add ( _scoreLabel );
        labelTextFieldPanel.add ( _scoreTextField );
        labelTextFieldPanel.add ( _gameStatus );
        _gameStatus.setText ( "Pacman is alive" );

        container.add ( labelTextFieldPanel, BorderLayout.SOUTH );

        _imagesCreated = 0;

        /*
        show ( );
        */

        setVisible ( true );

        //Sleeper.sleep ( 1000 ); //to draw frame before other objects 
        Graphics g = getGraphics ( );

        _wall = new Wall ( g );
        _wall.draw ( );

        final int cherry_x0 = Wall._ORIGIN_X + Wall._WIDTH / 2 - 
                             Cherry._WIDTH / 2;
        final int cherry_y0 = Wall._ORIGIN_Y + Wall._WIDTH / 2 - 
                             Cherry._HEIGHT / 2;

        final int cherry_x1 = _wall.getCherryX ( 1 );
        final int cherry_y1 = cherry_y0;

        final int cherry_x2 = _wall.getCherryX ( 2 );
        final int cherry_y2 = cherry_y0;

        _cherries = new Cherry [ 3 ];
        _cherries [ 0 ] = new Cherry ( cherry_x0, cherry_y0, g ); //in middle
        _cherries [ 1 ] = new Cherry ( cherry_x1, cherry_y1, g );
        _cherries [ 2 ] = new Cherry ( cherry_x2, cherry_y2, g );

        _cherries [ 0 ].draw ( );
        _cherries [ 1 ].draw ( );
        _cherries [ 2 ].draw ( );

        _moneys = new Money [ 16 ];
        final int money_x [ ] = new int [ 16 ];
        final int money_y [ ] = new int [ 16 ];
        setCoordinatesForMoneys ( money_x,  money_y );
        
        for ( int i = 0; i < _moneys.length; i++ )
        {
            _moneys [ i ] = new Money ( money_x [ i ], money_y [ i ], g, 
                                        super.getBackground ( ) );
            _moneys [ i ].draw ( );
        }

        final int pacmanX = Wall._ORIGIN_X + Wall._THICKNESS;
        final int pacmanY = Wall._ORIGIN_Y + Wall._THICKNESS;

        _pacman = new Pacman ( pacmanX, pacmanX, g, _wall, _moneys, _cherries,
                               _scoreTextField, _gameStatus ); 

        final int ghostX[ ] = new int [ 4 ];
        final int ghostY[ ] = new int [ 4 ];
        ghostX [ 0 ] = cherry_x1 + Cherry._WIDTH;
        ghostY [ 0 ] = cherry_y0 - Ghost._HEIGHT;

        ghostX [ 1 ] = cherry_x2 - Ghost._HEIGHT;
        ghostY [ 1 ] = cherry_y2 - Ghost._HEIGHT;

        ghostX [ 2 ] = ghostX [ 0 ];
        ghostY [ 2 ] = cherry_y0 + Cherry._HEIGHT;

        ghostX [ 3 ] = ghostX [ 1 ];
        ghostY [ 3 ] = cherry_y0 + Cherry._HEIGHT;

        _ghosts = new Ghost [ 4 ];
        _ghosts [ 0 ] = new Ghost ( ghostX [ 0 ], ghostY [ 0 ], g, _pacman, 
                                    _scoreTextField, _gameStatus );
        _ghosts [ 1 ] = new Ghost ( ghostX [ 1 ], ghostY [ 1 ], g, _pacman, 
                                    _scoreTextField, _gameStatus );
        _ghosts [ 2 ] = new Ghost ( ghostX [ 2 ], ghostY [ 2 ], g, _pacman, 
                                    _scoreTextField, _gameStatus );
        _ghosts [ 3 ] = new Ghost ( ghostX [ 3 ], ghostY [ 3 ], g, _pacman, 
                                    _scoreTextField, _gameStatus );

        _ghosts [ 0 ].draw ( );
        _ghosts [ 1 ].draw ( );
        _ghosts [ 2 ].draw ( );
        _ghosts [ 3 ].draw ( );
        
        _pacman.setGhosts ( _ghosts );

        StartAnimationHandler startAnimationHandler = 
                      new StartAnimationHandler ( _pacman, _ghosts, 
                          _cherries, _moneys,  _startAnimationButton );
        _startAnimationButton.addActionListener ( startAnimationHandler );

        Timer timer = startAnimationHandler.getTimer ( );
        _pacman.setTimer ( timer );

        ButtonHandler buttonHandler = new ButtonHandler ( _pacman, _leftButton, 
                         _rightButton, _upButton, _downButton, 
                         _startAnimationButton );
        _rightButton.addActionListener ( buttonHandler );
        _leftButton.addActionListener ( buttonHandler );
        _upButton.addActionListener ( buttonHandler );
        _downButton.addActionListener ( buttonHandler );
        _pacman.draw ( );
        _imagesCreated = 1;

    }
    
    public void paint ( Graphics g_ )
    {
        _leftButton.repaint ( );
        _rightButton.repaint ( );
        _upButton.repaint ( );
        _downButton.repaint ( );
        _startAnimationButton.repaint ( );
        _scoreLabel.repaint ( );
        _scoreTextField.repaint ( );
        _gameStatus.repaint ( );
        if ( _imagesCreated == 1)
        {
            _wall.draw ( );
            _cherries [ 0 ].draw ( );
            _cherries [ 1 ].draw ( );
            _cherries [ 2 ].draw ( );
            for ( int i = 0; i < _moneys.length; i++ )
            {
                _moneys [ i ].draw ( );
            }
            for ( int i = 0; i < _ghosts.length; i++ )
            {
                if ( _ghosts [ i ].getEaten ( ) == 0 )
                {
                    _ghosts [ i ].draw ( );
                }
            }
            _pacman.draw ( );
        }
   }

    private void setCoordinatesForMoneys ( final int money_x_ [ ], 
                                           final int money_y_ [ ])
    {
        money_x_ [ 0 ] = Wall._X5 + Wall._THICKNESS + Wall._GAP / 2 - 
                         Money._WIDTH / 2;
        money_y_ [ 0 ] = Wall._ORIGIN_Y + Wall._THICKNESS + Wall._GAP / 2 - 
                         Money._HEIGHT / 2;
        money_x_ [ 1 ] = Wall._X2 + Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 1 ] = money_y_ [ 0 ];
        money_x_ [ 2 ] = Wall._X7 - Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 2 ] = money_y_ [ 0 ];

        money_x_ [ 3 ] = Wall._X5 + Wall._THICKNESS + Wall._GAP / 2 - 
                         Money._WIDTH / 2;
        money_y_ [ 3 ] = Wall._Y4 + Wall._THICKNESS + Wall._GAP / 2 - 
                         Money._HEIGHT / 2;
        money_x_ [ 4 ] = Wall._X2 + Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 4 ] = money_y_ [ 3 ];
        money_x_ [ 5 ] = Wall._X7 - Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 5 ] = money_y_ [ 3 ];

        money_x_ [ 6 ] = Wall._ORIGIN_X + Wall._THICKNESS + Wall._GAP / 2 - 
                         Money._WIDTH / 2;
        money_y_ [ 6 ] = Wall._Y1 + Wall._THICKNESS + Wall._GAP;
        money_x_ [ 7 ] = money_x_ [ 6 ];
        money_y_ [ 7 ] = Wall._Y2 + Wall._GAP / 2 - Money._HEIGHT / 2;
        money_x_ [ 8 ] = money_x_ [ 6 ];
        money_y_ [ 8 ] = Wall._Y4 - Wall._GAP - Money._HEIGHT;


        money_x_ [ 9 ] =  Wall._X4 + Wall._THICKNESS + Wall._GAP / 2 - 
                          Money._WIDTH / 2;
        money_y_ [ 9 ] =  Wall._Y1 + Wall._THICKNESS + Wall._GAP;
        money_x_ [ 10 ] = money_x_ [ 9 ]; 
        money_y_ [ 10 ] = Wall._Y2 + Wall._GAP / 2 - Money._HEIGHT / 2;
        money_x_ [ 11 ] = money_x_ [ 9 ];
        money_y_ [ 11 ] = Wall._Y4 - Wall._GAP - Money._HEIGHT;

        money_x_ [ 12 ] = Wall._X2 + Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 12 ] = Wall._Y1 + Wall._THICKNESS + Wall._GAP / 2 - 
                          Money._HEIGHT / 2;
        money_x_ [ 13 ] = Wall._X2 + Wall._GAP / 2 - Money._WIDTH / 2;
        money_y_ [ 13 ] = Wall._Y5 + Wall._THICKNESS + Wall._GAP / 2 - 
                          Money._HEIGHT / 2;
        money_x_ [ 14 ] = Wall._X1 + Wall._THICKNESS + Wall._GAP / 2 - 
                          Money._WIDTH / 2;
        money_y_ [ 14 ] = Wall._Y2 + Wall._GAP / 2 - Money._HEIGHT / 2;
        money_x_ [ 15 ] = Wall._X7 + Wall._THICKNESS + Wall._GAP / 2 - 
                          Money._WIDTH / 2;;
        money_y_ [ 15 ] = Wall._Y2 + Wall._GAP / 2 - Money._HEIGHT / 2;
        
    }
    public static void main ( String args [ ] )
    {
        Pac pac = new Pac ( );
        /*
        WindowHandler wh = new  WindowHandler ( );  
        pac.addWindowListener ( wh );  
        */
    }
}



class Pacman
{
    public final static int _LEFT = 0;
    public final static int _RIGHT = 1;
    public final static int _UP = 2;
    public final static int _DOWN = 3;
    public final static Color _PACMAN_COLOR = Color.blue;
    
    
    public final static int _WIDTH = 40;
    public final static int _HEIGHT = 40;
    private final int _MOVE_DISTANCE = 10; 

    private final int _EYE_WIDTH = _WIDTH / 5;
    private final int _EYE_HEIGHT = _EYE_WIDTH;
    
    private int _x;
    private int _y;
    private int _direction; 
    private int _deadTimes;
    private final Graphics _g;
    private final Wall _wall;
    private final Money _moneys [ ];
    private final Cherry _cherries [ ];
    private Ghost _ghosts [ ];

    private JTextField _scoreTextField;
    private JTextField _gameStatus;

    private int _totalScore;

    private Timer _timer;

    public Pacman ( final int x_, final int y_, final Graphics g_, 
                    final Wall wall_, final Money moneys_ [ ], 
                    final Cherry cherries_ [ ],
                    final JTextField scoreTextField_, 
                    final JTextField gameStatus_ )
    {
        _x = x_;
        _y = y_;
        _direction = _RIGHT;
        _g = g_;
        _wall = wall_;
        _moneys = moneys_;
        _cherries = cherries_;
        _ghosts = null;
        _scoreTextField = scoreTextField_;
        _gameStatus = gameStatus_;

        _deadTimes = 0;
        _totalScore = 0;
    }

    public void setMovingDirection ( final int direction_ )
    {
        _direction = direction_;
    }

    public void draw ( )
    {
        _g.setColor ( _PACMAN_COLOR );
        if ( _direction == _RIGHT )
        {
            _g.fillArc (  _x, _y, _WIDTH, _HEIGHT, 45, 280 );
        }
        else if ( _direction == _LEFT )
        {
            _g.fillArc (  _x, _y, _WIDTH, _HEIGHT, 135, -280 );
        }
        else if ( _direction == _UP )
        {
            _g.fillArc (  _x, _y, _WIDTH, _HEIGHT, 135, 280 );
        }
        else if ( _direction == _DOWN )
        {
            _g.fillArc (  _x, _y, _WIDTH, _HEIGHT, 225, -280 );
        }
        else
        {        //default image
            _g.fillArc (  _x, _y, _WIDTH, _HEIGHT, 45, 280 );
        }
        drawEye ( _direction );
    }

    private void drawEye ( final int direction_ )
    {
        _g.setColor ( Color.red );
        if ( (direction_ == _RIGHT ) || ( direction_ == _LEFT ) )
        {
            _g.fillOval ( _x + _WIDTH / 2 - _EYE_WIDTH / 2, _y + _HEIGHT / 7, 
                          _EYE_WIDTH, _EYE_HEIGHT );
        }
        if ( (direction_ == _UP ) || ( direction_ == _DOWN ) )
        {
            _g.fillOval ( _x + _WIDTH / 7, _y + _HEIGHT / 2 - _EYE_HEIGHT / 2,
                          _EYE_WIDTH, _EYE_HEIGHT );
        }
    }

    public void mouthCloseOpen ( )
    {
        closeMouth ( );
        Sleeper.sleep ( TimerHandler._ANIMATION_DELAY_TIME / 3 );
        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        openMouth ( );
    }

    public void move ( )
    {

        int moveDistance;
        if ( _direction ==  _RIGHT )
        {   
            int rightLimitOfX;
            if (  _y == Wall._ORIGIN_Y + Wall._THICKNESS   ||
                  _y == Wall._Y1 + Wall._THICKNESS         ||
                  _y == Wall._Y5 + Wall._THICKNESS         ||
                  _y == Wall._Y4 + Wall._THICKNESS )
            {
                rightLimitOfX = Wall._ORIGIN_X + Wall._WIDTH - 
                                    Wall._THICKNESS - _WIDTH;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._ORIGIN_X <= _x ) && 
                   ( _x <= Wall._ORIGIN_X + Wall._THICKNESS) ) )
            {
                rightLimitOfX = Wall._ORIGIN_X + Wall._THICKNESS;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._X4 + Wall._THICKNESS <= _x ) && 
                   (_x <= Wall._X4 + Wall._THICKNESS + Wall._THICKNESS ) ) )
            {

                if ( _x + _MOVE_DISTANCE <= Wall._X4 + Wall._THICKNESS + 
                     Wall._THICKNESS )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
                    if ( moveDistance >= _WIDTH )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y, moveDistance, _HEIGHT ); 
                    }
                    _x = _x + moveDistance;
                }
                else
                {
                    moveDistance = _x - Wall._ORIGIN_X;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _x = _x - moveDistance;
                }
                return;
            }

            //center big empty area
            if ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                   <=_y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                   && ( ( Wall._X5 + Wall._THICKNESS <=_x ) && 
                   ( _x <= Wall._X7 - _WIDTH ) ) )
            {
                rightLimitOfX = Wall._X7 - _WIDTH;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            return;
        }

        if ( _direction == _LEFT )
        {
            int leftLimitOfX;
            if (  _y == Wall._ORIGIN_Y + Wall._THICKNESS   ||
                  _y == Wall._Y1 + Wall._THICKNESS         ||
                  _y == Wall._Y5 + Wall._THICKNESS         ||
                  _y == Wall._Y4 + Wall._THICKNESS )
            {
                leftLimitOfX = Wall._ORIGIN_X + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._ORIGIN_X <= _x ) && 
                   ( _x <= Wall._ORIGIN_X + Wall._THICKNESS) ) )
            {
                if ( _x - _MOVE_DISTANCE >= Wall._ORIGIN_X )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
                    if ( moveDistance >= _WIDTH )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x + ( _WIDTH - moveDistance ), 
                                       _y, moveDistance, _HEIGHT ); 
                    }
                    _x = _x - moveDistance;
                }
                else
                {
                    moveDistance = Wall._X4 + Wall._THICKNESS + 
                                   Wall._THICKNESS - _x;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _x = _x + moveDistance;
                }
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._X4 + Wall._THICKNESS <= _x ) && 
                   (_x <= Wall._X4 + Wall._THICKNESS + Wall._THICKNESS ) ) )
            {
                leftLimitOfX = Wall._X4 + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            if ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                   <=_y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                   && ( ( Wall._X5 + Wall._THICKNESS <=_x ) && 
                   ( _x <= Wall._X7 - _WIDTH ) ) )
            {
                leftLimitOfX = Wall._X5 + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            return;
        }

        if ( _direction == _UP )
        {
            int upLimitOfY;
            if ( ( _x == Wall._ORIGIN_X + Wall._THICKNESS ) ||
                 ( _x == Wall._X4 + Wall._THICKNESS )
               )
            {
                upLimitOfY = Wall._ORIGIN_Y + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }
            if ( ( ( Wall._Y1 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y5 + Wall._THICKNESS ) 
                 ) &&
                 ( ( _x == Wall._X1 + Wall._THICKNESS ) || 
                   ( _x == Wall._X5 + Wall._THICKNESS ) ||
                   ( _x == Wall._X7 - _WIDTH )          ||
                   ( _x == Wall._X7 + Wall._THICKNESS )
                 )
               )
            {
                upLimitOfY = Wall._Y1 + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }

            if ( ( ( Wall._X5 + Wall._THICKNESS <= _x ) && 
                   ( _x <= Wall._X7 - Wall._GAP ) 
                 ) && 
                 ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                     <= _y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                 )
               )
            {
                upLimitOfY = Wall._Y1 + Wall._THICKNESS + Wall._GAP + 
                             Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2  ) && 
                 ( ( Wall._ORIGIN_Y <= _y ) && 
                   ( _y <= Wall._ORIGIN_Y + Wall._THICKNESS ) 
                 )
               )
            {
                if ( _y - _MOVE_DISTANCE >= Wall._ORIGIN_Y )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
                    if ( moveDistance >= _HEIGHT )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y + ( _HEIGHT - moveDistance ), 
                                       _WIDTH, moveDistance ); 
                    }
                    _y = _y - moveDistance;
                }
                else
                {
                    moveDistance = Wall._Y4 + Wall._THICKNESS + 
                                   Wall._THICKNESS - _y; 
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _y = _y + moveDistance;
                }
                return;
            }

            if ( ( _x == Wall._X2 ) && 
                 ( ( Wall._Y4 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y4 + Wall._THICKNESS + Wall._THICKNESS ) 
                 )
               )
            {
                upLimitOfY = Wall._Y4 + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }
            return;
        }

        if ( _direction == _DOWN )
        {   
            int downLimitOfY; 
         
            if ( ( _x == Wall._ORIGIN_X + Wall._THICKNESS ) ||
                 ( _x == Wall._X4 + Wall._THICKNESS )
               )
            {
                downLimitOfY = Wall._ORIGIN_Y + Wall._WIDTH - Wall._THICKNESS - 
                                   _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( ( Wall._Y1 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y5 + Wall._THICKNESS ) 
                 ) &&
                 ( ( _x == Wall._X1 + Wall._THICKNESS ) || 
                   ( _x == Wall._X5 + Wall._THICKNESS ) ||
                   ( _x == Wall._X7 - _WIDTH )          ||
                   ( _x == Wall._X7 + Wall._THICKNESS )
                 )
               )
            {
                downLimitOfY = Wall._Y4 - _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( ( Wall._X5 + Wall._THICKNESS <= _x ) && 
                   ( _x <= Wall._X7 - Wall._GAP ) 
                 ) && 
                 ( ( Wall._Y1 + Wall._THICKNESS+ Wall._GAP + Wall._THICKNESS 
                     <= _y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                 )
               )
            {
                downLimitOfY = Wall._Y5 - _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2  ) && 
                 ( ( Wall._ORIGIN_Y <= _y ) && 
                   ( _y <= Wall._ORIGIN_Y + Wall._THICKNESS ) 
                 )
               )
            {
                downLimitOfY = Wall._ORIGIN_Y + Wall._THICKNESS;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2 ) && 
                 ( ( Wall._Y4 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y4 + Wall._THICKNESS + Wall._THICKNESS ) 
                 )
               )
            {
                if ( _y + _MOVE_DISTANCE <= Wall._Y4 + Wall._THICKNESS + 
                     Wall._THICKNESS   )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
                    if ( moveDistance >= _HEIGHT )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y, _WIDTH, moveDistance ); 
                    }
                    _y = _y + moveDistance;
                }
                else
                {
                    moveDistance = _y - Wall._ORIGIN_Y;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _y = _y - moveDistance;
                }
                return;
            }
            return;
        }
    }

    public void detectCollisionWithMoneys ( )
    { 
        StringBuffer scoreStringBuffer = new StringBuffer ( );
        for ( int i = 0; i < _moneys.length; i++ )
        {
            if ( _moneys [ i ].getEaten ( ) == 1 )
            {
                continue;
            }
            int pacmanCenterX = _x + _WIDTH / 2;
            int pacmanCenterY = _y + _HEIGHT / 2;
            int moneyCenterX = _moneys [ i ].getX ( ) + Money._WIDTH / 2; 
            int moneyCenterY = _moneys [ i ].getY ( ) + Money._HEIGHT / 2; 
            int collidingDistance = ( _WIDTH + Money._WIDTH ) / 2;
            int pacmanMoneyDistanceSquare = ( pacmanCenterX - moneyCenterX ) *
                                            ( pacmanCenterX - moneyCenterX ) + 
                                            ( pacmanCenterY - moneyCenterY ) *
                                            ( pacmanCenterY - moneyCenterY );   
            if ( pacmanMoneyDistanceSquare < collidingDistance * 
                 collidingDistance ) 
            {
                System.out.println ( "Pacman ate a money, left" );
                _totalScore += 100;
                scoreStringBuffer.append ( _totalScore );
                _scoreTextField.setText ( 
                                   scoreStringBuffer.toString ( ) );
                String statusString = "Pacman ate money " + i;
                _gameStatus.setText ( statusString );
                _moneys [ i ].setEaten ( 1 );
                return;
            }
        }
    }
    
    public void detectCollisionWithCherries ( ) // for central empty area
    {
        StringBuffer scoreStringBuffer = new StringBuffer ( );
        for ( int i = 0; i < _cherries.length; i++ )
        {
            if ( _cherries [ i ].getEaten ( ) == 1 )
            {
                continue;
            }
            int pacmanCenterX = _x + _WIDTH / 2;
            int pacmanCenterY = _y + _HEIGHT / 2;
            int cherryCenterX = _cherries [ i ].getX ( ) + Cherry._WIDTH / 2; 
            int cherryCenterY = _cherries [ i ].getY ( ) + Cherry._HEIGHT / 2; 
            int collidingDistance = ( _WIDTH + Cherry._WIDTH ) / 2;
            int pacmanCherryDistanceSquare=( pacmanCenterX - cherryCenterX ) *
                                           ( pacmanCenterX - cherryCenterX ) + 
                                           ( pacmanCenterY - cherryCenterY ) *
                                           ( pacmanCenterY - cherryCenterY );   

            if ( pacmanCherryDistanceSquare < collidingDistance * 
                 collidingDistance ) 

            {
                System.out.println ( "Pacman ate a cherry" );
                _totalScore += 200;
                scoreStringBuffer.append ( _totalScore );
                _scoreTextField.setText ( 
                                   scoreStringBuffer.toString ( ) );
                String statusString = "Pacman ate cherry " + i;
                _gameStatus.setText ( statusString );
                _cherries [ i ].setEaten ( 1 );
                return;
            }
        }
    }

    public void detectCollisionWithGhosts ( )
    { 
        if ( _ghosts == null ) 
        {
            System.out.println ( "Error: ghosts not set!" );
            return;
        }
        StringBuffer scoreStringBuffer = new StringBuffer ( );
        for ( int i = 0; i < _ghosts.length; i++ )
        {
            if ( _ghosts [ i ].getEaten ( ) == 1 )
            {
                continue;
            }
            int pacmanCenterX = _x + _WIDTH / 2;
            int pacmanCenterY = _y + _HEIGHT / 2;
            int ghostCenterX = _ghosts [ i ].getX ( ) + Ghost._WIDTH / 2; 
            int ghostCenterY = _ghosts [ i ].getY ( ) + Ghost._HEIGHT / 2; 
            int collidingDistance = ( _WIDTH + Ghost._WIDTH ) / 2;
            int pacmanGhostDistanceSquare = ( pacmanCenterX - ghostCenterX ) *
                                            ( pacmanCenterX - ghostCenterX ) + 
                                            ( pacmanCenterY - ghostCenterY ) *
                                            ( pacmanCenterY - ghostCenterY );   
            if ( pacmanGhostDistanceSquare < collidingDistance * 
                 collidingDistance ) 
            {
                System.out.println ( "Pacman collided with a ghost" );
                if ( allCherriesEaten ( ) == 1 )
                {
                    _totalScore += 500;
                    scoreStringBuffer.append ( _totalScore );
                    _scoreTextField.setText ( scoreStringBuffer.toString ( ) );
                    String statusString = "Pacman collided with ghost " + i;
                    _gameStatus.setText ( statusString );
                    _ghosts [ i ].clearImage ( );
                    _ghosts [ i ].setEaten ( 1 );
                }
                else
                {
                    _totalScore = 0;
                    _deadTimes++;
                    scoreStringBuffer.append ( _totalScore );
                    _scoreTextField.setText ( scoreStringBuffer.toString ( ) );
                    String statusString = "Pacman collided with ghost " + i + 
                                      ", Pacman died " + _deadTimes + " times";
                    _gameStatus.setText ( statusString );
                    revertToOriginalState ( );
                }
            }
        }
    }

    public int allCherriesEaten ( )
    {
        for ( int i = 0; i < _cherries.length; i++ )
        {
            if ( _cherries [ i ].getEaten ( ) == 0 )
            {
                return 0;
            }
        }
        return 1;
    }

    public void detectGameOver ( final JButton startAnimationButton_ )
    {
        int allEatenUp = 1;
        for ( int i = 0; i < _moneys.length; i++ )
        {
            if ( _moneys [ i ].getEaten ( ) == 0 )
            {
                allEatenUp = 0;
                break;
            }
        }
        for ( int i = 0; i < _cherries.length; i++ )
        {
            if ( _cherries [ i ].getEaten ( ) == 0 )
            {
                allEatenUp = 0;
                break;
            }
        }
        for ( int i = 0; i < _ghosts.length; i++ )
        {
            if ( _ghosts [ i ].getEaten ( ) == 0 )
            {
                allEatenUp = 0;
                break;
            }
        }
        if ( allEatenUp == 1 || _deadTimes >= 3 ) //game is over!!!
        {
            String statusString = "Game is over!   Your Score is: " + 
                                  _totalScore;
            _gameStatus.setText ( statusString );
            startAnimationButton_.setEnabled ( true );
            revertEverythingToOriginalState ( );

        }
    }

    private void revertEverythingToOriginalState ( )
    {
        for ( int i = 0; i < _moneys.length; i++ )
        {
             _moneys [ i ].setEaten ( 0 );
             _moneys [ i ].draw ( );
        }
        for ( int i = 0; i < _cherries.length; i++ )
        {
             _cherries [ i ].setEaten ( 0 );
             _cherries [ i ].draw ( );
        }
        for ( int i = 0; i < _ghosts.length; i++ )
        {
             _ghosts [ i ].setEaten ( 0 );
             _ghosts [ i ].revertToOriginalState ( );
        }
        _deadTimes = 0;
        _scoreTextField.setText ( "00000" );
        _totalScore = 0;
        this.revertToOriginalState ( );
        _timer.stop ( );
    }

    public void clearImage ( )
    {
        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
    }

    private void closeMouth ( )
    {
        _g.setColor ( _PACMAN_COLOR );
        _g.fillOval ( _x, _y, _WIDTH, _HEIGHT );
        drawEye ( _direction );
    }

    private void moveImageRightward ( final int rightLimitOfX_ )
    {
        int moveDistance;
        if ( _x + _MOVE_DISTANCE < rightLimitOfX_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = rightLimitOfX_ - _x;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
        if ( moveDistance >= _WIDTH )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y, moveDistance, _HEIGHT ); 
        }
        _x = _x + moveDistance;
        return;
    }

    private void moveImageLeftward ( final int leftLimitOfX_ )
    {
        int moveDistance;
        if ( _x - _MOVE_DISTANCE > leftLimitOfX_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = _x - leftLimitOfX_;
        }

        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
        if ( moveDistance >= _WIDTH )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x + ( _WIDTH - moveDistance ), 
                           _y, moveDistance, _HEIGHT ); 
        }
        _x = _x - moveDistance;
        return;
    }

    private void moveImageUpward ( final int upLimitOfY_ )
    {
        int moveDistance;
        if ( _y - _MOVE_DISTANCE > upLimitOfY_ )
        {
            moveDistance = _MOVE_DISTANCE;
        } 
        else
        {
            moveDistance = _y - upLimitOfY_;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
        if ( moveDistance >= _HEIGHT )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y + ( _HEIGHT - moveDistance ), 
                           _WIDTH, moveDistance ); 
        }
        _y = _y - moveDistance;
        return;
    }

    private void moveImageDownward ( final int downLimitOfY_ )
    {
        int moveDistance;
        if ( _y + _MOVE_DISTANCE < downLimitOfY_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = downLimitOfY_ - _y;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
        if ( moveDistance >= _HEIGHT )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y, _WIDTH, moveDistance ); 
        }
        _y = _y + moveDistance;
        return;
    }

    private void openMouth ( )
    {
        draw ( );
    }

    public int getMovingDirection ( )
    {
        return _direction;
    }

    public void setGhosts ( final Ghost ghosts_ [ ] )
    {
        _ghosts = ghosts_;
    }

    public int getX ( ) 
    { return _x; }

    public int getY ( ) 
    { return _y; }

    public void increaseScore ( final int amount_ ) { _totalScore += amount_; }

    public void increaseDeadTimes ( ) { _deadTimes++; }

    public void resetScore ( ) { _totalScore = 0; }

    public int getScore ( ) { return _totalScore; }

    public int getDeadTimes ( ) { return _deadTimes; }
    
    public void revertToOriginalState ( )
    {
        clearImage ( );
        _x = Wall._ORIGIN_X + Wall._THICKNESS;
        _y = Wall._ORIGIN_Y + Wall._THICKNESS;
        _direction = _RIGHT;
        draw ( );
    }

    public void setTimer ( final Timer timer_ )
    {
        _timer = timer_;
    }
}

class Ghost
{
    public final static int _LEFT = 0;
    public final static int _RIGHT = 1;
    public final static int _UP = 2;
    public final static int _DOWN = 3;
    

    public final static int _WIDTH = 40;
    public final static int _HEIGHT = 40;

    private int _MOVE_DISTANCE;  //will be changed to lower case


    private final int _EYE_WIDTH = _WIDTH / 5;
    private final int _EYE_HEIGHT = _EYE_WIDTH;
    private final int _MOUTH_WIDTH = 2 * _WIDTH / 3;
    private final int _MOUTH_HEIGHT = _WIDTH / 2;
    
    private final Color _availableColors [ ] = { 
            Color.gray, Color.red, Color.green, Color.white, 
            Color.orange, Color.blue, Color.black, Color.cyan, 
            Color.darkGray, Color.magenta, Color.pink, Color.yellow, 
            Color.lightGray };

    private boolean _mouthShouldOpen;

    private int _x;
    private int _y;
    private int _initialStateX;
    private int _initialStateY;
    private int _eaten;
    private int _direction; 
    private final Color _ghostColor;
    private final Graphics _g;
    private final Pacman _pacman;

    
    private JTextField _scoreTextField;
    private JTextField _gameStatus;

    public Ghost ( final int x_, final int y_, final Graphics g_, 
                   final Pacman pacman_, 
                   final JTextField scoreTextField_, 
                   final JTextField gameStatus_ )
    {
        _x = x_;
        _y = y_;
        _initialStateX = x_;
        _initialStateY = y_;
        _eaten = 0;
        _g = g_;
        _pacman = pacman_;
        _MOVE_DISTANCE = 10;  //will be changed to lower case
        _ghostColor = getRandomColor ( );
        _mouthShouldOpen = true;

        _scoreTextField = scoreTextField_;
        _gameStatus = gameStatus_;
    }

    public void draw ( )
    {
        _g.setColor ( _ghostColor );
        _g.fillOval (  _x, _y, _WIDTH, _HEIGHT );
        drawEyesMouth ( );
    }

    public void move ( )
    {
        _direction = getRandomDirection ( );
        //max moveDistance should be <= 80
        _MOVE_DISTANCE = 10 + ( int ) ( Math.random ( ) * 70 );
        int moveDistance;
        if ( _direction ==  _RIGHT )
        {   
            int rightLimitOfX;
            if (  _y == Wall._ORIGIN_Y + Wall._THICKNESS   ||
                  _y == Wall._Y1 + Wall._THICKNESS         ||
                  _y == Wall._Y5 + Wall._THICKNESS         ||
                  _y == Wall._Y4 + Wall._THICKNESS )
            {
                rightLimitOfX = Wall._ORIGIN_X + Wall._WIDTH - 
                                    Wall._THICKNESS - _WIDTH;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._ORIGIN_X <= _x ) && 
                   ( _x <= Wall._ORIGIN_X + Wall._THICKNESS) ) )
            {
                rightLimitOfX = Wall._ORIGIN_X + Wall._THICKNESS;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._X4 + Wall._THICKNESS <= _x ) && 
                   (_x <= Wall._X4 + Wall._THICKNESS + Wall._THICKNESS ) ) )
            {

                if ( _x + _MOVE_DISTANCE <= Wall._X4 + Wall._THICKNESS + 
                     Wall._THICKNESS )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
                    if ( moveDistance >= _WIDTH )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y, moveDistance, _HEIGHT ); 
                    }
                    _x = _x + moveDistance;
                }
                else
                {
                    moveDistance = _x - Wall._ORIGIN_X;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _x = _x - moveDistance;
                }
                return;
            }
            if ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                   <=_y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                   && ( ( Wall._X5 + Wall._THICKNESS <=_x ) && 
                   ( _x <= Wall._X7 - _WIDTH ) ) )
            {
                rightLimitOfX = Wall._X7 - _WIDTH;
                moveImageRightward ( rightLimitOfX );
                return;
            }
            return;
        }

        if ( _direction == _LEFT )
        {
            int leftLimitOfX;
            if (  _y == Wall._ORIGIN_Y + Wall._THICKNESS   ||
                  _y == Wall._Y1 + Wall._THICKNESS         ||
                  _y == Wall._Y5 + Wall._THICKNESS         ||
                  _y == Wall._Y4 + Wall._THICKNESS )
            {
                leftLimitOfX = Wall._ORIGIN_X + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._ORIGIN_X <= _x ) && 
                   ( _x <= Wall._ORIGIN_X + Wall._THICKNESS) ) )
            {
                if ( _x - _MOVE_DISTANCE >= Wall._ORIGIN_X )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
                    if ( moveDistance >= _WIDTH )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x + ( _WIDTH - moveDistance ), 
                                       _y, moveDistance, _HEIGHT ); 
                    }
                    _x = _x - moveDistance;
                }
                else
                {
                    moveDistance = Wall._X4 + Wall._THICKNESS + 
                                   Wall._THICKNESS - _x;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _x = _x + moveDistance;
                }
                return;
            }
            if ( ( _y == Wall._Y2 ) && 
                 ( ( Wall._X4 + Wall._THICKNESS <= _x ) && 
                   (_x <= Wall._X4 + Wall._THICKNESS + Wall._THICKNESS ) ) )
            {
                leftLimitOfX = Wall._X4 + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            if ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                   <=_y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                   && ( ( Wall._X5 + Wall._THICKNESS <=_x ) && 
                   ( _x <= Wall._X7 - _WIDTH ) ) )
            {
                leftLimitOfX = Wall._X5 + Wall._THICKNESS;
                moveImageLeftward ( leftLimitOfX );
                return;
            }
            return;
        }

        if ( _direction == _UP )
        {
            int upLimitOfY;
            if ( ( _x == Wall._ORIGIN_X + Wall._THICKNESS ) ||
                 ( _x == Wall._X4 + Wall._THICKNESS )
               )
            {
                upLimitOfY = Wall._ORIGIN_Y + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }
            if ( ( ( Wall._Y1 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y5 + Wall._THICKNESS ) 
                 ) &&
                 ( ( _x == Wall._X1 + Wall._THICKNESS ) || 
                   ( _x == Wall._X5 + Wall._THICKNESS ) ||
                   ( _x == Wall._X7 - _WIDTH )          ||
                   ( _x == Wall._X7 + Wall._THICKNESS )
                 )
               )
            {
                upLimitOfY = Wall._Y1 + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }

            if ( ( ( Wall._X5 + Wall._THICKNESS <= _x ) && 
                   ( _x <= Wall._X7 - Wall._GAP ) 
                 ) && 
                 ( ( Wall._Y1 + Wall._THICKNESS + Wall._GAP + Wall._THICKNESS 
                     <= _y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                 )
               )
            {
                upLimitOfY = Wall._Y1 + Wall._THICKNESS + Wall._GAP + 
                             Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2  ) && 
                 ( ( Wall._ORIGIN_Y <= _y ) && 
                   ( _y <= Wall._ORIGIN_Y + Wall._THICKNESS ) 
                 )
               )
            {
                if ( _y - _MOVE_DISTANCE >= Wall._ORIGIN_Y )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
                    if ( moveDistance >= _HEIGHT )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y + ( _HEIGHT - moveDistance ), 
                                       _WIDTH, moveDistance ); 
                    }
                    _y = _y - moveDistance;
                }
                else
                {
                    moveDistance = Wall._Y4 + Wall._THICKNESS + 
                                   Wall._THICKNESS - _y; 
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _y = _y + moveDistance;
                }
                return;
            }

            if ( ( _x == Wall._X2 ) && 
                 ( ( Wall._Y4 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y4 + Wall._THICKNESS + Wall._THICKNESS ) 
                 )
               )
            {
                upLimitOfY = Wall._Y4 + Wall._THICKNESS;
                moveImageUpward ( upLimitOfY );
                return;
            }
            return;
        }

        if ( _direction == _DOWN )
        {   
            int downLimitOfY; 
         
            if ( ( _x == Wall._ORIGIN_X + Wall._THICKNESS ) ||
                 ( _x == Wall._X4 + Wall._THICKNESS )
               )
            {
                downLimitOfY = Wall._ORIGIN_Y + Wall._WIDTH - Wall._THICKNESS - 
                                   _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( ( Wall._Y1 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y5 + Wall._THICKNESS ) 
                 ) &&
                 ( ( _x == Wall._X1 + Wall._THICKNESS ) || 
                   ( _x == Wall._X5 + Wall._THICKNESS ) ||
                   ( _x == Wall._X7 - _WIDTH )          ||
                   ( _x == Wall._X7 + Wall._THICKNESS )
                 )
               )
            {
                downLimitOfY = Wall._Y4 - _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( ( Wall._X5 + Wall._THICKNESS <= _x ) && 
                   ( _x <= Wall._X7 - Wall._GAP ) 
                 ) && 
                 ( ( Wall._Y1 + Wall._THICKNESS+ Wall._GAP + Wall._THICKNESS 
                     <= _y ) && ( _y <= Wall._Y5 - _HEIGHT ) 
                 )
               )
            {
                downLimitOfY = Wall._Y5 - _HEIGHT;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2  ) && 
                 ( ( Wall._ORIGIN_Y <= _y ) && 
                   ( _y <= Wall._ORIGIN_Y + Wall._THICKNESS ) 
                 )
               )
            {
                downLimitOfY = Wall._ORIGIN_Y + Wall._THICKNESS;
                moveImageDownward ( downLimitOfY );
                return;
            }

            if ( ( _x == Wall._X2 ) && 
                 ( ( Wall._Y4 + Wall._THICKNESS <= _y ) && 
                   ( _y <= Wall._Y4 + Wall._THICKNESS + Wall._THICKNESS ) 
                 )
               )
            {
                if ( _y + _MOVE_DISTANCE <= Wall._Y4 + Wall._THICKNESS + 
                     Wall._THICKNESS   )
                {
                    moveDistance = _MOVE_DISTANCE;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
                    if ( moveDistance >= _HEIGHT )
                    {
                        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    }
                    else
                    {
                        _g.clearRect ( _x, _y, _WIDTH, moveDistance ); 
                    }
                    _y = _y + moveDistance;
                }
                else
                {
                    moveDistance = _y - Wall._ORIGIN_Y;
                    _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
                    _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
                    _y = _y - moveDistance;
                }
                return;
            }
            return;
        }
    }

    public void clearImage ( )
    {
        _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
    }

    private void drawEyesMouth ( )
    {
        _g.setColor ( getRandomColor ( ) );
        _g.fillOval ( _x + _WIDTH / 4, _y + _HEIGHT / 7, 
                      _EYE_WIDTH, _EYE_HEIGHT );
        _g.fillOval ( _x +  3 * _WIDTH / 4 - _EYE_WIDTH, _y + _HEIGHT / 7, 
                      _EYE_WIDTH, _EYE_HEIGHT );

        _g.setColor ( Color.red );
        if ( _mouthShouldOpen == true )
        {
            _g.fillOval ( _x + _WIDTH / 2 - _MOUTH_WIDTH / 2, 
                          _y +  _HEIGHT / 3, _MOUTH_WIDTH, _MOUTH_HEIGHT );
            _mouthShouldOpen = false;
        }
        else
        {
            _g.fillOval ( _x + _WIDTH / 2 - _MOUTH_WIDTH / 2, 
                          _y +  _HEIGHT / 2, _MOUTH_WIDTH, _MOUTH_HEIGHT/ 3 );
            _mouthShouldOpen = true;
        }
    }

    private Color getRandomColor ( )
    {
        int i = ( int ) ( Math.random ( ) * _availableColors.length );
        return _availableColors [ i ];
    }
    
    private int getRandomDirection ( )
    {
        int direction = ( int ) ( Math.random ( ) * 4 );
        return direction;
    }

    private void moveImageRightward ( final int rightLimitOfX_ )
    {
        int moveDistance;
        if ( _x + _MOVE_DISTANCE < rightLimitOfX_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = rightLimitOfX_ - _x;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, moveDistance, 0 );
        if ( moveDistance >= _WIDTH )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y, moveDistance, _HEIGHT ); 
        }
        _x = _x + moveDistance;
        return;
    }

    private void moveImageLeftward ( final int leftLimitOfX_ )
    {
        int moveDistance;
        if ( _x - _MOVE_DISTANCE > leftLimitOfX_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = _x - leftLimitOfX_;
        }

        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, -moveDistance, 0 );
        if ( moveDistance >= _WIDTH )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x + ( _WIDTH - moveDistance ), 
                           _y, moveDistance, _HEIGHT ); 
        }
        _x = _x - moveDistance;
        return;
    }

    private void moveImageUpward ( final int upLimitOfY_ )
    {
        int moveDistance;
        if ( _y - _MOVE_DISTANCE > upLimitOfY_ )
        {
            moveDistance = _MOVE_DISTANCE;
        } 
        else
        {
            moveDistance = _y - upLimitOfY_;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, -moveDistance );
        if ( moveDistance >= _HEIGHT )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y + ( _HEIGHT - moveDistance ), 
                           _WIDTH, moveDistance ); 
        }
        _y = _y - moveDistance;
        return;
    }

    private void moveImageDownward ( final int downLimitOfY_ )
    {
        int moveDistance;
        if ( _y + _MOVE_DISTANCE < downLimitOfY_ )
        {
            moveDistance = _MOVE_DISTANCE;
        }
        else
        {
            moveDistance = downLimitOfY_ - _y;
        }
        _g.copyArea ( _x, _y, _WIDTH, _HEIGHT, 0, moveDistance );
        if ( moveDistance >= _HEIGHT )
        {
            _g.clearRect ( _x, _y, _WIDTH, _HEIGHT ); 
        }
        else
        {
            _g.clearRect ( _x, _y, _WIDTH, moveDistance ); 
        }
        _y = _y + moveDistance;
        return;
    }

    public void detectCollisionWithPacman ( )
    { 
        StringBuffer scoreStringBuffer = new StringBuffer ( );
        int pacmanCenterX = _pacman.getX ( ) + Pacman._WIDTH / 2;
        int pacmanCenterY = _pacman.getY ( ) + Pacman._HEIGHT / 2;
        int ghostCenterX = _x + Ghost._WIDTH / 2; 
        int ghostCenterY = _y + Ghost._HEIGHT / 2; 
        int collidingDistance = ( Pacman._WIDTH + Ghost._WIDTH ) / 2;
        int pacmanGhostDistanceSquare = ( pacmanCenterX - ghostCenterX ) *
                                        ( pacmanCenterX - ghostCenterX ) + 
                                        ( pacmanCenterY - ghostCenterY ) *
                                        ( pacmanCenterY - ghostCenterY );   
        if ( pacmanGhostDistanceSquare < collidingDistance * 
             collidingDistance ) 
        {
            System.out.println ( "Ghost collided with pacman" );
            if ( _pacman.allCherriesEaten ( ) == 1 )
            {
                _pacman.increaseScore ( 500 );
                scoreStringBuffer.append ( _pacman.getScore ( ) );
                _scoreTextField.setText ( scoreStringBuffer.toString ( ) );
                String statusString = "Ghost collided with pacman, ghost eaten";
                _gameStatus.setText ( statusString );
                clearImage ( );
                setEaten ( 1 );
            }
            else
            {
                _pacman.resetScore ( );
                _pacman.increaseDeadTimes ( );
                scoreStringBuffer.append ( _pacman.getScore ( ) );
                _scoreTextField.setText ( scoreStringBuffer.toString ( ) );
                String statusString = "Ghost collided with pacman " + 
                   ", Pacman died " + _pacman.getDeadTimes ( ) + " times";
                _gameStatus.setText ( statusString );
                _pacman.revertToOriginalState ( );
            }
        }
    }

    public void revertToOriginalState ( )
    {
        clearImage ( );
        _x = _initialStateX; 
        _y = _initialStateY; 
        draw ( );
    }

    public int getX ( ) { return _x; }
    public int getY ( ) { return _y; }
    public void setEaten ( final int eatenFlag_ ) { _eaten = eatenFlag_; }
    public int getEaten ( ) { return _eaten; }
}

class Cherry
{

    public final static int _WIDTH = 40;
    public final static int _HEIGHT = 40;

    private final int _FRUIT_WIDTH = _WIDTH / 4;
    private final int _FRUIT_HEIGHT = _HEIGHT / 3;

    private int _x;
    private int _y;
    private int _eaten;
    private final Graphics _g;

    private final int _fruitX0; 
    private final int _fruitY0; 
    private final int _fruitX1;
    private final int _fruitY1;
    private final int _fruitX2;
    private final int _fruitY2;

    private final int _lineStartX;
    private final int _lineStartY;
    private final int _lineEndX0;
    private final int _lineEndY0;
    private final int _lineEndX1;
    private final int _lineEndY1;
    private final int _lineEndX2;
    private final int _lineEndY2;

    public Cherry ( final int x_, final int y_, Graphics g_ )
    {
        _x = x_;
        _y = y_;
        _eaten = 0;
        _g = g_;

        _fruitX0 = _x;
        _fruitY0 = _y + _HEIGHT - _FRUIT_HEIGHT;

        _fruitX1 = _x + _WIDTH / 2 - _FRUIT_WIDTH / 2;
        _fruitY1 = _fruitY0;
    
        _fruitX2 = _x + _WIDTH - _FRUIT_WIDTH;
        _fruitY2 = _fruitY0;

        _lineStartX = _x + _WIDTH / 2;
        _lineStartY = _y;

        _lineEndX0 = _fruitX0 + _FRUIT_WIDTH / 2;
        _lineEndY0 = _fruitY0;

        _lineEndX1 = _x + _WIDTH / 2;
        _lineEndY1 = _lineEndY0;

        _lineEndX2 = _fruitX2 + _FRUIT_WIDTH / 2;
        _lineEndY2 = _lineEndY0;
    }
    
    public void draw ( )
    {
        if ( _eaten == 1 )
        {
            return;
        }
        _g.setColor ( Color.magenta );
        _g.fillOval ( _fruitX0, _fruitY0, _FRUIT_WIDTH, _FRUIT_HEIGHT );
        _g.fillOval ( _fruitX1, _fruitY1, _FRUIT_WIDTH, _FRUIT_HEIGHT );
        _g.fillOval ( _fruitX2, _fruitY2, _FRUIT_WIDTH, _FRUIT_HEIGHT );
       
        _g.setColor ( Color.green );
        _g.drawLine ( _lineStartX, _lineStartY, _lineEndX0, _lineEndY0 );
        _g.drawLine ( _lineStartX, _lineStartY, _lineEndX1, _lineEndY1 );
        _g.drawLine ( _lineStartX, _lineStartY, _lineEndX2, _lineEndY2 );
    }

    public int getX ( ) { return _x; }
    public int getY ( ) { return _y; }
    public void setEaten ( final int eatenFlag_ ) { _eaten = eatenFlag_; }
    public int getEaten ( ) { return _eaten; }
}


class Money
{

    public final static int _WIDTH = 20;
    public final static int _HEIGHT = 20;

    private final static int _RECTANGLE_WIDTH = _WIDTH / 2;
    private final static int _RECTANGLE_HEIGHT = _HEIGHT / 2;

    private int _x;
    private int _y;
    private int _eaten;
    private final Graphics _g;
    private final Color _moneyColor;
    private final Color _backgroundColor;

    private final Color _availableColors [ ] = { 
            Color.gray, Color.red, Color.green, Color.white, 
            Color.orange, Color.blue, Color.black, Color.cyan, 
            Color.darkGray, Color.magenta, Color.pink, Color.yellow, 
            Color.lightGray };

    public Money ( final int x_, final int y_, Graphics g_, 
                   final Color backgroundColor_ )
    {
        _x = x_;
        _y = y_;
        _eaten = 0;
        _g = g_;
        _moneyColor = getRandomColor ( );
        _backgroundColor = backgroundColor_;
    }
    
    public void draw ( )
    {
        if ( _eaten == 1 )
        {
            return;
        }
        _g.setColor ( _moneyColor );
        _g.fillOval ( _x, _y, _WIDTH, _HEIGHT );
        _g.setColor ( _backgroundColor );
        _g.fillRect ( _x + _WIDTH / 2 - _RECTANGLE_WIDTH / 2, 
                      _y + _HEIGHT / 2 - _RECTANGLE_HEIGHT / 2, 
                      _RECTANGLE_WIDTH, _RECTANGLE_HEIGHT );
    }

    private Color getRandomColor ( )
    {
        int i = ( int ) ( Math.random ( ) * _availableColors.length );
        return _availableColors [ i ];
    }

    public int getX ( ) { return _x; }
    public int getY ( ) { return _y; }
    public void setEaten ( final int eatenFlag_ ) { _eaten = eatenFlag_; }
    public int getEaten ( ) { return _eaten; }
}

class Wall
{
    public final static int _WIDTH = 400;    //_WIDTH means length.
    public final static int _THICKNESS = 10;     //vertical length is _WIDTH
    public final static int _ORIGIN_X = 1;
    public final static int _ORIGIN_Y = 1;
    public final static int _GAP = 40;


    public final static int _X1 = _ORIGIN_X + _THICKNESS + _GAP;
    public final static int _X2 = _ORIGIN_X + _WIDTH / 2 - _GAP / 2;
    public final static int _X3 = _X2 + _GAP; 
    public final static int _X4 = _ORIGIN_X + _WIDTH - _THICKNESS - _GAP - 
                                  _THICKNESS;
    public final static int _X5 = _X1 + _THICKNESS + _GAP;
    public final static int _X6 = _X5 + _THICKNESS + _GAP;
    public final static int _X7 = _X4 - _GAP - _THICKNESS;

    public final static int _Y1 = _ORIGIN_Y + _THICKNESS + _GAP;
    public final static int _Y2 = _ORIGIN_Y + _WIDTH /2 - _GAP / 2;
    public final static int _Y3 = _Y2 + _GAP;
    public final static int _Y4 = _ORIGIN_Y + _WIDTH - _THICKNESS - _GAP -
                                  _THICKNESS;
    public final static int _Y5 = _Y4 - _GAP - _THICKNESS; 

    private final Graphics _g;

    public Wall ( final Graphics g_ )
    {
        _g = g_;
        
    }
    
    public void draw ( )
    {   //pararell walls 
        _g.setColor ( Color.pink );
        //draw outer walls        
        final int horizontalWidth0 = _WIDTH / 2 - _GAP / 2;
        _g.fillRect ( _ORIGIN_X, _ORIGIN_Y, horizontalWidth0, _THICKNESS );
        _g.fillRect ( _X3, _ORIGIN_Y, horizontalWidth0, _THICKNESS ); 

        _g.fillRect ( _ORIGIN_X, _ORIGIN_Y + _WIDTH - _THICKNESS, 
                      horizontalWidth0, _THICKNESS);
        _g.fillRect ( _X3, _ORIGIN_Y + _WIDTH - _THICKNESS, horizontalWidth0,
                      _THICKNESS );

        final int verticalWidth0 = _WIDTH / 2 - _GAP / 2;
        _g.fillRect ( _ORIGIN_X, _ORIGIN_Y, _THICKNESS, verticalWidth0 );
        _g.fillRect ( _ORIGIN_X, _Y3, _THICKNESS, verticalWidth0 );

        _g.fillRect ( _ORIGIN_X + _WIDTH - _THICKNESS, _ORIGIN_Y, 
                      _THICKNESS, verticalWidth0 );
        _g.fillRect ( _ORIGIN_X + _WIDTH - _THICKNESS, _Y3, _THICKNESS, 
                      verticalWidth0 ); 

        //draw inner walls
        final int horizontalWidth1 = _WIDTH - 2 * _THICKNESS - 2 * _GAP; 
        _g.fillRect ( _X1, _Y1, horizontalWidth1, _THICKNESS );
        _g.fillRect ( _X1, _Y4, horizontalWidth1, _THICKNESS );

        final int verticalWidth1 = _Y4 - _Y1 - _THICKNESS - 2 * _GAP;  
        _g.fillRect ( _X1, _Y1 + _THICKNESS + _GAP, _THICKNESS, 
                      verticalWidth1 ); 
        _g.fillRect ( _X4, _Y1 + _THICKNESS + _GAP, _THICKNESS, 
                      verticalWidth1 );

        //draw inner most walls
        _g.fillRect ( _X5, _Y1 + _THICKNESS + _GAP, _THICKNESS, 
                      verticalWidth1 );
        _g.fillRect ( _X7, _Y1 + _THICKNESS + _GAP, _THICKNESS, 
                      verticalWidth1 );

        final int horizontalWidth2 = _X7 - _X5 - _THICKNESS - _GAP - _GAP;
        _g.fillRect ( _X6, _Y1 + _THICKNESS + _GAP, horizontalWidth2, 
                      _THICKNESS ); 
        _g.fillRect ( _X6, _Y5, horizontalWidth2, _THICKNESS );
    }
     
    public int getCherryX (final int cherrySequenceNumber_ )
    {
        if ( cherrySequenceNumber_ == 1 )
        {
            return _X6;
        } 
        if ( cherrySequenceNumber_ == 2 )
        {
            return ( _X7 - _GAP - Cherry._WIDTH );
        } 
        return 0;
    }
}


class ButtonHandler implements ActionListener
{
    private final Pacman _pacman;
    private final JButton _leftButton;
    private final JButton _rightButton;
    private final JButton _upButton;
    private final JButton _downButton;
    private final JButton _startAnimationButton;

    public ButtonHandler ( final Pacman pacman_, final JButton leftButton_, 
                           final JButton rightButton_, 
                           final JButton upButton_, final JButton downButton_,
                           final JButton startAnimationButton_
                         )
    {
        _pacman = pacman_;
        _leftButton = leftButton_; 
        _rightButton = rightButton_;
        _upButton = upButton_;
        _downButton = downButton_;
        _startAnimationButton = startAnimationButton_ ; 
    }
    public void actionPerformed ( ActionEvent e )
    {
        if ( e.getSource ( ) == _leftButton )
        {
            if ( _pacman.getMovingDirection ( ) != Pacman._LEFT )
            {
                _pacman.setMovingDirection ( Pacman._LEFT );
                _pacman.clearImage ( );
                _pacman.draw ( );
                _pacman.move ( );
            }
            else
            {
                _pacman.move ( );
            }
        }
        else if ( e.getSource ( ) == _rightButton )
        {
            if ( _pacman.getMovingDirection ( ) != Pacman._RIGHT )
            {
                _pacman.setMovingDirection ( Pacman._RIGHT );
                _pacman.clearImage ( );
                _pacman.draw ( );
                _pacman.move ( );
            }
            else
            {
                _pacman.move ( );
            }
        }
        else if ( e.getSource ( ) == _upButton )
        {
            if ( _pacman.getMovingDirection ( ) != Pacman._UP )
            {
                _pacman.setMovingDirection ( Pacman._UP );
                _pacman.clearImage ( );
                _pacman.draw ( );
                _pacman.move ( );
            }
            else
            {
                _pacman.move ( );
            }
        }
        else if ( e.getSource ( ) == _downButton )
        {
            if ( _pacman.getMovingDirection ( ) != Pacman._DOWN )
            {
                _pacman.setMovingDirection ( Pacman._DOWN );
                _pacman.clearImage ( );
                _pacman.draw ( );
                _pacman.move ( );
            }
            else
            {
                _pacman.move ( );
            }
        }
        else
        {  
        }
        _pacman.detectCollisionWithMoneys ( );
        _pacman.detectCollisionWithCherries ( );
        _pacman.detectCollisionWithGhosts ( );
        _pacman.detectGameOver ( _startAnimationButton );
    }
}

class StartAnimationHandler implements ActionListener
{
    private final Pacman _pacman;
    private final Ghost  _ghosts [ ];
    private final Cherry _cherries [ ];
    private final Money  _moneys [ ];
    private final JButton _startAnimationButton;
    private final Timer _timer;
    

    public StartAnimationHandler ( final Pacman pacman_, 
               final Ghost ghosts_ [ ], final Cherry cherries_ [ ],  
               final Money moneys_ [ ],  final JButton startAnimationButton_ ) 
    {
        _pacman = pacman_;
        _ghosts = ghosts_;
        _cherries = cherries_;
        _moneys = moneys_;
        _startAnimationButton = startAnimationButton_;
        TimerHandler timerHandler = new TimerHandler ( _pacman, _ghosts, 
                               _cherries, _moneys, _startAnimationButton );

        _timer = new Timer ( TimerHandler._ANIMATION_DELAY_TIME, timerHandler );
    }
    
    public void actionPerformed ( ActionEvent e )
    {
        _startAnimationButton.setEnabled ( false );
        _timer.start ( );
    }
    public Timer getTimer ( ) { return _timer; }
}

class TimerHandler implements ActionListener
{
    public static final int _ANIMATION_DELAY_TIME = 1000/2;
    private final Pacman _pacman;
    private final Ghost  _ghosts [ ];
    private final Cherry _cherries [ ];
    private final Money  _moneys [ ];
    private final JButton _startAnimationButton;

    public TimerHandler ( final Pacman pacman_, final Ghost ghosts_ [ ], 
                          final Cherry cherries_ [ ], final Money moneys_ [ ],
                          final JButton startAnimationButton_ )
    {
        _pacman = pacman_;
        _ghosts = ghosts_;
        _cherries = cherries_;
        _moneys = moneys_;
        _startAnimationButton = startAnimationButton_;
    }
    public void actionPerformed ( ActionEvent e )
    {
        _pacman.mouthCloseOpen ( );
        for ( int i = 0; i < _ghosts.length; i++ )
        {
            if ( _ghosts [ i ].getEaten ( ) == 1 )
            {
                continue;
            }
            _ghosts [ i ].clearImage ( );
            _ghosts [ i ].draw ( );
            _ghosts [ i ].move ( );
            _ghosts [ i ].detectCollisionWithPacman ( );
            _pacman.detectGameOver ( _startAnimationButton );
        }
        for ( int i = 0; i < _cherries.length; i++ )
        {
            _cherries [ i ].draw ( );
        }
        for ( int i = 0; i < _moneys.length; i++ )
        {
            _moneys [ i ].draw ( );
        }
    }
}




class WindowHandler extends WindowAdapter 
{
    public WindowHandler ( )
    {
        super ( );
    }
    public void windowClosing ( WindowEvent e )
    {
        System.exit ( 0 );
    }
}

class Sleeper
{
    public static void sleep ( final int miliSeconds_ )
    {
        try 
        {
            Thread.sleep ( miliSeconds_ );
        }
        catch ( InterruptedException e )
        {
            System.err.println ( e.toString ( ) ); 
        }
    }
}
