/// <summary>
        /// Perform robust collision detection using BoundingSpheres to determine if two models are intersecting.
        /// </summary>
        /// <param name="modelA"></param>
        /// <param name="modelB"></param>
        /// <returns></returns>
        public bool collidesWith(BasicModel modelA, BasicModel modelB)
        {
            // get the position of each model
            Matrix modelATranslation = modelA.GetWorld();
            Matrix modelBTranslation = modelB.GetWorld();

            // check each bounding sphere of each model
            foreach (ModelMesh modelMeshesA in modelA.model.Meshes) {
                foreach (ModelMesh modelMeshesB in modelB.model.Meshes) {
                    // update bounding spheres of each models mesh
                    BoundingSphere BSA = modelMeshesA.BoundingSphere.Transform(modelATranslation);
                    BoundingSphere BSB = modelMeshesB.BoundingSphere.Transform(modelBTranslation);

                    // check if any the mesh bounding sphere intersects with another
                    if (BSA.Intersects(BSB)) {
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Knockback the enemy when it hits a target
        /// </summary>
        /// <param name=""></param>
        internal void KnockBackFrom(BasicModel model)
        {
            // if knockback from player, then throw into the air, if another car, then small knockback
            if (model.GetType() == typeof(Player)) {
                this.knockbackModelPosition = model;

                if (this.knockbackModelPosition != null) {
                    HandleJump(true);
                }

                // make sure Y always at identity Y
                if (translation.Translation.Y < Matrix.Identity.Translation.Y) {
                    translation.Translation = new Vector3(translation.Translation.X, Matrix.Identity.Translation.Y, translation.Translation.Z);
                }

            } else {
                translation.Translation += Vector3.Normalize(translation.Translation - model.translation.Translation) * 50f;
            }
        }
        /// <summary>
        /// Knockback the player when it hits a target. Use similar as flee mechanic
        /// TODO: Convert this to do a jump mechanic at same time.
        /// </summary>
        /// <param name=""></param>
        public void KnockBackFrom(BasicModel model)
        {
            float knockbackDistance = 5f;

            if (model.GetType() == typeof(Enemy)) {
                knockbackDistance = 50f;
            } else if (model.GetType() == typeof(MonsterTruck)) {
                knockbackDistance = 100f;
            } else {
                // knockback further if boosting
                if (isBoosting()) {
                    knockbackDistance = 12f;
                }
            }

            translation.Translation += Vector3.Normalize(translation.Translation - model.translation.Translation) * knockbackDistance;

            // some reason Y can go below 0, make sure Y always at identity Y
            if (translation.Translation.Y < Matrix.Identity.Translation.Y) {
                translation.Translation = new Vector3(translation.Translation.X, Matrix.Identity.Translation.Y, translation.Translation.Z);
            }
        }
        /// <summary>
        /// When debug mode show path from enemy to target of a*
        /// </summary>
        private void HandleDebugMode(BasicModel model)
        {
            if (((Game1)game).debugMode) {
                // Add token indicating where enemy seeking. One indicator arrow for each astar coordinate
                // Also, first verify that the token doesnt already exist at the location

                foreach (Vector2 seekLocation in ((Enemy)model).aStarPaths) {

                    if (!SeekTokenAlreadyExists(new Vector3(seekLocation.X, 0, seekLocation.Y))) {
                        SeekIndicator seekIndicator = new SeekIndicator(
                            Game.Content.Load<Model>(@"Models/ArrowPointer/ArrowPointerModel"),
                            new Vector3(seekLocation.X, 0, seekLocation.Y));
                        models.Add(seekIndicator);
                        seekIndicatorCount++;
                    }

                }
            } else {
                // clear all astar debug tokens if not debug mode
                ClearSeekTokens();
            }
        }
 /// <summary>
 /// Only perform collision detection on valid models. Ie, no point performing collision detection between ground (mapTile) and car models
 /// </summary>
 /// <param name="model"></param>
 /// <returns></returns>
 private bool validModelType(BasicModel model)
 {
     return (model.GetType() != typeof(MapTile));
 }
        /// <summary>
        /// Only perform collision checks on valid models
        /// -
        /// - use spacial partition using quadrant to check if models are in same quadrant before more robust and computationallly expensive collision detection routine is performed
        /// - ignore collisions for ground (MapTile) models, there are many, so this vastly improves performance.
        /// - 
        /// </summary>
        /// <returns></returns>
        private bool isValidCheck(BasicModel modelA, BasicModel modelB)
        {
            if (modelA.GetType() == typeof(SeekIndicator) && modelB.GetType() != typeof(Enemy)) {
                return false;
            } else if (modelB.GetType() == typeof(SeekIndicator) && modelA.GetType() != typeof(Enemy)) {
                return false;
            }

            if (modelA.uniqueId != modelB.uniqueId && (validModelType(modelA)) && (validModelType(modelB))) {
                if (isMatchingMapQuadrant(modelA, modelB)) {
                    return true;
                }
            }

            return false;
        }
 /// <summary>
 /// Check if 2 models are in matching map quadrants. 
 /// </summary>
 /// <returns></returns>
 private bool isMatchingMapQuadrant(BasicModel modelA, BasicModel modelB)
 {
     return getMapQuadrant(modelA) == getMapQuadrant(modelB);
 }
        /// <summary>
        /// Depending on the pair of models colliding, handle appropriately
        /// </summary>
        /// <param name="modelA"></param>
        /// <param name="modelB"></param>
        private void HandleCollision(BasicModel modelA, BasicModel modelB)
        {
            Type typeModelA = modelA.GetType();
            Type typeModelB = modelB.GetType();

            if (typeModelA == typeof(Enemy) && typeModelB == typeof(Pickup)) {
                ///
                /// COLLISION - ENEMY AND PICKUP ITEM
                ///
                score.enemyBatteryCount++;
                ((Enemy)modelA).FullHealth();

                modelB.currentDrawState = BasicModel.drawState.remove;

            } else if (typeModelA == typeof(Enemy) && typeModelB == typeof(SeekIndicator)) {
                ///
                /// COLLISION - ENEMY AND SEEK INDICATOR DEBUG ITEM
                ///
                modelB.currentDrawState = BasicModel.drawState.remove;

            } else if (typeModelA == typeof(Player) && typeModelB == typeof(Pickup)) {
                ///
                /// COLLISION - PLAYER AND PICKUP ITEM
                ///
                audioManager.charge.Play();
                if (((Player)modelA).energy + 25 >= Player.MAX_ENERGY) {
                    ((Player)modelA).energy = Player.MAX_ENERGY;
                } else {
                    ((Player)modelA).energy += 25;
                }
                modelB.currentDrawState = BasicModel.drawState.remove;
                score.playerBatteryCount++;

            } else if (typeModelA == typeof(Enemy) && typeModelB == typeof(Enemy)) {
                ///
                /// COLLISION - ENEMY AI WITH ENEMY AI
                ///

                Enemy enemyModelA = (Enemy)modelA;
                Enemy enemyModelB = (Enemy)modelB;

                // knockback each enemy
                enemyModelA.KnockBackFrom(enemyModelB);
                enemyModelB.KnockBackFrom(enemyModelA);

                // reduce limited amount of health
                enemyModelA.health -= 5;
                enemyModelB.health -= 5;

                if (audioManager.crash.State != SoundState.Playing) {
                    audioManager.crash.Play();
                }

                if (enemyModelA.health <= 0) {
                    audioManager.enemyDeath.Play();
                    enemyModelA.currentDrawState = BasicModel.drawState.remove;
                    score.enemiesDefeatedCount++;
                }
                if (enemyModelB.health <= 0) {
                    audioManager.enemyDeath.Play();
                    enemyModelB.currentDrawState = BasicModel.drawState.remove;
                    score.enemiesDefeatedCount++;
                }

            } else if (typeModelA == typeof(MonsterTruck) && typeModelB == typeof(Player)) {
                ///
                /// COLLISION - MONSTER TRUCK (BOSS) AND PLAYER
                ///
                MonsterTruck monsterTruckModel = (MonsterTruck)modelA;
                Player playerModel = (Player)modelB;

                //monsterTruckModel.KnockBackFrom(playerModel); // knockback player from enemy

                if (playerModel.isBoosting()) {
                    playerModel.KnockBackFrom(monsterTruckModel); // knockback player from monstertruck

                    playerModel.energy -= playerModel.energy / 2;
                    monsterTruckModel.health -= 35;
                    playerModel.health -= 5;
                } else {
                    playerModel.KnockBackFrom(monsterTruckModel); // knockback player from monstertruck

                    playerModel.health -= 5;
                    if (audioManager.crash.State != SoundState.Playing) {
                        audioManager.crash.Play();
                    }
                    if (playerModel.energy <= 0) {
                        playerModel.health -= 20;
                    }
                }

                if (monsterTruckModel.health <= 0) {
                    audioManager.enemyDeath.Play();
                    monsterTruckModel.currentDrawState = BasicModel.drawState.remove;
                    score.monsterTruckDefeatedCount++;

                    if (modelManager.monsterTruckCount > 0) {
                        modelManager.monsterTruckCount--;
                    }

                }

                if (playerModel.health <= 0) {
                    audioManager.enemyDeath.Play();
                    audioManager.accelerate.Stop();
                    audioManager.boost.Stop();
                    audioManager.charge.Stop();
                    audioManager.idleLoop.Stop();
                    playerModel.health = 0;
                    playerModel.currentDrawState = BasicModel.drawState.remove;
                    splashScreen.SetData("TODO - enemy wins", Game1.GameState.END); // change splash state
                    this.game.ChangeGameState(Game1.GameState.END, 1); // change game state
                }

            } else if (typeModelA == typeof(Enemy) && typeModelB == typeof(Player)) {
                ///
                /// COLLISION - ENEMY AND PLAYER
                ///

                Enemy enemyModel = (Enemy)modelA;
                Player playerModel = (Player)modelB;

                enemyModel.KnockBackFrom(playerModel); // knockback enemy from player

                if (playerModel.isBoosting()) {
                    playerModel.energy -= playerModel.energy / 5;
                    enemyModel.health = enemyModel.health - enemyModel.health;
                    playerModel.health -= 5;
                } else {
                    playerModel.KnockBackFrom(enemyModel); // knockback player from enemy
                    enemyModel.health -= 35;

                    playerModel.health -= 5;
                    if (audioManager.crash.State != SoundState.Playing) {
                        audioManager.crash.Play();
                    }
                    if (playerModel.energy <= 0) {
                        playerModel.health -= 20;
                    }
                }

                if (enemyModel.health <= 0) {
                    audioManager.enemyDeath.Play();
                    enemyModel.currentDrawState = BasicModel.drawState.remove;
                    score.enemiesDefeatedCount++;
                }

                if (playerModel.health <= 0) {
                    audioManager.enemyDeath.Play();
                    audioManager.accelerate.Stop();
                    audioManager.boost.Stop();
                    audioManager.charge.Stop();
                    audioManager.idleLoop.Stop();
                    playerModel.health = 0;
                    playerModel.currentDrawState = BasicModel.drawState.remove;
                    splashScreen.SetData("TODO - enemy wins", Game1.GameState.END); // change splash state
                    this.game.ChangeGameState(Game1.GameState.END, 1); // change game state
                }

            } else if (typeModelA == typeof(Player) && typeModelB == typeof(Tire)) {
                ///
                /// COLLISION - PLAYER AND TIRE OBSTACLE
                ///
                Player playerModel = (Player)modelA;
                Tire tireModel = (Tire)modelB;

                playerModel.KnockBackFrom(tireModel); // knockback player from obstacle

            } else if (typeModelA == typeof(Player) && typeModelB == typeof(Barrier)) {
                ///
                /// COLLISION - PLAYER AND BARRIER OBSTACLE
                ///
                Player playerModel = (Player)modelA;
                Barrier barrierModel = (Barrier)modelB;

                playerModel.KnockBackFrom(barrierModel); // knockback player from obstacle

            }
        }
        /// <summary>
        /// Get the map quadrant based on the provided models position vector.
        /// This is used for spacial partitioning
        /// 
        /// Map is devided into 4 quadrants:
        /// 
        ///         |
        ///      A  |  B
        ///   ------+------
        ///      D  |  C
        ///         |
        /// 
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private Quadrant getMapQuadrant(BasicModel model)
        {
            float modelX = model.translation.Translation.X;
            float modelZ = model.translation.Translation.Z;

            // detect quadrant 1
            if (modelX < 0 && modelZ >= 0) {
                return Quadrant.A;
            } else if (modelX >= 0 && modelZ >= 0) {
                return Quadrant.B;
            } else if (modelX >= 0 && modelZ < 0) {
                return Quadrant.C;
            } else if (modelX < 0 && modelZ < 0) {
                return Quadrant.D;
            }

            return 0;
        }