package jp.yucchi.personalitydiagnosis;
 
import com.ibm.watson.developer_cloud.personality_insights.v3.model.Profile;
import com.ibm.watson.developer_cloud.personality_insights.v3.model.Trait;
import java.io.StringReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.concurrent.Task;
import static javafx.concurrent.Worker.State.RUNNING;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontSmoothingType;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import twitter4j.Status;
 
/**
 *
 * @author Yucchi
 */
public class FXMLPersonalityDiagnosisController implements Initializable {
 
    @FXML
    private ResourceBundle resources;
 
    @FXML
    private URL location;
 
    @FXML
    private AnchorPane anchorPane;
 
    @FXML
    private Canvas personalityChart;
 
    @FXML
    private Canvas personalityCanvas;
 
    @FXML
    private Canvas needsChart;
 
    @FXML
    private Canvas needsCanvas;
 
    @FXML
    private Canvas valuesChart;
 
    @FXML
    private Canvas valuesCanvas;
 
    @FXML
    private Label big5Label;
 
    @FXML
    private Label needsLabel;
 
    @FXML
    private Label valueLabel;
 
    @FXML
    private Label idLabel;
 
    @FXML
    private TextField idTextField;
 
    @FXML
    private Button diagnosisButton;
 
    @FXML
    private Button cancelButton;
 
    @FXML
    private ProgressIndicator progressIndicator;
 
    @FXML
    private Button clearButton;
 
    @FXML
    private Label messageLabel;
 
    // PersonalityChart 頂点の座標
    // 知的好奇心
    private double opennessX;
    private double opennessY;
    // 誠実性
    private double conscientiousnessX;
    private double conscientiousnessY;
    // 外向性
    private double extraversionX;
    private double extraversionY;
    // 感情起伏
    private double emotionalRangeX;
    private double emotionalRangeY;
    // 協調性
    private double agreeablenessX;
    private double agreeablenessY;
 
    // NeedsChart 頂点の座標
    // 実用主義
    private double practicalityX;
    private double practicalityY;
    // 好奇心
    private double curiosityX;
    private double curiosityY;
    // 自己表現
    private double selfExpressionX;
    private double selfExpressionY;
    // 理想
    private double idealX;
    private double idealY;
    // 自由主義
    private double libertyX;
    private double libertyY;
    // 安定性
    private double stabilityX;
    private double stabilityY;
    // 仕組
    private double structureX;
    private double structureY;
    // 興奮
    private double excitementX;
    private double excitementY;
    // 挑戦
    private double challengeX;
    private double challengeY;
    // 社会性
    private double loveX;
    private double loveY;
    // 調和
    private double harmonyX;
    private double harmonyY;
    // 親密
    private double closenessX;
    private double closenessY;
 
    // ValueChart 頂点の座標
    // 快楽主義
    private double hedonismX;
    private double hedonismY;
    // 自己増進
    private double selfEnhancementX;
    private double selfEnhancementY;
    // 変化許容性
    private double opennessToChangeX;
    private double opennessToChangeY;
    // 現状維持
    private double conservationX;
    private double conservationY;
    // 自己超越
    private double selfTranscendenceX;
    private double selfTranscendenceY;
 
    private final ExecutorService service = Executors.newSingleThreadExecutor();
    private Task<Profile> task;
    private TweetCollection tweetCollection;
    private PersonalityAnalysis personalityAnalysis;
    private GraphicsContext personalityGc;
    private GraphicsContext needsGc;
    private GraphicsContext valuesGc;
    private double width;
    private double jpCenterHeight;
    private double offset;
 
    @FXML
    void cancel(ActionEvent event) {
        task.cancel();
    }
 
    @FXML
    void diagnosis(ActionEvent event) {
        clearChart();
        getprofile();
    }
 
    @FXML
    void clear(ActionEvent event) {
        clearChart();
        idTextField.clear();
        messageLabel.textProperty().unbind();
        messageLabel.setText("Please enter the Twitter ID and press the diagnosis button.");
    }
 
