/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package poe.level.fx; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXListView; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ButtonType; import javafx.scene.layout.VBox; import org.json.JSONArray; import org.json.JSONObject; import poe.level.data.*; import poe.level.fx.SocketGroupsPanel_Controller.SocketGroupLinker; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import java.util.logging.Logger; /** * FXML Controller class * * @author Christos */ public class BuildsPanel_Controller implements Initializable { private final Logger m_logger = Logger.getLogger(BuildsPanel_Controller.class.getName()); public class BuildLinker{ public BuildEntry_Controller pec; public Build build; public int id; public BuildsPanel_Controller root; public ArrayList<SocketGroupLinker> sgl_list = new ArrayList<>(); public int hook(BuildsPanel_Controller root){ this.root = root; id = BuildsPanel_Controller.sign(); return id; } public void delete(){ root.deleteBuild(); } public void update(){ root.update(id); } public void requestNameChange(String newName){ build.buildName = newName; } } private static HashSet<Integer> randomIDs; public static int sign(){ if(randomIDs == null) randomIDs = new HashSet<>(); int ran; do{ ran = ThreadLocalRandom.current().nextInt(1,999999); }while(randomIDs.contains(ran)); randomIDs.add(ran); return ran; } @FXML private JFXButton addBuild_button; @FXML private JFXButton removeBuild_button; @FXML private JFXListView buildsBox; private MainApp_Controller root; private SocketGroupsPanel_Controller sgc; private HashMap<Integer,BuildLinker> linker; private int activeBuildID; public String lastbuild_invalidated; public int lastbuild_invalidatedID; public void hook(MainApp_Controller root){ this.root = root; } public void hookSG_Controller(SocketGroupsPanel_Controller sgc){ this.sgc = sgc; } public void loadBuilds(ArrayList<Build> buildsLoaded){ for(Build b : buildsLoaded){ loadNewBuild(b); } } public void addNewSocketGroup(SocketGroup sg){ linker.get(activeBuildID).build.getSocketGroup().add(sg); } public void removeSocketGroup(SocketGroup sg){ linker.get(activeBuildID).build.getSocketGroup().remove(sg); } @FXML private void addNewBuild(){ root.buildPopup(); } public boolean validate(){ Build active_build = linker.get(activeBuildID).build; if(active_build.validate()){ System.out.println("Validating build " + linker.get(activeBuildID).build.getName() + " .. success"); if(!active_build.isValid){ //signal a graphic change active_build.isValid = true; updateBuildValidationBanner(activeBuildID); } return true; }else{ System.out.println("Validating build " + linker.get(activeBuildID).build.getName() + " .. failed"); lastbuild_invalidatedID = activeBuildID; return false; } } public boolean validateAll(){ for(BuildLinker bl : linker.values()){ Build active_build = bl.build; if(!active_build.validate()){ if(active_build.isValid){ //active_build.isValid = false; System.out.println("Validating build "+ active_build.getName() + " via validate all option ... failed -> setting Build to non-valid."); lastbuild_invalidatedID = bl.id; //updateBuildValidationBanner(bl.id); return false; } }else if(active_build.validate() && !active_build.isValid){ //if it passed validation but was marked as non valid //signal a graphic change active_build.isValid = true; updateBuildValidationBanner(bl.id); } } return true; } public String validateError(){ Build active_build = linker.get(lastbuild_invalidatedID).build; lastbuild_invalidated = active_build.getName(); return active_build.validate_failed_string(); } public String validateAllError(){ lastbuild_invalidated = ""; for(BuildLinker bl : linker.values()){ Build active_build = bl.build; if(!active_build.validate()){ lastbuild_invalidated = active_build.getName(); lastbuild_invalidatedID = bl.id; //not sure what this one does in this method. return active_build.validate_failed_string(); } } return null; } public void setBuildToNonValid(){ Build active_build = linker.get(lastbuild_invalidatedID).build; active_build.isValid = false; active_build.patch_missing_sgroups(); updateBuildValidationBanner(lastbuild_invalidatedID); } public Build getCurrentBuild(){ return linker.get(activeBuildID).build; } public int sign_jsons(HashSet<Integer> unique_ids){ if(unique_ids == null) unique_ids = new HashSet<>(); int ran; do{ ran = ThreadLocalRandom.current().nextInt(1,999999); }while(unique_ids.contains(ran)); unique_ids.add(ran); return ran; } public void saveBuild() throws IOException { JSONArray builds_array = new JSONArray(); HashSet<Integer> unique_ids = new HashSet<>(); for( BuildLinker bl : linker.values()){ buildTo64(bl.build, builds_array, unique_ids); } String build_to_json = builds_array.toString(); //Gson gson = new Gson(); //String build_to_json = gson.toJson(linker.get(activeBuildID).build); //System.out.println(build_to_json); String stringValueBase64Encoded = Base64.getEncoder().encodeToString(build_to_json.getBytes()); //System.out.println(build_to_json + " when Base64 encoded is: " + stringValueBase64Encoded); BufferedWriter bw = null; FileWriter fw = null; try { fw = new FileWriter(POELevelFx.directory+"\\Path of Leveling\\Builds\\builds.txt"); bw = new BufferedWriter(fw); bw.write(stringValueBase64Encoded); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) bw.close(); if (fw != null) fw.close(); } catch (IOException ex) { ex.printStackTrace(); } } } public String allTo64(){ JSONArray builds_array = new JSONArray(); HashSet<Integer> unique_ids = new HashSet<>(); for( BuildLinker bl : linker.values()){ buildTo64(bl.build, builds_array, unique_ids); } String build_to_json = builds_array.toString(); //System.out.println(build_to_json); String stringValueBase64Encoded = Base64.getEncoder().encodeToString(build_to_json.getBytes()); //System.out.println(build_to_json + " when Base64 encoded is: " + stringValueBase64Encoded); return stringValueBase64Encoded; } public String activeTo64(){ Build build = linker.get(activeBuildID).build; JSONArray builds_array = new JSONArray(); HashSet<Integer> unique_ids = new HashSet<>(); buildTo64(build, builds_array, unique_ids); String build_to_json = builds_array.toString(); //System.out.println(build_to_json); String stringValueBase64Encoded = Base64.getEncoder().encodeToString(build_to_json.getBytes()); //System.out.println(build_to_json + " when Base64 encoded is: " + stringValueBase64Encoded); return stringValueBase64Encoded; } private void buildTo64(Build build, JSONArray builds_array, HashSet<Integer> unique_ids) { JSONObject bObj = new JSONObject(); bObj.put("buildName",build.getName()); bObj.put("className",build.getClassName()); if(build.getAsc().equals("Assasin")) System.err.println("not fixed typo when loading build from external source"); bObj.put("ascendancyName",build.getAsc()); bObj.put("isValid", build.isValid); bObj.put("level", build.getCharacterLevel()); //<change bObj.put("characterName",build.getCharacterName()); bObj.put("hasPob",build.hasPob); bObj.put("pobLink",build.pobLink); JSONArray socket_group_array = new JSONArray(); bObj.put("socketGroup", socket_group_array); for(SocketGroup sg : build.getSocketGroup()){ JSONObject sObj = new JSONObject(); if(sg.id == -1) sg.id = sign_jsons(unique_ids); sObj.put("id",sg.id); if(sg.getActiveGem().id == -1){ sg.getActiveGem().id = sign_jsons(unique_ids); } sObj.put("active", sg.getActiveGem().id); sObj.put("fromGroupLevel", sg.getFromGroupLevel()); sObj.put("untilGroupLevel", sg.getUntilGroupLevel()); sObj.put("replaceGroup", sg.replaceGroup()); sObj.put("replacesGroup", sg.replacesGroup()); if(sg.replaceGroup()){ if(sg.getGroupReplaced().id == -1){ sg.getGroupReplaced().id = sign_jsons(unique_ids); } sObj.put("socketGroupReplace", sg.getGroupReplaced().id); } if(sg.replacesGroup()){ if(sg.getGroupThatReplaces().id == -1){ sg.getGroupThatReplaces().id = sign_jsons(unique_ids); } sObj.put("socketGroupThatReplaces", sg.getGroupThatReplaces().id); } sObj.put("note",sg.getNote()); JSONArray gems_array = new JSONArray(); for(Gem g : sg.getGems()){ JSONObject gObj = new JSONObject(); if(g.id == -1){ g.id = sign_jsons(unique_ids); } gObj.put("id",g.id); gObj.put("name", g.getGemName()); gObj.put("level_added", g.getLevelAdded()); gObj.put("replaced", g.replaced); gObj.put("replaces", g.replaces); if(g.replaced){ if(g.replacedWith.id == -1){ g.replacedWith.id = sign_jsons(unique_ids); } gObj.put("replaceWith", g.replacedWith.id); } if(g.replaces){ if(g.replacesGem.id == -1){ g.replacesGem.id = sign_jsons(unique_ids); } gObj.put("replacesGem", g.replacesGem.id); } gems_array.put(gObj); } sObj.put("gem", gems_array); socket_group_array.put(sObj); } //now we need to connect data System.out.println("Saving build " + build.getName() + " .. success"); builds_array.put(bObj); } public boolean loadBuildsFromPastebin(String rawPaste){ //pseudo for loop loads builds and panels put them //into buildlinker and add buildlinker to the list //TODO: remember to sign the build with the static method ArrayList<Build> buildsLoaded = new ArrayList<>(); byte[] byteValueBase64Decoded = null; try{ byteValueBase64Decoded = Base64.getDecoder().decode(rawPaste.trim()); }catch(java.lang.IllegalArgumentException e){ System.out.println("Seems you try to load a POB pastebin."); e.printStackTrace(); return false; } catch(Exception e){ System.out.println("Seems you try to load a POB pastebin."); e.printStackTrace(); return false; } String stringValueBase64Decoded = new String(byteValueBase64Decoded); // System.out.println(stringValueBase64Decoded); //JSONArray obj = new JsonParser().parse(stringValueBase64Encoded).getAsJsonArray(); try{ JSONArray builds_array = new JSONArray(stringValueBase64Decoded); for (int i = 0; i < builds_array.length(); i++) { JSONObject bObj = builds_array.getJSONObject(i); String ascName = ""; if(bObj.getString("ascendancyName").equals("Assasin")){ System.err.println("Replaced assassin typo when loading build from external source."); ascName = "Assassin"; }else{ ascName = bObj.getString("ascendancyName"); } Build build = new Build( bObj.getString("buildName"), bObj.getString("className"), ascName ); //when importing character name will not be passed. build.setCharacterName(""); build.setCharacterLevel(0); try{ //bObj.get("hasPob"); build.isValid = bObj.getBoolean("isValid"); }catch(org.json.JSONException e){ //if it doesnt exist prob its valid from earlier versions build.isValid = true; } try{ //bObj.get("hasPob"); build.hasPob = bObj.getBoolean("hasPob"); if(build.hasPob){ build.pobLink = bObj.getString("pobLink"); } }catch(org.json.JSONException e){ build.hasPob = false; build.pobLink = ""; } GemHolder.getInstance().className = build.getClassName(); JSONArray socket_group_array = bObj.getJSONArray("socketGroup"); //build.level for (int j = 0; j < socket_group_array.length(); j++) { JSONObject sObj = socket_group_array.getJSONObject(j); SocketGroup sg = new SocketGroup(); sg.id = sObj.getInt("id"); sg.setFromGroupLevel(sObj.getInt("fromGroupLevel")); sg.setUntilGroupLevel(sObj.getInt("untilGroupLevel")); sg.setReplaceGroup(sObj.getBoolean("replaceGroup")); sg.setReplacesGroup(sObj.getBoolean("replacesGroup")); if(sg.replaceGroup()){ int id_replace = sObj.getInt("socketGroupReplace"); sg.id_replace = id_replace; } if(sg.replacesGroup()){ int id_replaces = sObj.getInt("socketGroupThatReplaces"); sg.id_replaces = id_replaces; } int active_id = sObj.getInt("active"); sg.active_id = active_id; try{ //bObj.get("hasPob"); sg.addNote(sObj.getString("note")); }catch(org.json.JSONException e){ sg.addNote(""); } JSONArray gems_array = sObj.getJSONArray("gem"); for(int k = 0 ; k<gems_array.length(); k++ ){ JSONObject gObj = gems_array.getJSONObject(k); String gemName = gObj.getString("name"); if(gemName.equals("Detonate Mines")){ System.out.println(); } Gem gem = GemHolder.getInstance().createGemFromCache(gemName,build.getClassName()); if(gem == null){ System.out.println(); } gem.id = gObj.getInt("id"); gem.level_added = gObj.getInt("level_added"); gem.replaced = gObj.getBoolean("replaced"); gem.replaces = gObj.getBoolean("replaces"); if(gem.replaced){ int id_replaced = gObj.getInt("replaceWith"); gem.id_replaced = id_replaced; } if(gem.replaces){ int id_replaces = gObj.getInt("replacesGem"); gem.id_replaces = id_replaces; } sg.getGems().add(gem);//***check line 324 in GemsPanel_Controller; } build.getSocketGroup().add(sg); } //update data links for(SocketGroup sg : build.getSocketGroup()){ if(sg.active_id!=-1){ for(Gem g : sg.getGems()){ if(g.id == sg.active_id){ sg.setActiveGem(g); break; } } } //this is super simple because the action is super complex and i will prob fuck up //i could potentially save a lot of search time but im bad if(sg.replaceGroup()){ for(SocketGroup sg1 : build.getSocketGroup()){ if(sg.id_replace == sg1.id){ sg.setGroupReplaced(sg1); break; } } } if(sg.replacesGroup()){ for(SocketGroup sg1 : build.getSocketGroup()){ if(sg.id_replaces == sg1.id){ sg.setGroupThatReplaces(sg1); break; } } } for(Gem g : sg.getGems()){ //if g active id != -1 if(g.replaces){ for(Gem g1 : sg.getGems()){ if(g1.id == g.id_replaces){ g.replacesGem = g1; break; } } } if(g.replaced){ for(Gem g1: sg.getGems()){ if(g1.id == g.id_replaced){ g.replacedWith = g1; break; } } } } } buildsLoaded.add(build); } }catch(Exception e){ m_logger.log(Level.WARNING, "Exception while parsing POB response.", e); return false; } for(Build bl : buildsLoaded){ loadNewBuild(bl); POELevelFx.buildsLoaded.add(bl); //this line is extra and not included in the method above } return true; } public Build addNewBuildFromPOB(String buildName, String className, String ascendancyName, ArrayList<ArrayList<String>> info){ //addNewBuild(buildName,className,ascendancyName); Build pobBuild = new Build(buildName,className,ascendancyName); //make empty build pobBuild.setCharacterName(""); pobBuild.setCharacterLevel(0); //main app controller will take care of pob link and hasPob tag //actually later i discovered that this brings a new bug //if u set it no non valid that means that during validation this build will be "accepted" //and be passed to write to memory, but then since it didnt passed the validation in the first place //it may have null values, most likely in sg.getactivegem. until then set it to true. //pobBuild.isValid = false; //obv needs checking after import from pob. pobBuild.isValid = true; //obv needs checking after import from pob. GemHolder.getInstance().className = pobBuild.getClassName(); for(ArrayList<String> sgList : info){ SocketGroup sg = new SocketGroup(); for( String gemString : sgList){ Gem gem = GemHolder.getInstance().createGemFromCache(gemString,pobBuild.getClassName()); if(gem == null) { System.err.println(gemString); continue; } if(gem.isActive && sg.getActiveGem() == null){ sg.setActiveGem(gem); } sg.getGems().add(gem);//***check line 324 in GemsPanel_Controller; } if(!sg.getGems().isEmpty()){ pobBuild.getSocketGroup().add(sg); if(sg.getActiveGem() == null){ sg.setActiveGem(sg.getGems().get(0)); } } } //but we also need to link the build to the build panel //we do this by load method loadNewBuild(pobBuild); POELevelFx.buildsLoaded.add(pobBuild); //add reference to build list. return pobBuild; } public void addNewBuild(String buildName, String className, String ascendancyName){ BuildLinker bl = new BuildLinker(); int linker_id = bl.hook(this); linker.put(linker_id,bl); removeBuild_button.setDisable(false); FXMLLoader loader = new FXMLLoader(getClass().getResource("buildEntry.fxml")); try { //this will add the AnchorPane to the VBox //buildsBox.getChildren().add(loader.load()); buildsBox.getItems().add(loader.load()); } catch (IOException ex) { Logger.getLogger(MainApp_Controller.class.getName()).log(Level.SEVERE, null, ex); } bl.pec = loader.<BuildEntry_Controller>getController(); //add controller to the linker class bl.pec.init(Util.charToImage(className,ascendancyName), buildName, ascendancyName, bl); bl.build = new Build(buildName,className,ascendancyName); //every new build should be considered non-valid? //bl.build.isValid = false; bl.build.isValid = true; //actually later i discovered that this brings a new bug //if u set it no non valid that means that during validation this build will be "accepted" //and be passed to write to memory, but then since it didnt passed the validation in the first place //it may have null values, most likely in sg.getactivegem. until then set it to true. //also setting it to valid will cause it to be put through the validation process. bl.build.hasPob = false; bl.build.pobLink = ""; for(SocketGroup sg : bl.build.getSocketGroup()){ SocketGroupLinker sgl = sgc.new SocketGroupLinker(sg); //sgl.sg = sg; //sgl.generateLabel(); bl.sgl_list.add(sgl); } POELevelFx.buildsLoaded.add(bl.build); root.toggleAllBuilds(true); } public void loadNewBuild(Build build){ BuildLinker bl = new BuildLinker(); int linker_id = bl.hook(this); linker.put(linker_id,bl); removeBuild_button.setDisable(false); FXMLLoader loader = new FXMLLoader(getClass().getResource("buildEntry.fxml")); try { //this will add the AnchorPane to the VBox //buildsBox.getChildren().add(loader.load()); buildsBox.getItems().add(loader.load()); } catch (IOException ex) { Logger.getLogger(MainApp_Controller.class.getName()).log(Level.SEVERE, null, ex); } bl.pec = loader.<BuildEntry_Controller>getController(); //add controller to the linker class bl.pec.init(Util.charToImage(build.getClassName(),build.getAsc()) , build.getName(), build.getAsc(), bl); if(!build.isValid) bl.pec.initInvalidBackgroundColor(); bl.build = build; //bl.build.isValid = true; for(SocketGroup sg : bl.build.getSocketGroup()){ SocketGroupLinker sgl = sgc.new SocketGroupLinker(sg); //sgl.sg = sg; //sgl.generateLabel(); bl.sgl_list.add(sgl); } root.toggleAllBuilds(true); } @FXML private void deleteBuild(){ Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Build delete"); alert.setHeaderText("You are about to delete Build : "+ linker.get(activeBuildID).build.getName()); alert.setContentText("Are you ok with this?"); Optional<ButtonType> result = alert.showAndWait(); if (result.get() == ButtonType.OK){ BuildLinker bl = linker.get(activeBuildID); if(!bl.build.isValid){ root.toggleFooterVisibility(true); } POELevelFx.buildsLoaded.remove(bl.build); sgc.reset(); //buildsBox.getChildren().remove(bl.pec.getRoot()); // remove from the UI buildsBox.getItems().remove(bl.pec.getRoot()); // remove from the UI buildsBox.getSelectionModel().clearSelection(); linker.remove(bl.id); //remove from data root.toggleActiveBuilds(false); if(POELevelFx.buildsLoaded.isEmpty()){ root.toggleAllBuilds(false); } } // and also remove from file system? } private void updateBuildValidationBanner(int id){ BuildLinker bl = linker.get(id); bl.pec.setValidBackgroundColor(bl.build.isValid , id==activeBuildID); root.toggleFooterVisibility(bl.build.isValid); } public void update(int id){ if(id!=activeBuildID){ activeBuildID = id; root.toggleActiveBuilds(true); for (BuildLinker bl : linker.values()) { if(bl.id!=id){ bl.pec.reset(bl.build.isValid); }else{ //update the sgroup controller with new build info GemHolder.getInstance().className = getCurrentClass(id); sgc.hookBuild_Controller(this); sgc.update(bl.sgl_list); root.toggleFooterVisibility(bl.build.isValid); } } //root.buildChanged(id); } } public ArrayList<SocketGroup> getSocketGroups(int id){ BuildLinker bl = linker.get(id); return bl.build.getSocketGroup(); } public String getCurrentClass(int id){ BuildLinker bl = linker.get(id); return bl.build.getClassName(); } /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { // TODO linker = new HashMap<>(); //loadBuildsFromMemory(); buildsBox.getSelectionModel().getSelectedItems().addListener(new ListChangeListener() { @Override public void onChanged(Change change) { boolean added = false; boolean removed = false; while (change.next()) { if (change.wasAdded()) { added = true; } else if (change.wasRemoved()) { removed = true; } } removeBuild_button.setDisable(removed && !added); root.setValidateBuildDisabled(removed && !added); } }); } }