package com.middlemind.Odroid_Tutorial2_PongClone;

import net.middlemind.MmgGameApiJava.MmgCore.GamePanel.GameStates;
import net.middlemind.MmgGameApiJava.MmgCore.GamePanel.GameType;
import net.middlemind.MmgGameApiJava.MmgCore.GameSettings;
import net.middlemind.MmgGameApiJava.MmgCore.GenericEventMessage;
import net.middlemind.MmgGameApiJava.MmgCore.Helper;
import net.middlemind.MmgGameApiJava.MmgCore.Screen;
import java.util.Random;
import net.middlemind.MmgGameApiJava.MmgBase.Mmg9Slice;
import net.middlemind.MmgGameApiJava.MmgBase.MmgBmp;
import net.middlemind.MmgGameApiJava.MmgBase.MmgBmpScaler;
import net.middlemind.MmgGameApiJava.MmgBase.MmgColor;
import net.middlemind.MmgGameApiJava.MmgBase.MmgFont;
import net.middlemind.MmgGameApiJava.MmgBase.MmgFontData;
import net.middlemind.MmgGameApiJava.MmgBase.MmgPen;
import net.middlemind.MmgGameApiJava.MmgBase.MmgScreenData;
import net.middlemind.MmgGameApiJava.MmgBase.MmgHelper;
import net.middlemind.MmgGameApiJava.MmgBase.MmgSound;
import net.middlemind.MmgGameApiJava.MmgBase.MmgVector2;

/**
 * A game screen object, ScreenGame, that extends the MmgGameScreen base
 * class. This class is for testing new UI widgets, etc.
 *
 * @author Victor G. Brusca
 * 03/15/2020
 */
public class ScreenGame extends Screen {
            
    /**
     * An enumeration that tracks which number is visible during game start countdown and in-game countdown.
     */
    private enum NumberState {
        NONE,
        NUMBER_1,
        NUMBER_2,
        NUMBER_3
    };
    
    /**
     * An enumeration that tracks which state this Screen is currently rendering.
     * Allows the Screen to support different views like in-game, countdown, game over, game start, etc.
     */
    private enum State {
        NONE,
        SHOW_GAME,
        SHOW_COUNT_DOWN,
        SHOW_COUNT_DOWN_IN_GAME,        
        SHOW_GAME_OVER,
        SHOW_GAME_EXIT
    };
        
    private GameType gameType = GameType.GAME_ONE_PLAYER;
    private State statePrev = State.NONE;
    private State state = State.NONE;
    
    private NumberState numberState = NumberState.NONE;
    private long timeNumberMs = 0L;
    private long timeNumberDisplayMs = 1000;
    private long timeTmpMs = 0L;
    
    private MmgSound bounceNorm;
    private MmgSound bounceSuper;
    
    private MmgBmp bground;
    private MmgBmp paddleLeft;
    private MmgBmp paddleRight;
    private MmgVector2 paddle1Pos;
    private MmgVector2 paddle2Pos;    
    
    private MmgBmp ball;
    private MmgVector2 ballPos;
    private int ballMovePerFrameMin = GetSpeedPerFrame(150);
    private int ballMovePerFrameMax = GetSpeedPerFrame(425);
    private int ballDirX = 1;
    private int ballDirY = 1;
    private int ballMovePerFrameX = 0;
    private int ballMovePerFrameY = 0;
    
    private MmgBmp number1;
    private MmgBmp number2;
    private MmgBmp number3;    
    
    private int scoreGameWin = 7;
    private MmgFont scoreLeft;
    private int scorePlayerRight = 0;
    private MmgFont scoreRight;
    private int scorePlayerLeft = 0;
    private MmgFont exit;
    private MmgBmp exitBground;
        
    private int paddle1MovePerFrame = GetSpeedPerFrame(400);
    private boolean paddle1MoveUp = false;
    private boolean paddle1MoveDown = false;
    
    private int aiMaxSpeed = 425;
    private int paddle2MovePerFrame = GetSpeedPerFrame(400);
    private boolean paddle2MoveUp = false;
    private boolean paddle2MoveDown = false;        
    
    private Random rand;
    private int ballNewX;
    private int ballNewY;
    private boolean bounced = false;
    private MmgVector2 screenPos;
    
    private int lastX;
    private int lastY;
    private boolean mousePos = true;
    
    private MmgBmp bgroundPopupSrc;
    private Mmg9Slice bgroundPopup;
    private MmgFont txtOk;
    private MmgFont txtCancel;
    
    private MmgFont txtGoal;
    private MmgFont txtDirecP1;
    private MmgFont txtDirecP2;    
 
    private MmgFont txtGameOver1;
    private MmgFont txtGameOver2;
    
    private int padding = MmgHelper.ScaleValue(4);
    private int popupTotalWidth = MmgHelper.ScaleValue(300);
    private int popupTotalHeight = MmgHelper.ScaleValue(120); 
    
    private boolean infiniteBounce = false;
    
    
    /**
     * Constructor, sets the game state associated with this screen, and sets
     * the owner GamePanel instance.
     *
     * @param State The game state of this game screen.
     * @param Owner The owner of this game screen.
     */
    @SuppressWarnings("LeakingThisInConstructor")
    public ScreenGame(GameStates State, GamePanel Owner) {
        super(State, Owner);
        pause = false;
        ready = false;
        owner = Owner;
    }

    /**
     * Loads all the resources needed to display this game screen and support all Screen states.
     */
    @Override
    @SuppressWarnings("UnusedAssignment")
    public void LoadResources() {
        pause = true;        
        rand = new Random((int)System.currentTimeMillis());
        
        classConfig = MmgHelper.ReadClassConfigFile(GameSettings.CLASS_CONFIG_DIR + GameSettings.NAME + "/screen_game.txt");        
        
        if(gameType == GameType.GAME_TWO_PLAYER) {
            paddle2MovePerFrame = GetSpeedPerFrame(aiMaxSpeed);            
        }
        
        SetHeight(MmgScreenData.GetGameHeight());
        SetWidth(MmgScreenData.GetGameWidth());
        SetPosition(MmgScreenData.GetPosition());
        screenPos = GetPosition();
        
        String key = "";
        double scale = 1.0;
        MmgBmp lval = null;        
        MmgSound sval = null;        
        String file = "";
        int tmp = 0;
        int tmpW = 0;
        int tmpH = 0;
        
        //Load game board config
        key = "bmpGameBoard";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "game_board.png";
        }        
        
        lval = MmgHelper.GetBasicCachedBmp(file);        
        bground = lval;
        if (bground != null) {
            key = "gameBoardScale";
            if(classConfig.containsKey(key)) {
                scale = classConfig.get(key).number.doubleValue();
                if(scale != 1.0) {
                    bground = MmgBmpScaler.ScaleMmgBmp(bground, scale, false);
                } else {
                    bground = MmgBmpScaler.ScaleMmgBmpToGameScreen(bground, false);
                }
            }
            
            MmgHelper.CenterHorAndVert(bground);
                
            key = "gameBoardPosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                bground.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "gameBoardPosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bground.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "gameBoardOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bground.GetPosition().SetY(bground.GetY() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "gameBoardOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bground.GetPosition().SetX(bground.GetX() + MmgHelper.ScaleValue(tmp));
            }            
            