    private void getprofile() {
 
        progressIndicator.setOpacity(1.0);
 
        task = new Task<>() {
 
            @Override
            protected Profile call() throws Exception {
 
                List<Status> tweets;
                Profile profile;
 
                updateMessage("Getting a tweets.");
                tweets = tweetCollection.getTweets(idTextField, task);
 
                if (isCancelled()) {
                    return null;
                }
 
                updateMessage("Completed getting tweets.  Personality analysis started.  Please wait for a while.");
                profile = personalityAnalysis.toAnalyze(tweets);
 
                if (profile == null) {
                    failed();
                }
 
                return profile;
 
            }
 
            @Override
            protected void succeeded() {
 
                super.succeeded();
 
                Profile profile = getValue();
                List<Trait> personality = profile.getPersonality();
                List<Trait> needs = profile.getNeeds();
                List<Trait> values = profile.getValues();
 
                JsonObject jsonObject;
                try (JsonReader jsonReader = Json.createReader(new StringReader(profile.toString()))) {
                    jsonObject = jsonReader.readObject();
                }
 
                updateMessage("Personality analysis is completed with " + jsonObject.getInt("word_count") + " words of Tweet.");
 
                Map<String, Double> personalityMap = new HashMap<>(personality.size());
                Map<String, Double> valuesMap = new HashMap<>(values.size());
                Map<String, Double> needsMap = new HashMap<>(needs.size());
 
                for (int i = 0; personality.size() > i; i++) {
                    String key = jsonObject.getJsonArray("personality").getJsonObject(i).getJsonString("name").getString();
                    double value = jsonObject.getJsonArray("personality").getJsonObject(i).getJsonNumber("percentile").doubleValue();
                    personalityMap.put(key, value);
                }
 
                for (int i = 0; values.size() > i; i++) {
                    String key = jsonObject.getJsonArray("values").getJsonObject(i).getJsonString("name").getString();
                    double value = jsonObject.getJsonArray("values").getJsonObject(i).getJsonNumber("percentile").doubleValue();
                    valuesMap.put(key, value);
                }
 
                for (int i = 0; needs.size() > i; i++) {
                    String key = jsonObject.getJsonArray("needs").getJsonObject(i).getJsonString("name").getString();
                    double value = jsonObject.getJsonArray("needs").getJsonObject(i).getJsonNumber("percentile").doubleValue();
                    needsMap.put(key, value);
                }
 
                drawPersonalityResult(personalityMap);
                drawNeedsResult(needsMap);
                drawValuesResult(valuesMap);
 
            }
 
            @Override
            protected void cancelled() {
                super.cancelled();
                updateMessage("Cancelled!");
                clearChart();
            }
 
            @Override
            protected void failed() {
                super.failed();
                updateMessage("Failed!  An unexpected error occurred for some reason.");
            }
 
            private void drawPersonalityResult(Map<String, Double> personalityMap) {
 
                // PersonalityResult
                double openness = personalityMap.getOrDefault("知的好奇心", 0.0);
                double conscientiousness = personalityMap.getOrDefault("誠実性", 0.0);
                double extraversion = personalityMap.getOrDefault("外向性", 0.0);
                double emotionalRange = personalityMap.getOrDefault("感情起伏", 0.0);
                double agreeableness = personalityMap.getOrDefault("協調性", 0.0);
 
                // チャート表示用
                String opennessResult = String.format("%.2f", openness * 100).concat("%");
                String conscientiousnessResult = String.format("%.2f", conscientiousness * 100).concat("%");
                String extraversionResult = String.format("%.2f", extraversion * 100).concat("%");
                String emotionalRangeResult = String.format("%.2f", emotionalRange * 100).concat("%");
                String agreeablenessResult = String.format("%.2f", agreeableness * 100).concat("%");
 
                // 原点
                final double originX = personalityCanvas.getWidth() / 2;
                final double originY = personalityCanvas.getHeight() / 2;
                // ライン長さ
                final double lineLength = 150.0;
                // 項目数
                final int items = personalityMap.size();
                // 知的好奇心 スコアポジション
                final double opennessScoreX = getPositionX(originX, lineLength, openness, items, 0);
                final double opennessScoreY = getPositionY(originY, lineLength, openness, items, 0);
                // 誠実性 スコアポジション
                final double conscientiousnessScoreX = getPositionX(originX, lineLength, conscientiousness, items, 1);
                final double conscientiousnessScoreY = getPositionY(originY, lineLength, conscientiousness, items, 1);
                // 外向性 スコアポジション
                final double extraversionScoreX = getPositionX(originX, lineLength, extraversion, items, 2);
                final double extraversionScoreY = getPositionY(originY, lineLength, extraversion, items, 2);
                // 感情起伏 スコアポジション
                final double emotionalRangeScoreX = getPositionX(originX, lineLength, emotionalRange, items, 3);
                final double emotionalRangeScoreY = getPositionY(originY, lineLength, emotionalRange, items, 3);
                // 協調性 スコアポジション
                final double agreeablenessScoreX = getPositionX(originX, lineLength, agreeableness, items, 4);
                final double agreeablenessScoreY = getPositionY(originY, lineLength, agreeableness, items, 4);
 
                // スコア表示
                personalityGc.setStroke(Color.rgb(255, 0, 255));
                personalityGc.setLineWidth(2);
                personalityGc.beginPath();
                personalityGc.moveTo(opennessScoreX, opennessScoreY);
                personalityGc.lineTo(conscientiousnessScoreX, conscientiousnessScoreY);
                personalityGc.lineTo(extraversionScoreX, extraversionScoreY);
                personalityGc.lineTo(emotionalRangeScoreX, emotionalRangeScoreY);
                personalityGc.lineTo(agreeablenessScoreX, agreeablenessScoreY);
                personalityGc.closePath();
                personalityGc.stroke();
                personalityGc.setFill(Color.rgb(255, 0, 255, 0.3));
                personalityGc.fill();
 
                // 項目 結果表示
                personalityGc.setFontSmoothingType(FontSmoothingType.LCD);
                personalityGc.setFont(Font.font(18));
                personalityGc.setFill(Color.RED);
                personalityGc.fillText(opennessResult, opennessX - width * 5 / 2, opennessY + jpCenterHeight - offset - offset * 1.1);
                personalityGc.fillText(conscientiousnessResult, conscientiousnessX + width / 2, conscientiousnessY + jpCenterHeight - offset * 1.1);
                personalityGc.fillText(extraversionResult, extraversionX - width, extraversionY + jpCenterHeight + offset + offset * 1.1);
                personalityGc.fillText(emotionalRangeResult, emotionalRangeX - width * 9 / 4, emotionalRangeY + jpCenterHeight + offset + offset * 1.1);
                personalityGc.fillText(agreeablenessResult, agreeablenessX - width * 7 / 2, agreeablenessY + jpCenterHeight - offset * 1.1);
 
            }
 
            private void drawNeedsResult(Map<String, Double> needsMap) {
 
                // NeedsResult
                double practicality = needsMap.getOrDefault("実用主義", 0.0);
                double curiosity = needsMap.getOrDefault("好奇心", 0.0);
                double selfExpression = needsMap.getOrDefault("自己表現", 0.0);
                double ideal = needsMap.getOrDefault("理想", 0.0);
                double liberty = needsMap.getOrDefault("自由主義", 0.0);
                double stability = needsMap.getOrDefault("安定性", 0.0);
                double structure = needsMap.getOrDefault("仕組", 0.0);
                double excitement = needsMap.getOrDefault("興奮", 0.0);
                double challenge = needsMap.getOrDefault("挑戦", 0.0);
                double love = needsMap.getOrDefault("社会性", 0.0);
                double harmony = needsMap.getOrDefault("調和", 0.0);
                double closeness = needsMap.getOrDefault("親密", 0.0);
 
                // チャート表示用
                String practicalityResult = String.format("%.2f", practicality * 100).concat("%");
                String curiosityResult = String.format("%.2f", curiosity * 100).concat("%");
                String selfExpressionResult = String.format("%.2f", selfExpression * 100).concat("%");
                String idealResult = String.format("%.2f", ideal * 100).concat("%");
                String libertyResult = String.format("%.2f", liberty * 100).concat("%");
                String stabilityResult = String.format("%.2f", stability * 100).concat("%");
                String structureResult = String.format("%.2f", structure * 100).concat("%");
                String excitementResult = String.format("%.2f", excitement * 100).concat("%");
                String challengeResult = String.format("%.2f", challenge * 100).concat("%");
                String loveResult = String.format("%.2f", love * 100).concat("%");
                String harmonyResult = String.format("%.2f", harmony * 100).concat("%");
                String closenessResult = String.format("%.2f", closeness * 100).concat("%");
 
                // 原点
                final double originX = needsCanvas.getWidth() / 2;
                final double originY = needsCanvas.getHeight() / 2;
                // ライン長さ
                final double lineLength = 150.0;
                // 項目数
                final int items = needsMap.size();
                // 実用主義 スコアポジション
                final double practicalityScoreX = getPositionX(originX, lineLength, practicality, items, 0);
                final double practicalityScoreY = getPositionY(originY, lineLength, practicality, items, 0);
                // 好奇心 スコアポジション
                final double curiosityScoreX = getPositionX(originX, lineLength, curiosity, items, 1);
                final double curiosityScoreY = getPositionY(originY, lineLength, curiosity, items, 1);
                // 自己表現 スコアポジション
                final double selfExpressionScoreX = getPositionX(originX, lineLength, selfExpression, items, 2);
                final double selfExpressionScoreY = getPositionY(originY, lineLength, selfExpression, items, 2);
                // 理想 スコアポジション
                final double idealScoreX = getPositionX(originX, lineLength, ideal, items, 3);
                final double idealScoreY = getPositionY(originY, lineLength, ideal, items, 3);
                // 自由主義 スコアポジション
                final double libertyScoreX = getPositionX(originX, lineLength, liberty, items, 4);
                final double libertyScoreY = getPositionY(originY, lineLength, liberty, items, 4);
                // 安定性 スコアポジション
                final double stabilityScoreX = getPositionX(originX, lineLength, stability, items, 5);
                final double stabilityScoreY = getPositionY(originY, lineLength, stability, items, 5);
                // 仕組 スコアポジション
                final double structureScoreX = getPositionX(originX, lineLength, structure, items, 6);
                final double structureScoreY = getPositionY(originY, lineLength, structure, items, 6);
                // 興奮 スコアポジション
                final double excitementScoreX = getPositionX(originX, lineLength, excitement, items, 7);
                final double excitementScoreY = getPositionY(originY, lineLength, excitement, items, 7);
                // 挑戦 スコアポジション
                final double challengeScoreX = getPositionX(originX, lineLength, challenge, items, 8);
                final double challengeScoreY = getPositionY(originY, lineLength, challenge, items, 8);
                // 社会性 スコアポジション
                final double loveScoreX = getPositionX(originX, lineLength, love, items, 9);
                final double loveScoreY = getPositionY(originY, lineLength, love, items, 9);
                // 調和 スコアポジション
                final double harmonyScoreX = getPositionX(originX, lineLength, harmony, items, 10);
                final double harmonyScoreY = getPositionY(originY, lineLength, harmony, items, 10);
                // 親密 スコアポジション
                final double closenessScoreX = getPositionX(originX, lineLength, closeness, items, 11);
                final double closenessScoreY = getPositionY(originY, lineLength, closeness, items, 11);
 
                // スコア表示
                needsGc.setStroke(Color.rgb(255, 0, 255));
                needsGc.setLineWidth(2);
                needsGc.beginPath();
                needsGc.moveTo(practicalityScoreX, practicalityScoreY);
                needsGc.lineTo(curiosityScoreX, curiosityScoreY);
                needsGc.lineTo(selfExpressionScoreX, selfExpressionScoreY);
                needsGc.lineTo(idealScoreX, idealScoreY);
                needsGc.lineTo(libertyScoreX, libertyScoreY);
                needsGc.lineTo(stabilityScoreX, stabilityScoreY);
                needsGc.lineTo(structureScoreX, structureScoreY);
                needsGc.lineTo(excitementScoreX, excitementScoreY);
                needsGc.lineTo(challengeScoreX, challengeScoreY);
                needsGc.lineTo(loveScoreX, loveScoreY);
                needsGc.lineTo(harmonyScoreX, harmonyScoreY);
                needsGc.lineTo(closenessScoreX, closenessScoreY);
                needsGc.closePath();
                needsGc.stroke();
                needsGc.setFill(Color.rgb(255, 0, 255, 0.3));
                needsGc.fill();
 
                // 項目 結果表示
                needsGc.setFontSmoothingType(FontSmoothingType.LCD);
                needsGc.setFont(Font.font(18));
                needsGc.setFill(Color.RED);
                needsGc.fillText(practicalityResult, practicalityX - width * 4 / 2, practicalityY + jpCenterHeight - offset - offset * 1.1);
                needsGc.fillText(curiosityResult, curiosityX + width / 2, curiosityY + jpCenterHeight - offset / 2 - offset * 1.1);
                needsGc.fillText(selfExpressionResult, selfExpressionX + width / 2, selfExpressionY - jpCenterHeight + offset / 2 - offset * 1.1);
                needsGc.fillText(idealResult, idealX + width / 2, idealY + jpCenterHeight - offset * 1.1);
                needsGc.fillText(libertyResult, libertyX + width / 2, libertyY + jpCenterHeight + offset / 2 + offset * 1.1);;
                needsGc.fillText(stabilityResult, stabilityX + width / 2, stabilityY - jpCenterHeight + offset * 1.5 + offset * 1.1);
                needsGc.fillText(structureResult, structureX - width * 2 / 2, structureY + jpCenterHeight + offset + offset * 1.1);
                needsGc.fillText(excitementResult, excitementX - width * 5 / 2, excitementY - jpCenterHeight + offset * 1.5 + offset * 1.1);
                needsGc.fillText(challengeResult, challengeX - width * 5 / 2, challengeY + jpCenterHeight + offset / 2 + offset * 1.1);
                needsGc.fillText(loveResult, loveX - width * 7 / 2, loveY + jpCenterHeight - offset * 1.1);
                needsGc.fillText(harmonyResult, harmonyX - width * 5 / 2, harmonyY - jpCenterHeight + offset / 2 - offset * 1.1);
                needsGc.fillText(closenessResult, closenessX - width * 5 / 2, closenessY + jpCenterHeight - offset / 2 - offset * 1.1);
 
            }
 
            private void drawValuesResult(Map<String, Double> valuesMap) {
 
                double hedonism = valuesMap.getOrDefault("快楽主義", 0.0);
                double selfEnhancement = valuesMap.getOrDefault("自己増進", 0.0);
                double opennessToChange = valuesMap.getOrDefault("変化許容性", 0.0);
                double conservation = valuesMap.getOrDefault("現状維持", 0.0);
                double selfTranscendence = valuesMap.getOrDefault("自己超越", 0.0);
 
                String hedonismResult = String.format("%.2f", hedonism * 100).concat("%");
                String selfEnhancementResult = String.format("%.2f", selfEnhancement * 100).concat("%");
                String opennessToChangeResult = String.format("%.2f", opennessToChange * 100).concat("%");
                String conservationResult = String.format("%.2f", conservation * 100).concat("%");
                String selfTranscendenceResult = String.format("%.2f", selfTranscendence * 100).concat("%");
 
                // 原点
                final double originX = valuesCanvas.getWidth() / 2;
                final double originY = valuesCanvas.getHeight() / 2;
                // ライン長さ
                final double lineLength = 150.0;
                // 項目数
                final int items = valuesMap.size();
                // 快楽主義 スコアポジション
                final double hedonismScoreX = getPositionX(originX, lineLength, hedonism, items, 0);
                final double hedonismScoreY = getPositionY(originY, lineLength, hedonism, items, 0);
                // 自己増進 スコアポジション
                final double selfEnhancementScoreX = getPositionX(originX, lineLength, selfEnhancement, items, 1);
                final double selfEnhancementScoreY = getPositionY(originY, lineLength, selfEnhancement, items, 1);
                // 変化許容性 スコアポジション
                final double opennessToChangeScoreX = getPositionX(originX, lineLength, opennessToChange, items, 2);
                final double opennessToChangeScoreY = getPositionY(originY, lineLength, opennessToChange, items, 2);
                // 現状維持 スコアポジション
                final double conservationScoreX = getPositionX(originX, lineLength, conservation, items, 3);
                final double conservationScoreY = getPositionY(originY, lineLength, conservation, items, 3);
                // 自己超越 スコアポジション
                final double selfTranscendenceScoreX = getPositionX(originX, lineLength, selfTranscendence, items, 4);
                final double selfTranscendenceScoreY = getPositionY(originY, lineLength, selfTranscendence, items, 4);
 
                // スコア表示
                valuesGc.setStroke(Color.rgb(255, 0, 255));
                valuesGc.setLineWidth(2);
                valuesGc.beginPath();
                valuesGc.moveTo(hedonismScoreX, hedonismScoreY);
                valuesGc.lineTo(selfEnhancementScoreX, selfEnhancementScoreY);
                valuesGc.lineTo(opennessToChangeScoreX, opennessToChangeScoreY);
                valuesGc.lineTo(conservationScoreX, conservationScoreY);
                valuesGc.lineTo(selfTranscendenceScoreX, selfTranscendenceScoreY);
                valuesGc.closePath();
                valuesGc.stroke();
                valuesGc.setFill(Color.rgb(255, 0, 255, 0.3));
                valuesGc.fill();
 
                // 項目 結果表示
                valuesGc.setFontSmoothingType(FontSmoothingType.LCD);
                valuesGc.setFont(Font.font(18));
                valuesGc.setFill(Color.RED);
                valuesGc.fillText(hedonismResult, hedonismX - width * 4 / 2, hedonismY + jpCenterHeight - offset - offset * 1.1);
                valuesGc.fillText(selfEnhancementResult, selfEnhancementX + width / 2, selfEnhancementY + jpCenterHeight - offset * 1.1);
                valuesGc.fillText(opennessToChangeResult, opennessToChangeX - width * 2, opennessToChangeY + jpCenterHeight + offset + offset * 1.1);
                valuesGc.fillText(conservationResult, conservationX - width * 9 / 4, conservationY + jpCenterHeight + offset + offset * 1.1);
                valuesGc.fillText(selfTranscendenceResult, selfTranscendenceX - width * 9 / 2, selfTranscendenceY + jpCenterHeight - offset * 1.1);
 
            }
 
        };
 
        service.submit(task);
 
        idTextField.editableProperty().bind(task.stateProperty().isNotEqualTo(RUNNING));
        diagnosisButton.disableProperty().bind(Bindings.or(
                task.stateProperty().isEqualTo(RUNNING),
                idTextField.textProperty().isEmpty()));
        cancelButton.disableProperty().bind(task.stateProperty().isNotEqualTo(RUNNING));
        clearButton.disableProperty().bind(task.stateProperty().isEqualTo(RUNNING));
        progressIndicator.visibleProperty().bind(task.stateProperty().isEqualTo(RUNNING));
        messageLabel.textProperty().bind(task.messageProperty());
 
    }
 
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        tweetCollection = new TweetCollection();
        personalityAnalysis = new PersonalityAnalysis();
        personalityGc = personalityCanvas.getGraphicsContext2D();
        needsGc = needsCanvas.getGraphicsContext2D();
        valuesGc = valuesCanvas.getGraphicsContext2D();
        progressIndicator.setProgress(-1.0);
        diagnosisButton.disableProperty().bind(idTextField.textProperty().isEmpty());
 
