<commit_before>
/**
 * Copyright 2016 SciFY.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.scify.memori;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.scify.memori.interfaces.*;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.*;

import static javafx.scene.input.KeyCode.*;
import static javafx.scene.input.KeyCode.LEFT;
import static javafx.scene.input.KeyCode.RIGHT;

public class FXRenderingEngine implements RenderingEngine<MemoriGameState>, UI, EventHandler<KeyEvent> {

    /**
     * An Audio Engine object, able to play sounds
     */
    private FXAudioEngine fxAudioEngine;

    /**
     * gridpane holds all cards
     */
    private GridPane gridPane;
    /**
     * computing the current screen height and width
     */
    private double mWidth = Screen.getPrimary().getBounds().getWidth();
    private double mHeight = Screen.getPrimary().getBounds().getHeight();
    /**
     * first draw defines whether the rendering engine will initialize or update the UI components
     */
    private boolean firstDraw = true;
    /**
     * current game scene
     */
    Scene gameScene;
    /**
     * JavFX component to bind the scene with the .fxml and .css file
     */
    protected Parent root;


    public FXRenderingEngine() {
        try {
            root = FXMLLoader.load(getClass().getResource("/fxml/game.fxml"));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        //initialize the audio engine object
        fxAudioEngine = new FXAudioEngine();

        gameScene = new Scene(root, mWidth, mHeight);
    }

    /**
     * List of actions captured by the user interaction. User in the Player-derived methods.
     */
    List<UserAction> pendingUserActions = Collections.synchronizedList(new ArrayList<>());

    @Override
    public void drawGameState(MemoriGameState currentState) {
        System.err.println("drawGameState");
        if (firstDraw) {
            //initialize UI components=
                try {
                    setUpFXComponents();
                    initFXComponents(currentState);
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            firstDraw = false;
        }
        else {
            //update UI components
            updateFXComponents(currentState);
        }
    }

    private long lLastUpdate = -1L;
    protected void updateFXComponents(MemoriGameState currentState) {
        System.err.println("updateFXComponents");
        long lNewTime = new Date().getTime();
        if (lNewTime - lLastUpdate < 100L) {// If no less than 1/10 sec has passed
            Thread.yield(); // TODO: Use it or do nothing?
            return; // Do nothing
        } else {
            lLastUpdate = lNewTime;
            // DEBUG LINES
//            System.err.println("Updating components " +lLastUpdate);
            List<GameEvent> eventsList = currentState.getEventQueue();
            ListIterator<GameEvent> listIterator = eventsList.listIterator();
            while (listIterator.hasNext()) {
                //System.out.println(listIterator.next());
                GameEvent currentGameEvent = listIterator.next();
                String eventType = currentGameEvent.type;
                Point2D coords;
                Card currCard;
                switch (eventType) {
                    case "movement":
                        coords = (Point2D) currentGameEvent.parameters;
                        focusOnTile((int) coords.getX(), (int) coords.getY());
                        movementSound((int) coords.getX(), (int) coords.getY());
                        System.out.println("now at: " + coords.getX() + "," + coords.getY());
                        listIterator.remove();
                        break;
                    case "invalidMovement":
                        Platform.runLater(() -> {
                            fxAudioEngine.playInvalidMovementSound();
                        });
                        listIterator.remove();
                        break;
                    case "empty":
                        Platform.runLater(() -> {
                            fxAudioEngine.playEmptySound();
                        });
                        listIterator.remove();
                        break;
                    case "cardSound":
                        coords = (Point2D) currentGameEvent.parameters;
                        currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                        Platform.runLater(() -> {
                            fxAudioEngine.playCardSound(currCard.getSound(), currentGameEvent.blocking);
                        });
                        listIterator.remove();
                        break;
                    case "flip":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            coords = (Point2D) currentGameEvent.parameters;
                            currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                            Platform.runLater(() -> {
                                currCard.flipUI();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "flipBack":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            coords = (Point2D) currentGameEvent.parameters;
                            currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                            Platform.runLater(() -> {
                                currCard.flipBackUI();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "success":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            Platform.runLater(() -> {
                                fxAudioEngine.playSuccessSound();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "failure":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            Platform.runLater(() -> {
                                fxAudioEngine.playFailureSound();
                            });
                            //TODO: play appropriate sound informing about new turn
                            listIterator.remove();
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    @Override
    public void playGameOver() {
        System.err.println("play game over");
        Platform.runLater(() -> {
            fxAudioEngine.playSuccessSound();
        });
    }



    /**
     * Given the coordinates, marks a Node as visited (green background) by applying a CSS class
     * @param rowIndex the Node x position
     * @param columnIndex the Node y position
     */
    private void focusOnTile(int rowIndex, int columnIndex) {
        System.out.println("point: " + rowIndex + "," + columnIndex);
        //get Node (in our case it's a button)
        Node node = getNodeByRowColumnIndex(rowIndex, columnIndex, gridPane);
        //remove the focused class from every other Node
        ObservableList<Node> nodes = gridPane.getChildren();
        for(Node nd: nodes) {
            nd.getStyleClass().remove("focusedCard");
        }
        //apply the CSS class
        node.getStyleClass().addAll("focusedCard");
        Button btn = (Button) node;
        //DEBUG print button id
        System.out.println(btn.getId());
    }

    /**
     * Computes the sound balance (left-right panning) and rate and plays the movement sound
     * @param rowIndex the Node x position
     * @param columnIndex the Node y position
     */
    private void movementSound(int rowIndex, int columnIndex) {
        System.err.println("movementSound");
        double soundBalance = map(columnIndex, 0.0, (double) MainOptions.NUMBER_OF_ROWS - 0.6, -1.0, 1.0);
        double rate = map(rowIndex, 0.0, (double) MainOptions.NUMBER_OF_COLUMNS - 0.6, 1.0, 1.5);
        fxAudioEngine.playMovementSound(soundBalance, rate);
    }

    private Node getNodeByRowColumnIndex(final int row,final int column, GridPane gridPane) {
        Node result = null;
        ObservableList<Node> childrens = gridPane.getChildren();
        for(Node node : childrens) {
            if(gridPane.getRowIndex(node) == row && gridPane.getColumnIndex(node) == column) {
                result = node;
                break;
            }
        }
        return result;<commit_msg>Changed sound arate for movement user action MEM-54<commit_after>
/**
 * Copyright 2016 SciFY.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.scify.memori;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.scify.memori.interfaces.*;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.*;

import static javafx.scene.input.KeyCode.*;
import static javafx.scene.input.KeyCode.LEFT;
import static javafx.scene.input.KeyCode.RIGHT;

public class FXRenderingEngine implements RenderingEngine<MemoriGameState>, UI, EventHandler<KeyEvent> {

    /**
     * An Audio Engine object, able to play sounds
     */
    private FXAudioEngine fxAudioEngine;

    /**
     * gridpane holds all cards
     */
    private GridPane gridPane;
    /**
     * computing the current screen height and width
     */
    private double mWidth = Screen.getPrimary().getBounds().getWidth();
    private double mHeight = Screen.getPrimary().getBounds().getHeight();
    /**
     * first draw defines whether the rendering engine will initialize or update the UI components
     */
    private boolean firstDraw = true;
    /**
     * current game scene
     */
    Scene gameScene;
    /**
     * JavFX component to bind the scene with the .fxml and .css file
     */
    protected Parent root;


    public FXRenderingEngine() {
        try {
            root = FXMLLoader.load(getClass().getResource("/fxml/game.fxml"));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        //initialize the audio engine object
        fxAudioEngine = new FXAudioEngine();

        gameScene = new Scene(root, mWidth, mHeight);
    }

    /**
     * List of actions captured by the user interaction. User in the Player-derived methods.
     */
    List<UserAction> pendingUserActions = Collections.synchronizedList(new ArrayList<>());

    @Override
    public void drawGameState(MemoriGameState currentState) {
        if (firstDraw) {
            //initialize UI components=
                try {
                    setUpFXComponents();
                    initFXComponents(currentState);
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            firstDraw = false;
        }
        else {
            //update UI components
            updateFXComponents(currentState);
        }
    }

    private long lLastUpdate = -1L;
    protected void updateFXComponents(MemoriGameState currentState) {
        long lNewTime = new Date().getTime();
        if (lNewTime - lLastUpdate < 100L) {// If no less than 1/10 sec has passed
            Thread.yield(); // TODO: Use it or do nothing?
            return; // Do nothing
        } else {
            lLastUpdate = lNewTime;
            // DEBUG LINES
//            System.err.println("Updating components " +lLastUpdate);
            List<GameEvent> eventsList = currentState.getEventQueue();
            ListIterator<GameEvent> listIterator = eventsList.listIterator();
            while (listIterator.hasNext()) {
                //System.out.println(listIterator.next());
                GameEvent currentGameEvent = listIterator.next();
                String eventType = currentGameEvent.type;
                Point2D coords;
                Card currCard;
                switch (eventType) {
                    case "movement":
                        coords = (Point2D) currentGameEvent.parameters;
                        focusOnTile((int) coords.getX(), (int) coords.getY());
                        movementSound((int) coords.getX(), (int) coords.getY());
                        System.out.println("now at: " + coords.getX() + "," + coords.getY());
                        listIterator.remove();
                        break;
                    case "invalidMovement":
                        Platform.runLater(() -> {
                            fxAudioEngine.playInvalidMovementSound();
                        });
                        listIterator.remove();
                        break;
                    case "empty":
                        Platform.runLater(() -> {
                            fxAudioEngine.playEmptySound();
                        });
                        listIterator.remove();
                        break;
                    case "cardSound":
                        coords = (Point2D) currentGameEvent.parameters;
                        currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                        Platform.runLater(() -> {
                            fxAudioEngine.playCardSound(currCard.getSound(), currentGameEvent.blocking);
                        });
                        listIterator.remove();
                        break;
                    case "flip":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            coords = (Point2D) currentGameEvent.parameters;
                            currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                            Platform.runLater(() -> {
                                currCard.flipUI();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "flipBack":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            coords = (Point2D) currentGameEvent.parameters;
                            currCard = (Card) ((MemoriTerrain) (currentState.getTerrain())).getTileByRowAndColumn((int) coords.getY(), (int) coords.getX());
                            Platform.runLater(() -> {
                                currCard.flipBackUI();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "success":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            Platform.runLater(() -> {
                                fxAudioEngine.playSuccessSound();
                            });
                            listIterator.remove();
                        }
                        break;
                    case "failure":
                        //check if the event should happen after some time
                        if (new Date().getTime() > currentGameEvent.delay) {
                            Platform.runLater(() -> {
                                fxAudioEngine.playFailureSound();
                            });
                            //TODO: play appropriate sound informing about new turn
                            listIterator.remove();
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    @Override
    public void playGameOver() {
        System.err.println("play game over");
        Platform.runLater(() -> {
            fxAudioEngine.playSuccessSound();
        });
    }



    /**
     * Given the coordinates, marks a Node as visited (green background) by applying a CSS class
     * @param rowIndex the Node x position
     * @param columnIndex the Node y position
     */
    private void focusOnTile(int rowIndex, int columnIndex) {
        System.out.println("point: " + rowIndex + "," + columnIndex);
        //get Node (in our case it's a button)
        Node node = getNodeByRowColumnIndex(rowIndex, columnIndex, gridPane);
        //remove the focused class from every other Node
        ObservableList<Node> nodes = gridPane.getChildren();
        for(Node nd: nodes) {
            nd.getStyleClass().remove("focusedCard");
        }
        //apply the CSS class
        node.getStyleClass().addAll("focusedCard");
        Button btn = (Button) node;
        //DEBUG print button id
        System.out.println(btn.getId());
    }

    /**
     * Computes the sound balance (left-right panning) and rate and plays the movement sound
     * @param rowIndex the Node x position
     * @param columnIndex the Node y position
     */
    private void movementSound(int rowIndex, int columnIndex) {
        System.err.println("movementSound");
        double soundBalance = map(columnIndex, 0.0, (double) MainOptions.NUMBER_OF_COLUMNS, -1.0, 2.0);
        double rate = map(rowIndex, 0.0, (double) MainOptions.NUMBER_OF_ROWS, 1.5, 1.0);
        System.err.println("rate: " + rate);
        fxAudioEngine.playMovementSound(soundBalance, rate);
    }

    private Node getNodeByRowColumnIndex(final int row,final int column, GridPane gridPane) {
        Node result = null;
        ObservableList<Node> childrens = gridPane.getChildren();
        for(Node node : childrens) {
            if(gridPane.getRowIndex(node) == row && gridPane.getColumnIndex(node) == column) {
                result = node;
                break;
            }
        }
        return result;
{'fraction_non_alphanumeric': 0.09672067148155378, 'fraction_numerical': 0.0035135662697638103, 'mean_word_length': 1.598985415345593, 'pattern_counts': {'":': 16, '<': 21, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4935357', 'n_tokens_mistral': 5129, 'n_tokens_neox': 4879, 'n_words': 1308}