            AddObj(bground);
        }
        
        
        //Load bounce normal sound
        key = "soundBounceNorm";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "jump1.wav";
        }          
        
        sval = MmgHelper.GetBasicCachedSound(file);
        bounceNorm = sval;
        
        
        //Load bounce super sound        
        key = "soundBounceSuper";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "jump2.wav";
        }          
        
        sval = MmgHelper.GetBasicCachedSound(file);
        bounceSuper = sval;
        

        //Load pong ball config
        key = "bmpPongBall";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "pong_ball.png";
        }        
        
        lval = MmgHelper.GetBasicCachedBmp(file);        
        ball = lval;
        if (ball != null) {
            key = "pongBallScale";
            if(classConfig.containsKey(key)) {
                scale = classConfig.get(key).number.doubleValue();
                if(scale != 1.0) {
                    ball = MmgBmpScaler.ScaleMmgBmp(ball, scale, false);
                }
            }
            
            MmgHelper.CenterHorAndVert(ball);
                
            key = "pongBallPosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                ball.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "pongBallPosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                ball.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "pongBallOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                ball.GetPosition().SetY(bground.GetY() + MmgHelper.ScaleValue(tmp));
            }
            
            key = "pongBallOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                ball.GetPosition().SetX(bground.GetX() + MmgHelper.ScaleValue(tmp));
            }            
            
            ball.SetIsVisible(false);
            ballPos = ball.GetPosition();            
            AddObj(ball);
        }        
        
        
        //Load paddle left config
        key = "paddleLeftWidth";
        if(classConfig.containsKey(key)) {
            tmpW = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpW = MmgHelper.ScaleValue(20);
        }
        
        key = "paddleLeftHeight";
        if(classConfig.containsKey(key)) {
            tmpH = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpH = tmpH = (h / 6);
        }
        
        paddleLeft = MmgHelper.CreateFilledBmp(tmpW, tmpH, MmgColor.GetWhite());
        paddleLeft.SetPosition(MmgHelper.ScaleValue(20), screenPos.GetY() + (h - paddleLeft.GetHeight())/2);
        paddle1Pos = paddleLeft.GetPosition();
        AddObj(paddleLeft);
        
        
        //Load paddle right config
        key = "paddleRightWidth";
        if(classConfig.containsKey(key)) {
            tmpW = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpW = MmgHelper.ScaleValue(20);
        }
        
        key = "paddleRightHeight";
        if(classConfig.containsKey(key)) {
            tmpH = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpH = tmpH = (h / 6);
        }
        
        paddleRight = MmgHelper.CreateFilledBmp(tmpW, tmpH, MmgColor.GetLightGray());
        paddleRight.SetPosition((w - paddleRight.GetWidth() - MmgHelper.ScaleValue(20)), screenPos.GetY() + (h - paddleLeft.GetHeight())/2);                
        paddle2Pos = paddleRight.GetPosition();
        AddObj(paddleRight);
            
        
        //Load score left config
        scoreLeft = MmgFontData.CreateDefaultBoldMmgFontLg();
        scoreLeft.SetText("00");
        scoreLeft.SetMmgColor(MmgColor.GetRed());
        scoreLeft.SetPosition(MmgHelper.ScaleValue(10) + paddleLeft.GetX() + paddleLeft.GetWidth(), screenPos.GetY() + scoreLeft.GetHeight() + MmgHelper.ScaleValue(5));

        key = "scoreLeftPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            scoreLeft.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreLeftPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreLeft.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreLeftOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreLeft.GetPosition().SetY(scoreLeft.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreLeftOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreLeft.GetPosition().SetX(scoreLeft.GetX() + MmgHelper.ScaleValue(tmp));
        }
        AddObj(scoreLeft);
        
        
        //Load score right config        
        scoreRight = MmgFontData.CreateDefaultBoldMmgFontLg();
        scoreRight.SetText("00");
        scoreRight.SetMmgColor(MmgColor.GetRed());
        scoreRight.SetPosition(w - scoreRight.GetWidth() - MmgHelper.ScaleValue(10) - paddleLeft.GetX() - paddleRight.GetWidth(), screenPos.GetY() + scoreRight.GetHeight() + MmgHelper.ScaleValue(5));

        key = "scoreRightPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            scoreRight.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreRightPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreRight.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreRightOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreRight.GetPosition().SetY(scoreRight.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "scoreRightOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            scoreRight.GetPosition().SetX(scoreRight.GetX() + MmgHelper.ScaleValue(tmp));
        }        
        AddObj(scoreRight);        
           
        
        //Load string exit config
        key = "strExitText";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Press B to Exit";
        }        
        exit = MmgFontData.CreateDefaultBoldMmgFontLg();
        exit.SetText(file);
        exit.SetMmgColor(MmgColor.GetRed());
        exit.SetPosition((w - exit.GetWidth())/2, screenPos.GetY() + exit.GetHeight() + MmgHelper.ScaleValue(5));
        
        key = "exitTextPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            exit.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exit.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exit.GetPosition().SetY(exit.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exit.GetPosition().SetX(exit.GetX() + MmgHelper.ScaleValue(tmp));
        }         

        
        //Load exit text background config
        key = "exitTextBgroundWidth";
        if(classConfig.containsKey(key)) {
            tmpW = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpW = exit.GetWidth() + (padding * 2);
        }
        
        key = "exitTextBgroundHeight";
        if(classConfig.containsKey(key)) {
            tmpH = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
        } else {
            tmpH = exit.GetHeight() + (padding * 2);
        }        
        
        exitBground = MmgHelper.CreateFilledBmp(tmpW, tmpH, MmgColor.GetBlack());
        exitBground.SetPosition(exit.GetX() - padding, exit.GetY() - exit.GetHeight());
        
        key = "exitTextBgroundPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            exitBground.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextBgroundPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exitBground.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextBgroundOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exitBground.GetPosition().SetY(exitBground.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "exitTextBgroundOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            exitBground.GetPosition().SetX(exitBground.GetX() + MmgHelper.ScaleValue(tmp));
        } 
        AddObj(exitBground);        
        AddObj(exit);
          
        
        //Load number one config
        key = "bmpNumberOne";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "num_1_lrg.png";
        }
        
        lval = MmgHelper.GetBasicCachedBmp(file);
        number1 = lval;
        if(number1 != null) {
            key = "numberOneScale";
            if(classConfig.containsKey(key)) {
                scale = classConfig.get(key).number.doubleValue();
                if(scale != 1.0) {
                    number1 = MmgBmpScaler.ScaleMmgBmp(number1, scale, false);
                }
            }
            
            MmgHelper.CenterHorAndVert(number1);

            key = "numberOnePosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                number1.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberOnePosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number1.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberOneOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number1.GetPosition().SetY(number1.GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberOneOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number1.GetPosition().SetX(number1.GetX() + MmgHelper.ScaleValue(tmp));
            }
            
            number1.SetIsVisible(false);
            AddObj(number1);
        }
        
        
        //Load number two config
        key = "bmpNumberTwo";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "num_2_lrg.png";
        } 
        
        lval = MmgHelper.GetBasicCachedBmp(file);
        number2 = lval;
        if(number2 != null) {
            key = "numberTwoScale";
            if(classConfig.containsKey(key)) {
                scale = classConfig.get(key).number.doubleValue();
                if(scale != 1.0) {
                    number2 = MmgBmpScaler.ScaleMmgBmp(number2, scale, false);
                }
            }            
            
            MmgHelper.CenterHorAndVert(number2);
        
            key = "numberTwoPosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                number2.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberTwoPosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number2.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberTwoOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number2.GetPosition().SetY(number2.GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberTwoOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number2.GetPosition().SetX(number2.GetX() + MmgHelper.ScaleValue(tmp));
            }            
            
            number2.SetIsVisible(false);
            AddObj(number2);
        }
        
        
        //Load number three config
        key = "bmpNumberThree";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "num_3_lrg.png";
        }
        
        lval = MmgHelper.GetBasicCachedBmp(file);
        number3 = lval;        
        if(number3 != null) {
            key = "numberThreeScale";
            if(classConfig.containsKey(key)) {
                scale = classConfig.get(key).number.doubleValue();
                if(scale != 1.0) {
                    number3 = MmgBmpScaler.ScaleMmgBmp(number3, scale, false);
                }
            }
            
            MmgHelper.CenterHorAndVert(number3);
            
            key = "numberThreePosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                number3.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberThreePosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number3.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberThreeOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number3.GetPosition().SetY(number3.GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "numberThreeOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                number3.GetPosition().SetX(number3.GetX() + MmgHelper.ScaleValue(tmp));
            }            
            
            number3.SetIsVisible(false);
            AddObj(number3);
        }
        
        
        //Load string game win config
        key = "strGoalText";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Goal: First player to " + scoreGameWin + " points wins.";
        }         
        txtGoal = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtGoal.SetText(file);
        txtGoal.SetMmgColor(MmgColor.GetWhite());        
        MmgHelper.CenterHorAndVert(txtGoal);
        txtGoal.SetY(number1.GetY() - txtGoal.GetHeight() + MmgHelper.ScaleValue(5));
        
        key = "goalTextPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtGoal.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "goalTextPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGoal.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "goalTextOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGoal.GetPosition().SetY(txtGoal.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "goalTextOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGoal.GetPosition().SetX(txtGoal.GetX() + MmgHelper.ScaleValue(tmp));
        }        
        
        txtGoal.SetIsVisible(false);
        AddObj(txtGoal);        
        
        
        //Load string player 1 direction config
        key = "strPlayer1DirectionText";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Player 1: Right paddle, use UP, DOWN keys to move paddle.";
        }        
        txtDirecP1 = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtDirecP1.SetText(file);
        txtDirecP1.SetMmgColor(MmgColor.GetWhite());  
        MmgHelper.CenterHorAndVert(txtDirecP1);
        txtDirecP1.SetY(number1.GetY() + number1.GetHeight() + txtDirecP1.GetHeight() + MmgHelper.ScaleValue(10));
        
        key = "player1DirectionTextPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtDirecP1.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "player1DirectionTextPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP1.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "player1DirectionTextOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP1.GetPosition().SetY(txtDirecP1.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "player1DirectionTextOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP1.GetPosition().SetX(txtDirecP1.GetX() + MmgHelper.ScaleValue(tmp));
        }        
        
        txtDirecP1.SetIsVisible(false);
        AddObj(txtDirecP1);
        
        
        //Load string player 2 direction config
        key = "strPlayer2DirectionText";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Player 2: Left paddle, use MOUSE or S, X keys to move paddle.";
        }        
        txtDirecP2 = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtDirecP2.SetText(file);
        txtDirecP2.SetMmgColor(MmgColor.GetWhite());
        MmgHelper.CenterHorAndVert(txtDirecP2);
        txtDirecP2.SetY(txtDirecP1.GetY() + txtDirecP1.GetHeight() + MmgHelper.ScaleValue(10));

        key = "player2DirectionTextPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtDirecP2.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "player2DirectionTextPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP2.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "player2DirectionTextOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP2.GetPosition().SetY(txtDirecP2.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "player2DirectionTextOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtDirecP2.GetPosition().SetX(txtDirecP2.GetX() + MmgHelper.ScaleValue(tmp));
        }

        txtDirecP2.SetIsVisible(false);        
        AddObj(txtDirecP2);
        
        
        //Load game over player 1 config
        key = "strTextGameOver1";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Player 1 has won the game. Press A or B to exit.";
        }        
        txtGameOver1 = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtGameOver1.SetText(file);
        txtGameOver1.SetMmgColor(MmgColor.GetWhite());  
        MmgHelper.CenterHorAndVert(txtGameOver1);
        
        key = "textGameOver1PosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtGameOver1.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver1PosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver1.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver1OffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver1.GetPosition().SetY(txtGameOver1.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver1OffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver1.GetPosition().SetX(txtGameOver1.GetX() + MmgHelper.ScaleValue(tmp));
        }
        
        txtGameOver1.SetIsVisible(false);
        AddObj(txtGameOver1);
        
        
        //Load game over player 2 config
        key = "strTextGameOver2";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Player 2 has won the game. Press A or B to exit.";
        }        
        txtGameOver2 = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtGameOver2.SetText(file);
        txtGameOver2.SetMmgColor(MmgColor.GetWhite());  
        MmgHelper.CenterHorAndVert(txtGameOver2);
        
        key = "textGameOver2PosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtGameOver2.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver2PosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver2.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver2OffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver2.GetPosition().SetY(txtGameOver2.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "textGameOver2OffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtGameOver2.GetPosition().SetX(txtGameOver2.GetX() + MmgHelper.ScaleValue(tmp));
        }        
        
        txtGameOver2.SetIsVisible(false);
        AddObj(txtGameOver2);        
        
        
        //Load popup base
        key = "bmpPopupWindowBase";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "popup_window_base.png";
        }        
        lval = MmgHelper.GetBasicCachedBmp(file);
        bgroundPopupSrc = lval;        
        if(bgroundPopupSrc != null) {
            bgroundPopup = new Mmg9Slice(16, bgroundPopupSrc, popupTotalWidth, popupTotalHeight);
            bgroundPopup.SetPosition(MmgVector2.GetOriginVec());
            
            key = "popupWindowBaseWidth";
            if(classConfig.containsKey(key)) {
                tmpW = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
            } else {
                tmpW = popupTotalWidth;
            }
            bgroundPopup.SetWidth(tmpW);
            
            key = "popupWindowBaseHeight";
            if(classConfig.containsKey(key)) {
                tmpH = MmgHelper.ScaleValue(classConfig.get(key).number.intValue());
            } else {
                tmpH = popupTotalHeight;
            }                        
            bgroundPopup.SetHeight(tmpH);
            
            MmgHelper.CenterHorAndVert(bgroundPopup);
            
            key = "popupWindowBasePosY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();
                bgroundPopup.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "popupWindowBasePosX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bgroundPopup.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
            }

            key = "popupWindowBaseOffsetY";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bgroundPopup.GetPosition().SetY(bgroundPopup.GetY() + MmgHelper.ScaleValue(tmp));
            }

            key = "popupWindowBaseOffsetX";
            if(classConfig.containsKey(key)) {
                tmp = classConfig.get(key).number.intValue();                
                bgroundPopup.GetPosition().SetX(bgroundPopup.GetX() + MmgHelper.ScaleValue(tmp));
            }
            
            AddObj(bgroundPopup);
            bgroundPopup.SetIsVisible(false);
        }
        
        
        //Load popup window text exit
        key = "strPopupWindowTextExit";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Exit Game (A)";
        }
        
        txtOk = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtOk.SetText(file);
        txtOk.SetMmgColor(MmgColor.GetWhite());        
        MmgHelper.CenterHorAndVert(txtOk);
        txtOk.SetY(bgroundPopup.GetY() + txtOk.GetHeight() + MmgHelper.ScaleValue(20));

        key = "popupWindowTextExitPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtOk.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextExitPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtOk.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextExitOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtOk.GetPosition().SetY(txtOk.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextExitOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtOk.GetPosition().SetX(txtOk.GetX() + MmgHelper.ScaleValue(tmp));
        }

        txtOk.SetIsVisible(false);
        AddObj(txtOk);
        
        
        //Load popup window text cancel
        key = "strPopupWindowTextCancel";
        if(classConfig.containsKey(key)) {
            file = classConfig.get(key).string;
        } else {
            file = "Cancel Exit (B)";
        }
        
        txtCancel = MmgFontData.CreateDefaultBoldMmgFontLg();
        txtCancel.SetText(file);
        txtCancel.SetMmgColor(MmgColor.GetWhite()); 
        MmgHelper.CenterHorAndVert(txtCancel);
        txtCancel.SetY(txtOk.GetY() + txtOk.GetHeight() + MmgHelper.ScaleValue(20));
        
        key = "popupWindowTextCancelPosY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();
            txtCancel.GetPosition().SetY(GetPosition().GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextCancelPosX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtCancel.GetPosition().SetX(GetPosition().GetX() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextCancelOffsetY";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtCancel.GetPosition().SetY(txtCancel.GetY() + MmgHelper.ScaleValue(tmp));
        }

        key = "popupWindowTextCancelOffsetX";
        if(classConfig.containsKey(key)) {
            tmp = classConfig.get(key).number.intValue();                
            txtCancel.GetPosition().SetX(txtCancel.GetX() + MmgHelper.ScaleValue(tmp));
        }        
        
        txtCancel.SetIsVisible(false);
        AddObj(txtCancel);        
        
        //Set score text
        scorePlayerRight = 0;
        scorePlayerLeft = 0;        
        SetScoreRightText(scorePlayerRight);        
        SetScoreLeftText(scorePlayerLeft);
        
        SetState(State.SHOW_COUNT_DOWN);        
        ready = true;
        pause = false;
    }

    public void SetGameType(GameType gt) {
        gameType = gt;
    }
    
    public GameType GetGameType() {
        return gameType;
    }
    
    /**
     * Converts the given speed to a uniform speed per frame so that the game movement will
     * be the same even if the game runs at different frame rates.
     * 
     * @param speed     The target speed to convert to a speed per frame.
     * 
     * @return          A converted speed that represents the speed per frame of the given input speed. 
     */
    private static int GetSpeedPerFrame(int speed) {
        return (int)(speed/(MmgPongClone.FPS - 4));        
    }
    
    /**
     * A method to handle mouse press events from the MainFrame class.
     * 
     * @param v     The 2D coordinates of the mouse press event.
     * 
     * @return      A boolean indicating if this Screen has handled the mouse press event. 
     */
    @Override
    public boolean ProcessMousePress(MmgVector2 v) {
        return ProcessMousePress(v.GetX(), v.GetY());
    }

    /**
     * A method to handle mouse press events from the MainFrame class.
     * 
     * @param x     The X coordinate of the mouse press event.
     * @param y     The Y coordinate of the mouse press event.
     * 
     * @return      A boolean indicating if this Screen has handled the mouse press event. 
     */
    @Override
    public boolean ProcessMousePress(int x, int y) {
        return true;
    }

    /**
     * A method to handle mouse release events from the MainFrame class.
     * 
     * @param v     The 2D coordinates of the mouse release event.
     * 
     * @return      A boolean indicating if this Screen has handled the mouse release event. 
     */    
    @Override
    public boolean ProcessMouseRelease(MmgVector2 v) {
        return ProcessMousePress(v.GetX(), v.GetY());
    }

    /**
     * A method to handle mouse press events from the MainFrame class.
     * 
     * @param x     The X coordinate of the mouse press event.
     * @param y     The Y coordinate of the mouse press event.
     * 
     * @return      A boolean indicating if this Screen has handled the mouse press event. 
     */    
    @Override
    public boolean ProcessMouseRelease(int x, int y) {
        return true;
    }

    /**
     * A method to handle A button click events from the MainFrame class.
     * 
     * @return      A boolean indicating if this Screen has handled the A click event.
     */
    @Override
    public boolean ProcessAClick(int src) {
        if(state == State.SHOW_GAME_EXIT) {
            owner.SwitchGameState(GameStates.MAIN_MENU);
            return true;
            
        } else if(state == State.SHOW_GAME_OVER) {
            owner.SwitchGameState(GameStates.MAIN_MENU);
            return true;
            
        }
        
        return false;
    }
    
    /**
     * A method to handle B button click events from the MainFrame class.
     * 
     * @return      A boolean indicating if this Screen has handled the B click event.
     */    
    @Override
    public boolean ProcessBClick(int src) {
        if(state == State.SHOW_GAME_OVER) {
            owner.SwitchGameState(GameStates.MAIN_MENU);
            return true;
            
        } else {
            if(state != State.SHOW_GAME_EXIT) {        
                SetState(State.SHOW_GAME_EXIT);
                return true;
            
            } else {
                SetState(statePrev);
                return true;
            }
        }
    }
    
    /**
     * A method to handle debug click events from the MainFrame class, the D key on the keyboard.
     * You can use this method to turn on different debugging helpers.
     */
    @Override
    public void ProcessDebugClick() {
        if(scoreGameWin > 1) {
            Helper.wr("Setting game win score to 1.");
            scoreGameWin = 1;
        } else if(scoreGameWin == 1 && infiniteBounce == false) {
            Helper.wr("Setting infinite bounce to true.");
            infiniteBounce = true;
        } else if(scoreGameWin == 1 && infiniteBounce == true) {
            Helper.wr("Setting game win score to 7 and infinite bounce to false.");
            scoreGameWin = 7;
            infiniteBounce = false;
        }
    }

    /**
     * A method to handle key press events from the MainFrame class.
     * 
     * @param c     The character of the key that was pressed on the keyboard.
     * 
     * @return      A boolean indicating if the key press event was handled by this Screen.
     */
    @Override
    public boolean ProcessKeyPress(char c) {
        if(state == State.SHOW_GAME && pause == false) {
            if(gameType == GameType.GAME_TWO_PLAYER) {
                if(c == 'x' || c == 'X') {            
                    paddle1MoveUp = false;
                    paddle1MoveDown = true;
                    return true;

                } else if(c == 's' || c == 'S') {            
                    paddle1MoveUp = true;
                    paddle1MoveDown = false;            
                    return true;

                }
            }
        }
        
        return false;        
    }
    
    /**
     * A method to handle key release events from the MainFrame class.
     * 
     * @param c     The character of the key that was released on the keyboard.
     * 
     * @return      A boolean indicating if the key release event was handled by this Screen.
     */    
    @Override
    public boolean ProcessKeyRelease(char c) {
        if(state == State.SHOW_GAME && pause == false) {        
            if(gameType == GameType.GAME_TWO_PLAYER) {        
                if(c == 'x' || c == 'X') {
                    paddle1MoveDown = false;
                    return true;

                } else if(c == 's' || c == 'S') {
                    paddle1MoveUp = false;
                    return true;

                }
            }
        }
        
        return false;       
    }
    
    /**
     * A method to handle key click events from the MainFrame class.
     * 
     * @param c     The character of the key that was clicked on the keyboard.
     * 
     * @return      A boolean indicating if the key click event was handled by this Screen.
     */    
    @Override
    public boolean ProcessKeyClick(char c) {
        return false;
    }    
    
    /**
     * A method to handle dpad press events from the MainFrame class.
     * 
     * @param dir   The dpad code, UP, DOWN, LEFT, RIGHT of the direction that was pressed on the keyboard.
     * 
     * @return      A boolean indicating if the dpad press was handled by this Screen.
     */
    @Override
    public boolean ProcessDpadPress(int dir) {
        if(state == State.SHOW_GAME && pause == false) {
            if(dir == GameSettings.DOWN_KEYBOARD) {
                paddle2MoveUp = false;
                paddle2MoveDown = true;
                return true;

            } else if(dir == GameSettings.UP_KEYBOARD) {            
                paddle2MoveUp = true;
                paddle2MoveDown = false;            
                return true;

            } else if(this.gameType == GameType.GAME_TWO_PLAYER) {
                if(dir == GameSettings.DOWN_GAMEPAD_1 || dir == GameSettings.DOWN_GPIO) {
                    if(dir == GameSettings.DOWN_GPIO) {
                        Helper.wr(("GPIO Gamepad Down Button Event"));
                    }
                    
                    paddle1MoveUp = false;
                    paddle1MoveDown = true;
                    return true;
                    
                } else if(dir == GameSettings.UP_GAMEPAD_1 || dir == GameSettings.UP_GPIO) {
                    if(dir == GameSettings.UP_GPIO) {
                        Helper.wr(("GPIO Gamepad Up Button Event"));
                    }
                    
                    paddle1MoveUp = true;
                    paddle1MoveDown = false;            
                    return true;
                
                }
            }
        }
        
        return false;
    }

    /**
     * A method to handle dpad release events from the MainFrame class.
     * 
     * @param dir   The dpad code, UP, DOWN, LEFT, RIGHT of the direction that was released on the keyboard.
     * 
     * @return      A boolean indicating if the dpad release was handled by this Screen.
     */    
    @Override
    public boolean ProcessDpadRelease(int dir) {
        if(state == State.SHOW_GAME && pause == false) {        
            if(dir == GameSettings.DOWN_KEYBOARD) {
                paddle2MoveDown = false;
                return true;

            } else if(dir == GameSettings.UP_KEYBOARD) {
                paddle2MoveUp = false;
                return true;

            } else if(this.gameType == GameType.GAME_TWO_PLAYER) {
                if(dir == GameSettings.DOWN_GAMEPAD_1 || dir == GameSettings.DOWN_GPIO) {
                    paddle1MoveDown = false;
                    return true;
                    
                } else if(dir == GameSettings.UP_GAMEPAD_1 || dir == GameSettings.UP_GPIO) {
                    paddle1MoveUp = false;
                    return true;
                
                }                
            }
        }
        
        return false;
    }
    
    /**
     * A method to handle dpad click events from the MainFrame class.
     * 
     * @param dir       The dpad code, UP, DOWN, LEFT, RIGHT of the direction that was clicked on the keyboard.
     * 
     * @return          A boolean indicating if the dpad click was handled by this Screen.
     */
    @Override
    public boolean ProcessDpadClick(int dir) {
        return true;
    }
    
    /**
     * A method to handle mouse click events from the MainFrame class.
     * 
     * @param v         The 2D mouse coordinates of the click event.
     * 
     * @return          A boolean indicating if the mouse click event was handled by this Screen.
     */
    @Override
    public boolean ProcessMouseClick(MmgVector2 v) {
        return ProcessMouseClick(v.GetX(), v.GetY());
    }

    /**
     * A method to handle the mouse click events from the MainFrame class.
     * 
     * @param x         The X coordinate of the mouse click event.
     * 
     * @param y         The Y coordinate of the mouse click event.
     * 
     * @return          A boolean indicating if the mouse click event was handled by this Screen.
     */
    @Override
    public boolean ProcessMouseClick(int x, int y) {        
        return true;
    }    
    
    /**
     * Processes mouse movement for player 2's input, the left hand paddle.
     * 
     * @param x     The X position of the mouse with the Screen's offset taken into account.
     * @param y     The Y position of the mouse with the Screen's offset taken into account.
     * 
     * @return      Returns a boolean indicating if the event was consumed by this game Screen. 
     */
    @Override
    public boolean ProcessMouseMove(int x, int y) {
        if(state == State.SHOW_GAME && pause == false) {        
            if(gameType == GameType.GAME_TWO_PLAYER) {
                if(y >= screenPos.GetY() && y <= (screenPos.GetY() + GetHeight() - paddleLeft.GetHeight())) {
                    lastX = x;
                    lastY = y;
                    mousePos = true;
                    return true;
                }
            }            
        }
        
        mousePos = false;
        return false;
    }    
    
    /**
     * Resets certain aspects of the UI that are related to the actual game.
     * Some aspects of the UI are left visible during the in-game count down.
     */
    private void ResetGame() {
        ball.SetIsVisible(true);                
        MmgHelper.CenterHorAndVert(ball);
        ballPos = ball.GetPosition();
        ballMovePerFrameX = ballMovePerFrameMin;
        ballMovePerFrameY = ballMovePerFrameMin;

        paddleLeft.SetIsVisible(true);                
        MmgHelper.CenterVert(paddleLeft);
        paddle1Pos = paddleLeft.GetPosition();
        paddle1MoveDown = false;
        paddle1MoveUp = false;

        paddleRight.SetIsVisible(true);                
        MmgHelper.CenterVert(paddleRight);
        paddle2Pos = paddleRight.GetPosition();
        paddle2MoveDown = false;
        paddle2MoveUp = false;                

        lastX = 0;
        lastY = 0;      
        mousePos = false;        
    }
    
    /**
     * Sets the Screen's current state. The state is used to prepare what MmgObj's are visible for the given state.
     * 
     * @param in        The desired State to set the Screen to.
     */
    private void SetState(State in) {        
        //clean up prev state
        switch(statePrev) {
            case NONE:
                break;
                
            case SHOW_GAME:
                break;
                
            case SHOW_COUNT_DOWN:
                break;
                
            case SHOW_COUNT_DOWN_IN_GAME:
                break;                
                
            case SHOW_GAME_OVER:
                break;
                
            case SHOW_GAME_EXIT:
                break;                
        }
        
        statePrev = state;
        state = in;
        
        switch(state) {
            case NONE:
                ResetGame();
                
                ball.SetIsVisible(false);
                paddleLeft.SetIsVisible(false);
                paddleRight.SetIsVisible(false);                
                bground.SetIsVisible(false);
                number1.SetIsVisible(false);
                number2.SetIsVisible(false);
                number3.SetIsVisible(false);
                txtGoal.SetIsVisible(false);                
                txtDirecP1.SetIsVisible(false);
                txtDirecP2.SetIsVisible(false);                
                scoreLeft.SetIsVisible(false);
                scoreRight.SetIsVisible(false);
                exit.SetIsVisible(false);                
                bgroundPopup.SetIsVisible(false);
                txtOk.SetIsVisible(false);
                txtCancel.SetIsVisible(false);
                
                scorePlayerRight = 0;
                scorePlayerLeft = 0;        
                SetScoreRightText(scorePlayerRight);        
                SetScoreLeftText(scorePlayerLeft);  
                
                pause = false;                
                dirty = false;                
                break;
                
            case SHOW_GAME_OVER:
                ball.SetIsVisible(true);               
                paddleLeft.SetIsVisible(true);
                paddleRight.SetIsVisible(true);
                bground.SetIsVisible(false);
                number1.SetIsVisible(false);
                number2.SetIsVisible(false);
                number3.SetIsVisible(false);                
                txtGoal.SetIsVisible(false);                
                txtDirecP1.SetIsVisible(false);
                txtDirecP2.SetIsVisible(false);                
                scoreLeft.SetIsVisible(true);
                scoreRight.SetIsVisible(true);
                exit.SetIsVisible(true);                
                bgroundPopup.SetIsVisible(false);
                txtOk.SetIsVisible(false);
                txtCancel.SetIsVisible(false);                
                
                if(scorePlayerRight == scoreGameWin) {
                    txtGameOver1.SetIsVisible(true);
                    txtGameOver2.SetIsVisible(false);
                    
                } else if(scorePlayerLeft == scoreGameWin) {
                    txtGameOver1.SetIsVisible(false);
                    txtGameOver2.SetIsVisible(true);
                    
                }
                numberState = NumberState.NONE;
                
                pause = false;
                dirty = true;                
                break;
                
            case SHOW_GAME:
                if(statePrev != State.SHOW_GAME_EXIT) {
                    timeNumberMs = System.currentTimeMillis();
                    ResetGame();
                }
                
                ball.SetIsVisible(true);                
                paddleLeft.SetIsVisible(true);
                paddleRight.SetIsVisible(true);
                bground.SetIsVisible(true);
                number1.SetIsVisible(false);
                number2.SetIsVisible(false);
                number3.SetIsVisible(false);
                txtGoal.SetIsVisible(false);                
                txtDirecP1.SetIsVisible(false);
                txtDirecP2.SetIsVisible(false);                
                scoreLeft.SetIsVisible(true);
                scoreRight.SetIsVisible(true);
                exit.SetIsVisible(true);                
                bgroundPopup.SetIsVisible(false);
                txtOk.SetIsVisible(false);
                txtCancel.SetIsVisible(false);
                txtGameOver1.SetIsVisible(false);
                txtGameOver2.SetIsVisible(false);
                
                if(statePrev != State.SHOW_GAME_EXIT) {                
                    if(rand.nextInt(11) % 2 == 0) {
                        ballDirX = 1;
                    } else {
                        ballDirX = -1;
                    }
                    ballMovePerFrameX = ballMovePerFrameMin;

                    if(rand.nextInt(11) % 2 == 0) {
                        ballDirY = 1;
                    } else {
                        ballDirY = -1;
                    }         
                    ballMovePerFrameY = ballMovePerFrameMin;                 
                }
                
                pause = false;
                dirty = true;
                break;
                
            case SHOW_COUNT_DOWN_IN_GAME:                
                ball.SetIsVisible(true);               
                paddleLeft.SetIsVisible(true);
                paddleRight.SetIsVisible(true);
                bground.SetIsVisible(true);
                
                if(statePrev != State.SHOW_GAME_EXIT) {
                    number1.SetIsVisible(false);
                    number2.SetIsVisible(false);
                    number3.SetIsVisible(false);
                }
                
                txtGoal.SetIsVisible(false);                
                txtDirecP1.SetIsVisible(false);
                txtDirecP2.SetIsVisible(false);                
                scoreLeft.SetIsVisible(true);
                scoreRight.SetIsVisible(true);
                exit.SetIsVisible(true);                
                bgroundPopup.SetIsVisible(false);
                txtOk.SetIsVisible(false);
                txtCancel.SetIsVisible(false);
                txtGameOver1.SetIsVisible(false);
                txtGameOver2.SetIsVisible(false);                
                
                if(statePrev != State.SHOW_GAME_EXIT) {
                    numberState = NumberState.NONE;
                } else {
                    //reset this number count down
                    timeNumberMs = System.currentTimeMillis();
                }
                
                pause = false;
                dirty = true;                
                break;
                
            case SHOW_COUNT_DOWN:
                ball.SetIsVisible(false);
                paddleLeft.SetIsVisible(false);
                paddleRight.SetIsVisible(false);
                bground.SetIsVisible(false);
                
                if(statePrev != State.SHOW_GAME_EXIT) {                
                    number1.SetIsVisible(false);
                    number2.SetIsVisible(false);
                    number3.SetIsVisible(false);
                    txtGoal.SetIsVisible(false);                
                    txtDirecP1.SetIsVisible(false);
                    txtDirecP2.SetIsVisible(false);
                }
                
                scoreLeft.SetIsVisible(true);
                scoreRight.SetIsVisible(true);
                exit.SetIsVisible(true);
                bgroundPopup.SetIsVisible(false);
                txtOk.SetIsVisible(false);
                txtCancel.SetIsVisible(false);
                txtGameOver1.SetIsVisible(false);
                txtGameOver2.SetIsVisible(false);
                
                if(statePrev != State.SHOW_GAME_EXIT) {                
                    numberState = NumberState.NONE;
                } else {
                    //reset this number count down
                    timeNumberMs = System.currentTimeMillis();
                }
                
                pause = false;
                dirty = true;
                break;
                                
            case SHOW_GAME_EXIT:
                bgroundPopup.SetIsVisible(true);
                txtOk.SetIsVisible(true);
                txtCancel.SetIsVisible(true);
                dirty = true;                
                break;                
        }        
    }
    
    /**
     * Updates player2's score, left hand paddle.
     * 
     * @param score     The score to set for player two.
     */    
    private void SetScoreLeftText(int score) {
        String tmp = score + "";
        if(tmp.length() != 2) {
            tmp = "0" + tmp;
        }
        scoreLeft.SetText(tmp);
    }
    
    /**
     * Updates player1's score, right hand paddle.
     * 
     * @param score     The score to set for player one.
     */
    private void SetScoreRightText(int score) {
        String tmp = score + "";
        if(tmp.length() != 2) {
            tmp = "0" + tmp;
        }
        scoreRight.SetText(tmp);
    }    
    
    /**
     * The DrawScreen method gets called by the MmgUpdate method if the Screen is not paused and is responsible for drawing the current screen state.
     */
    @Override
    public void DrawScreen() {
        //run each game frame
        pause = true;
        
        switch(state) {
            case NONE:
                break;
                
            case SHOW_GAME_EXIT:

                break;
                
            case SHOW_COUNT_DOWN_IN_GAME:
            case SHOW_COUNT_DOWN:
                switch(numberState) {
                    case NONE:
                        timeNumberMs = System.currentTimeMillis();
                        numberState = NumberState.NUMBER_3;
                        number1.SetIsVisible(false);
                        number2.SetIsVisible(false);
                        number3.SetIsVisible(true);
                        if(state == State.SHOW_COUNT_DOWN) {
                            txtGoal.SetIsVisible(true);                            
                            txtDirecP1.SetIsVisible(true);
                            if(gameType == GameType.GAME_TWO_PLAYER) {
                                txtDirecP2.SetIsVisible(true);
                            } else {
                                txtDirecP2.SetIsVisible(false);
                            }
                        } else {
                            txtGoal.SetIsVisible(false);
                            txtDirecP1.SetIsVisible(false);
                            txtDirecP2.SetIsVisible(false);
                        }
                        break;
                        
                    case NUMBER_1:
                        timeTmpMs = System.currentTimeMillis();
                        if(timeTmpMs - timeNumberMs >= timeNumberDisplayMs) {
                            timeNumberMs = timeTmpMs;
                            numberState = NumberState.NONE;
                            number1.SetIsVisible(false);
                            number2.SetIsVisible(false);
                            number3.SetIsVisible(false);
                            txtDirecP1.SetIsVisible(false);
                            txtDirecP2.SetIsVisible(false);
                            SetState(State.SHOW_GAME);
                            bounceNorm.Play();                            
                        }
                        break;
                        
                    case NUMBER_2:
                        timeTmpMs = System.currentTimeMillis();
                        if(timeTmpMs - timeNumberMs >= timeNumberDisplayMs) {
                            timeNumberMs = timeTmpMs;
                            numberState = NumberState.NUMBER_1;                            
                            number1.SetIsVisible(true);
                            number2.SetIsVisible(false);
                            number3.SetIsVisible(false);
                            txtDirecP1.SetIsVisible(true);
                            if(state == State.SHOW_COUNT_DOWN) {
                                txtGoal.SetIsVisible(true);
                                if(gameType == GameType.GAME_TWO_PLAYER) {
                                    txtDirecP2.SetIsVisible(true);
                                } else {
                                    txtDirecP2.SetIsVisible(false);
                                }
                            } else {
                                txtGoal.SetIsVisible(false);
                                txtDirecP1.SetIsVisible(false);
                                txtDirecP2.SetIsVisible(false);
                            }
                            bounceNorm.Play();                            
                        }
                        break;
                        
                    case NUMBER_3:
                        timeTmpMs = System.currentTimeMillis();
                        if(timeTmpMs - timeNumberMs >= timeNumberDisplayMs) {
                            timeNumberMs = timeTmpMs;
                            numberState = NumberState.NUMBER_2;
                            number1.SetIsVisible(false);
                            number2.SetIsVisible(true);
                            number3.SetIsVisible(false);
                            if(state == State.SHOW_COUNT_DOWN) {
                                txtGoal.SetIsVisible(true);
                                if(gameType == GameType.GAME_TWO_PLAYER) {
                                    txtDirecP2.SetIsVisible(true);
                                } else {
                                    txtDirecP2.SetIsVisible(false);
                                }
                            } else {
                                txtGoal.SetIsVisible(false);
                                txtDirecP1.SetIsVisible(false);
                                txtDirecP2.SetIsVisible(false);
                            }                                
                            bounceNorm.Play();
                        }
                        break;
                        
                }
                break;
                
            case SHOW_GAME:
                if(gameType == GameType.GAME_TWO_PLAYER) {
                    if(mousePos) {
                        paddle1Pos.SetY(lastY);
                    }

                    if(paddle1MoveUp) {
                        if(paddle1Pos.GetY() - paddle1MovePerFrame < screenPos.GetY()) {
                            paddle1Pos.SetY(screenPos.GetY());                
                        } else {
                            paddle1Pos.SetY(paddle1Pos.GetY() - paddle1MovePerFrame);
                        }

                    } else if(paddle1MoveDown) {
                        if(paddle1Pos.GetY() + paddleLeft.GetHeight() + paddle1MovePerFrame > screenPos.GetY() + GetHeight()) {
                            paddle1Pos.SetY(screenPos.GetY() + GetHeight() - paddleLeft.GetHeight());
                        } else {
                           paddle1Pos.SetY(paddle1Pos.GetY() + paddle1MovePerFrame);                            
                        }

                    }
                    
                } else {                    
                    //AI
                    if(ballPos.GetY() + ball.GetHeight()/2 < paddleLeft.GetY()) {
                        if(paddle1Pos.GetY() - paddle1MovePerFrame < screenPos.GetY()) {
                            paddle1Pos.SetY(screenPos.GetY());                
                        } else {
                            paddle1Pos.SetY(paddle1Pos.GetY() - paddle1MovePerFrame);
                        }
                        
                    }else if(ballPos.GetY() + ball.GetHeight()/2 > paddleLeft.GetY() + paddleLeft.GetHeight()) {
                        if(paddle1Pos.GetY() + paddleLeft.GetHeight() + paddle1MovePerFrame > screenPos.GetY() + GetHeight()) {
                            paddle1Pos.SetY(screenPos.GetY() + GetHeight() - paddleLeft.GetHeight());
                        } else {
                           paddle1Pos.SetY(paddle1Pos.GetY() + paddle1MovePerFrame);                            
                        }
                        
                    }
                    
                }
                
                if(paddle2MoveUp) {
                    if(paddle2Pos.GetY() - paddle2MovePerFrame < screenPos.GetY()) {
                        paddle2Pos.SetY(screenPos.GetY());                
                    } else {
                        paddle2Pos.SetY(paddle2Pos.GetY() - paddle2MovePerFrame);
                    }

                } else if(paddle2MoveDown) {
                    if(paddle2Pos.GetY() + paddleRight.GetHeight() + paddle2MovePerFrame > screenPos.GetY() + GetHeight()) {
                        paddle2Pos.SetY(screenPos.GetY() + GetHeight() - paddleRight.GetHeight());
                    } else {
                       paddle2Pos.SetY(paddle2Pos.GetY() + paddle2MovePerFrame);                            
                    }

                }
                
                //calculate where the ball will be
                ballNewX = ballPos.GetX() + (ballMovePerFrameX * ballDirX);
                ballNewY = ballPos.GetY() + (ballMovePerFrameY * ballDirY);

                //board collision
                if(ballNewY < screenPos.GetY()) {
                    //top
                    ballDirY = 1;
                    ballNewX = (ballPos.GetX() + (ballMovePerFrameX * ballDirX));
                    ballNewY = ((screenPos.GetY()));
                    
                } else if(ballNewY + ball.GetHeight() > screenPos.GetY() + GetHeight()) {
                    //bottom
                    ballDirY = -1;
                    ballNewX = (ballPos.GetX() + (ballMovePerFrameX * ballDirX));
                    ballNewY = ((screenPos.GetY() + GetHeight() - ball.GetHeight()));
                    
                }else if(ballNewX < screenPos.GetX()) {
                    //left
                    ballDirX = 1;
                    ballNewX = ((screenPos.GetX()));
                    ballNewY = (ballPos.GetY() + (ballMovePerFrameY * ballDirY));
                    scorePlayerRight += 1;
                    SetScoreRightText(scorePlayerRight);
                    
                    if(infiniteBounce == false) {
                        if(scorePlayerRight == scoreGameWin) {
                            SetState(State.SHOW_GAME_OVER);                        
                        } else {
                            SetState(State.SHOW_COUNT_DOWN_IN_GAME);
                        }
                    }
                    
                } else if(ballNewX + ball.GetWidth() > screenPos.GetX() + GetWidth()) {
                    //right
                    ballDirX = -1;
                    ballNewX = ((screenPos.GetX() + GetWidth() - ball.GetWidth()));
                    ballNewY = (ballPos.GetY() + (ballMovePerFrameY * ballDirY));                                                                            
                    scorePlayerLeft += 1;
                    SetScoreLeftText(scorePlayerLeft);
                    
                    if(infiniteBounce == false) {
                        if(scorePlayerLeft == scoreGameWin) {
                            SetState(State.SHOW_GAME_OVER);                        
                        } else {
                            SetState(State.SHOW_COUNT_DOWN_IN_GAME);
                        }
                    }
                    
                }
                
                bounced = false;
                //paddle1 collision
                if(ballNewX <= paddle1Pos.GetX() + paddleLeft.GetWidth() && ballDirX == -1) {
                    if(ballNewY + ball.GetHeight()/2 >= paddle1Pos.GetY() + paddleLeft.GetHeight()/3 && ballNewY + ball.GetHeight()/2 <= paddle1Pos.GetY() + ((paddleLeft.GetHeight()/3) * 2)) {
                        //middle
                        ballMovePerFrameX *= 1.5;
                        ballMovePerFrameY = rand.nextInt(ballMovePerFrameMin);
                        ballDirX = 1;
                        if(rand.nextInt() % 2 == 0) {
                            ballDirY = 1;
                        } else {
                            ballDirY = -1;
                        }
                        bounced = true;
                        ballNewX = (paddle1Pos.GetX() + paddleLeft.GetWidth());

                    } else if(ballNewY + ball.GetHeight() >= paddle1Pos.GetY() && ballNewY + ball.GetHeight() <= paddle1Pos.GetY() + paddleLeft.GetHeight()/2) {
                        //top
                        ballMovePerFrameX *= 1.5;
                        ballDirX = 1;
                        ballDirY = -1;

                        if(ballMovePerFrameY == 0) {
                            ballMovePerFrameY = ballMovePerFrameMin + paddle1MovePerFrame + rand.nextInt(ballMovePerFrameMin);
                        } else {                                                
                            ballMovePerFrameY *= 1.5;
                        }
                        bounced = true;
                        ballNewX = (paddle1Pos.GetX() + paddleLeft.GetWidth());

                    } else if(ballNewY >= paddle1Pos.GetY() + paddleLeft.GetHeight()/2 && ballNewY <= paddle1Pos.GetY() + paddleLeft.GetHeight()) {
                        //bottom
                        ballMovePerFrameX *= 1.5;
                        ballDirX = 1;                        
                        ballDirY = 1;

                        if(ballMovePerFrameY == 0) {
                            ballMovePerFrameY = ballMovePerFrameMin + paddle1MovePerFrame + rand.nextInt(ballMovePerFrameMin);
                        } else {                        
                            ballMovePerFrameY *= 1.5;
                        }
                        bounced = true;
                        ballNewX = (paddle1Pos.GetX() + paddleLeft.GetWidth());

                    }
                }

                //paddle2 collision
                if(ballNewX + ball.GetWidth() >= paddle2Pos.GetX() && ballDirX == 1) {
                    if(ballNewY + ball.GetHeight()/2 >= paddle2Pos.GetY() + paddleRight.GetHeight()/3 && ballNewY + ball.GetHeight()/2 <= paddle2Pos.GetY() + ((paddleRight.GetHeight()/3) * 2)) {
                        //middle
                        ballMovePerFrameX *= 1.5;
                        ballMovePerFrameY =  rand.nextInt(ballMovePerFrameMin);
                        ballDirX = -1;
                        if(rand.nextInt() % 2 == 0) {
                            ballDirY = 1;
                        } else {
                            ballDirY = -1;
                        }
                        bounced = true;
                        ballNewX = (paddle2Pos.GetX() - ball.GetWidth());

                    } else if(ballNewY + ball.GetHeight() >= paddle2Pos.GetY() && ballNewY + ball.GetHeight() <= paddle2Pos.GetY() + paddleRight.GetHeight()/2) {
                        //top
                        ballMovePerFrameX *= 1.5;
                        ballDirX = -1;
                        ballDirY = -1;

                        if(ballMovePerFrameY == 0) {
                            ballMovePerFrameY = ballMovePerFrameMin + paddle1MovePerFrame + rand.nextInt(ballMovePerFrameMin);
                        } else {                       
                            ballMovePerFrameY *= 1.5;                            
                        }
                        bounced = true;
                        ballNewX = (paddle2Pos.GetX() - ball.GetWidth());

                    } else if(ballNewY >= paddle2Pos.GetY() + paddleRight.GetHeight()/2 && ballNewY <= paddle2Pos.GetY() + paddleRight.GetHeight()) {
                        //bottom
                        ballMovePerFrameX *= 1.5;
                        ballDirX = -1;
                        ballDirY = 1;

                        if(ballMovePerFrameY == 0) {
                            ballMovePerFrameY = ballMovePerFrameMin + paddle1MovePerFrame + rand.nextInt(ballMovePerFrameMin);
                        } else {                       
                            ballMovePerFrameY *= 1.5;                            
                        }
                        bounced = true;
                        ballNewX = (paddle2Pos.GetX() - ball.GetWidth());

                    }
                }                

                //set limits on the ball's speed
                if(ballMovePerFrameX > ballMovePerFrameMax) {
                    ballMovePerFrameX = ballMovePerFrameMax;
                }

                if(ballMovePerFrameY > ballMovePerFrameMax) {
                    ballMovePerFrameY = ballMovePerFrameMax;
                }

                if(bounced) {
                    if(ballMovePerFrameY == ballMovePerFrameMax || ballMovePerFrameX == ballMovePerFrameMax) {
                        bounceSuper.Play();
                    } else {
                        bounceNorm.Play();
                    }
                }
                                
                ballPos.SetX(ballNewX);
                ballPos.SetY(ballNewY);                
                break;
        }
        
        pause = false;
    }
    
    /**
     * Unloads resources needed to display this game screen.
     */
    public void UnloadResources() {
        pause = true;
        SetBackground(null);
        
        ResetGame();
        state = State.NONE;
        statePrev = State.NONE;        
        
        bounceNorm = null;
        bounceSuper = null;
        bground = null;
        
        paddleLeft = null;
        paddleRight = null;
        paddle1Pos = null;
        paddle2Pos = null;
        
        ball = null;
        ballPos = null;
        number1 = null;
        number2 = null;
        number3 = null;
        
        scoreLeft = null;
        scoreRight = null;
        exit = null;
        exitBground = null;
        
        rand = null;
        screenPos = null;
        bgroundPopupSrc = null;
        bgroundPopup = null;
        
        txtOk = null;
        txtCancel = null;
        txtGoal = null;
        txtDirecP1 = null;
        txtDirecP2 = null;
        txtGameOver1 = null;
        txtGameOver2 = null;
                        
        ClearObjs();
        ready = false;
    }

    /**
     * The main drawing routine.
     *
     * @param p An MmgPen object to use for drawing this game screen.
     */
    @Override
    public void MmgDraw(MmgPen p) {
        if (pause == false && GetIsVisible() == true) {
            super.GetObjects().MmgDraw(p);
        } else {
            //do nothing
        }
    }

    /**
     * Handles generic events sent to this class.
     * 
     * @param obj       A GenericEventMessage class instance.
     */
    @Override
    public void HandleGenericEvent(GenericEventMessage obj) {
        //handle generic event
    }
}