        // Canvas のテキスト座標設定に使う。生肉、焼肉、苦肉の策としてこうなった。(>_<。) 今後の課題とする。
        Text text = new Text("夢");
        text.setFont(Font.font(18));
        width = text.getBoundsInLocal().getWidth();
        jpCenterHeight = (2 * text.getBaselineOffset() - text.getBoundsInLocal().getHeight()) / 2;
        offset = text.getFont().getSize();
 
        drawPersonalityChart();
        drawNeedsChart();
        drawValueChart();
    }
 
    void setStage(Stage stage) {
        stage.setOnCloseRequest((WindowEvent we) -> {
            // クロージングアニメーション
            DoubleProperty closeOpacityProperty = new SimpleDoubleProperty(1.0);
            stage.opacityProperty().bind(closeOpacityProperty);
            Timeline closeTimeline = new Timeline(
                    new KeyFrame(
                            new Duration(100),
                            new KeyValue(closeOpacityProperty, 1.0)
                    ), new KeyFrame(
                            new Duration(2_500),
                            new KeyValue(closeOpacityProperty, 0.0)
                    ));
            EventHandler<ActionEvent> eh = ae -> {
                service.shutdownNow();
                Platform.exit();
                System.exit(0);
            };
 
            closeTimeline.setOnFinished(eh);
            closeTimeline.setCycleCount(1);
            closeTimeline.play();
            we.consume();
        });
    }
 
    private void drawPersonalityChart() {
        // 原点
        final double originX = personalityChart.getWidth() / 2;
        final double originY = personalityChart.getHeight() / 2;
        // ライン長さ
        final double lineLength = 150.0;
        // 項目数
        final int items = 5;
        // 知的好奇心
        opennessX = getPositionX(originX, lineLength, 1.0, items, 0);
        opennessY = getPositionY(originY, lineLength, 1.0, items, 0);
        // 誠実性
        conscientiousnessX = getPositionX(originX, lineLength, 1.0, items, 1);
        conscientiousnessY = getPositionY(originY, lineLength, 1.0, items, 1);
        // 外向性
        extraversionX = getPositionX(originX, lineLength, 1.0, items, 2);
        extraversionY = getPositionY(originY, lineLength, 1.0, items, 2);
        // 感情起伏
        emotionalRangeX = getPositionX(originX, lineLength, 1.0, items, 3);
        emotionalRangeY = getPositionY(originY, lineLength, 1.0, items, 3);
        // 協調性
        agreeablenessX = getPositionX(originX, lineLength, 1.0, items, 4);
        agreeablenessY = getPositionY(originY, lineLength, 1.0, items, 4);
 
        GraphicsContext gc = personalityChart.getGraphicsContext2D();
 
        gc.setStroke(Color.BLUE);
        gc.setLineWidth(1);
        gc.strokeLine(originX, originY, opennessX, opennessY);
        gc.strokeLine(originX, originY, conscientiousnessX, conscientiousnessY);
        gc.strokeLine(originX, originY, extraversionX, extraversionY);
        gc.strokeLine(originX, originY, emotionalRangeX, emotionalRangeY);
        gc.strokeLine(originX, originY, agreeablenessX, agreeablenessY);
        for (int i = 0; 6 > i; i++) {
            gc.beginPath();
            gc.moveTo(getPositionX(originX, lineLength, i * 0.2, items, 0), getPositionY(originY, lineLength, i * 0.2, items, 0));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 1), getPositionY(originY, lineLength, i * 0.2, items, 1));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 2), getPositionY(originY, lineLength, i * 0.2, items, 2));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 3), getPositionY(originY, lineLength, i * 0.2, items, 3));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 4), getPositionY(originY, lineLength, i * 0.2, items, 4));
            gc.closePath();
            gc.stroke();
        }
 
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFont(Font.font(18));
        gc.setFill(Color.RED);
        gc.fillText("知的好奇心", opennessX - width * 5 / 2, opennessY + jpCenterHeight - offset);
        gc.fillText("誠実性", conscientiousnessX + width / 2, conscientiousnessY + jpCenterHeight);
        gc.fillText("外向性", extraversionX - width, extraversionY + jpCenterHeight + offset);
        gc.fillText("感情起伏", emotionalRangeX - width * 9 / 4, emotionalRangeY + jpCenterHeight + offset);
        gc.fillText("協調性", agreeablenessX - width * 7 / 2, agreeablenessY + jpCenterHeight);
 
    }
 
    private void drawNeedsChart() {
        // 原点
        final double originX = needsChart.getWidth() / 2;
        final double originY = needsChart.getHeight() / 2;
        // ライン長さ
        final double lineLength = 150.0;
        // 項目数
        final int items = 12;
        // 実用主義
        practicalityX = getPositionX(originX, lineLength, 1.0, items, 0);
        practicalityY = getPositionY(originY, lineLength, 1.0, items, 0);
        // 好奇心
        curiosityX = getPositionX(originX, lineLength, 1.0, items, 1);
        curiosityY = getPositionY(originY, lineLength, 1.0, items, 1);
        // 自己表現
        selfExpressionX = getPositionX(originX, lineLength, 1.0, items, 2);
        selfExpressionY = getPositionY(originY, lineLength, 1.0, items, 2);
        // 理想
        idealX = getPositionX(originX, lineLength, 1.0, items, 3);
        idealY = getPositionY(originY, lineLength, 1.0, items, 3);
        // 自由主義
        libertyX = getPositionX(originX, lineLength, 1.0, items, 4);
        libertyY = getPositionY(originY, lineLength, 1.0, items, 4);
        // 安定性
        stabilityX = getPositionX(originX, lineLength, 1.0, items, 5);
        stabilityY = getPositionY(originY, lineLength, 1.0, items, 5);
        // 仕組
        structureX = getPositionX(originX, lineLength, 1.0, items, 6);
        structureY = getPositionY(originY, lineLength, 1.0, items, 6);
        // 興奮
        excitementX = getPositionX(originX, lineLength, 1.0, items, 7);
        excitementY = getPositionY(originY, lineLength, 1.0, items, 7);
        // 挑戦
        challengeX = getPositionX(originX, lineLength, 1.0, items, 8);
        challengeY = getPositionY(originY, lineLength, 1.0, items, 8);
        // 社会性
        loveX = getPositionX(originX, lineLength, 1.0, items, 9);
        loveY = getPositionY(originY, lineLength, 1.0, items, 9);
        // 調和
        harmonyX = getPositionX(originX, lineLength, 1.0, items, 10);
        harmonyY = getPositionY(originY, lineLength, 1.0, items, 10);
        // 親密
        closenessX = getPositionX(originX, lineLength, 1.0, items, 11);
        closenessY = getPositionY(originY, lineLength, 1.0, items, 11);
 
        GraphicsContext gc = needsChart.getGraphicsContext2D();
 
        gc.setStroke(Color.BLUE);
        gc.setLineWidth(1);
        gc.strokeLine(originX, originY, practicalityX, practicalityY);
        gc.strokeLine(originX, originY, curiosityX, curiosityY);
        gc.strokeLine(originX, originY, selfExpressionX, selfExpressionY);
        gc.strokeLine(originX, originY, idealX, idealY);
        gc.strokeLine(originX, originY, libertyX, libertyY);
        gc.strokeLine(originX, originY, stabilityX, stabilityY);
        gc.strokeLine(originX, originY, structureX, structureY);
        gc.strokeLine(originX, originY, excitementX, excitementY);
        gc.strokeLine(originX, originY, challengeX, challengeY);
        gc.strokeLine(originX, originY, loveX, loveY);
        gc.strokeLine(originX, originY, harmonyX, harmonyY);
        gc.strokeLine(originX, originY, closenessX, closenessY);
        for (int i = 0; 6 > i; i++) {
            gc.beginPath();
            gc.moveTo(getPositionX(originX, lineLength, i * 0.2, items, 0), getPositionY(originY, lineLength, i * 0.2, items, 0));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 1), getPositionY(originY, lineLength, i * 0.2, items, 1));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 2), getPositionY(originY, lineLength, i * 0.2, items, 2));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 3), getPositionY(originY, lineLength, i * 0.2, items, 3));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 4), getPositionY(originY, lineLength, i * 0.2, items, 4));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 5), getPositionY(originY, lineLength, i * 0.2, items, 5));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 6), getPositionY(originY, lineLength, i * 0.2, items, 6));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 7), getPositionY(originY, lineLength, i * 0.2, items, 7));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 8), getPositionY(originY, lineLength, i * 0.2, items, 8));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 9), getPositionY(originY, lineLength, i * 0.2, items, 9));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 10), getPositionY(originY, lineLength, i * 0.2, items, 10));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 11), getPositionY(originY, lineLength, i * 0.2, items, 11));
            gc.closePath();
            gc.stroke();
        }
 
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFont(Font.font(18));
        gc.setFill(Color.RED);
        gc.fillText("実用主義", practicalityX - width * 4 / 2, practicalityY + jpCenterHeight - offset);
        gc.fillText("好奇心", curiosityX + width / 2, curiosityY + jpCenterHeight - offset / 2);
        gc.fillText("自己表現", selfExpressionX + width / 2, selfExpressionY - jpCenterHeight + offset / 2);
        gc.fillText("理想", idealX + width / 2, idealY + jpCenterHeight);
        gc.fillText("自由主義", libertyX + width / 2, libertyY + jpCenterHeight + offset / 2);
        gc.fillText("安定性", stabilityX + width / 2, stabilityY - jpCenterHeight + offset * 1.5);
        gc.fillText("仕組", structureX - width * 2 / 2, structureY + jpCenterHeight + offset);
        gc.fillText("興奮", excitementX - width * 5 / 2, excitementY - jpCenterHeight + offset * 1.5);
        gc.fillText("挑戦", challengeX - width * 5 / 2, challengeY + jpCenterHeight + offset / 2);
        gc.fillText("社会性", loveX - width * 7 / 2, loveY + jpCenterHeight);
        gc.fillText("調和", harmonyX - width * 5 / 2, harmonyY - jpCenterHeight + offset / 2);
        gc.fillText("親密", closenessX - width * 5 / 2, closenessY + jpCenterHeight - offset / 2);
 
    }
 
    private void drawValueChart() {
        // 原点
        final double originX = valuesChart.getWidth() / 2;
        final double originY = valuesChart.getHeight() / 2;
        // ライン長さ
        final double lineLength = 150.0;
        // 項目数
        final int items = 5;
 
        // 快楽主義
        hedonismX = getPositionX(originX, lineLength, 1.0, items, 0);
        hedonismY = getPositionY(originY, lineLength, 1.0, items, 0);
        // 自己増進
        selfEnhancementX = getPositionX(originX, lineLength, 1.0, items, 1);
        selfEnhancementY = getPositionY(originY, lineLength, 1.0, items, 1);
        // 変化許容性
        opennessToChangeX = getPositionX(originX, lineLength, 1.0, items, 2);
        opennessToChangeY = getPositionY(originY, lineLength, 1.0, items, 2);
        // 現状維持
        conservationX = getPositionX(originX, lineLength, 1.0, items, 3);
        conservationY = getPositionY(originY, lineLength, 1.0, items, 3);
        // 自己超越
        selfTranscendenceX = getPositionX(originX, lineLength, 1.0, items, 4);
        selfTranscendenceY = getPositionY(originY, lineLength, 1.0, items, 4);
 
        GraphicsContext gc = valuesChart.getGraphicsContext2D();
 
        gc.setStroke(Color.BLUE);
        gc.setLineWidth(1);
        gc.strokeLine(originX, originY, hedonismX, hedonismY);
        gc.strokeLine(originX, originY, selfEnhancementX, selfEnhancementY);
        gc.strokeLine(originX, originY, opennessToChangeX, opennessToChangeY);
        gc.strokeLine(originX, originY, conservationX, conservationY);
        gc.strokeLine(originX, originY, selfTranscendenceX, selfTranscendenceY);
        for (int i = 0; 6 > i; i++) {
            gc.beginPath();
            gc.moveTo(getPositionX(originX, lineLength, i * 0.2, items, 0), getPositionY(originY, lineLength, i * 0.2, items, 0));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 1), getPositionY(originY, lineLength, i * 0.2, items, 1));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 2), getPositionY(originY, lineLength, i * 0.2, items, 2));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 3), getPositionY(originY, lineLength, i * 0.2, items, 3));
            gc.lineTo(getPositionX(originX, lineLength, i * 0.2, items, 4), getPositionY(originY, lineLength, i * 0.2, items, 4));
            gc.closePath();
            gc.stroke();
        }
 
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFont(Font.font(18));
        gc.setFill(Color.RED);
        gc.fillText("快楽主義", hedonismX - width * 4 / 2, hedonismY + jpCenterHeight - offset);
        gc.fillText("自己増進", selfEnhancementX + width / 2, selfEnhancementY + jpCenterHeight);
        gc.fillText("変化許容性", opennessToChangeX - width * 2, opennessToChangeY + jpCenterHeight + offset);
        gc.fillText("現状維持", conservationX - width * 9 / 4, conservationY + jpCenterHeight + offset);
        gc.fillText("自己超越", selfTranscendenceX - width * 9 / 2, selfTranscendenceY + jpCenterHeight);
    }
 
    private double getPositionX(double originX, double lineLength, double ratio, int items, int vertex) {
        return originX + lineLength * ratio * Math.cos(Math.toRadians(90 - (360 / items) * vertex));
    }
 
    private double getPositionY(double originY, double lineLength, double ratio, int items, int vertex) {
        return originY - lineLength * ratio * Math.sin(Math.toRadians(90 - (360 / items) * vertex));
    }
 
    private void clearChart() {
        personalityGc.clearRect(0, 0, personalityCanvas.getWidth(), personalityCanvas.getHeight());
        needsGc.clearRect(0, 0, needsCanvas.getWidth(), needsCanvas.getHeight());
        valuesGc.clearRect(0, 0, valuesCanvas.getWidth(), valuesCanvas.getHeight());
    }
 
}