/*
 * 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.sun.javafx.application.LauncherImpl;
import java.awt.AWTException;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.*;
//import java.util.Base64;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.javafx.css.StyleManager;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.application.Preloader;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.input.KeyCombination;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import poe.level.PreloadNotification.GemDownloadNotification;
import poe.level.data.*;
import poe.level.data.Gem.Info;
import poe.level.keybinds.GlobalKeyListener;

/**
 *
 * @author Christos
 */
public class POELevelFx extends Application {
    //search for public void start(Stage stage) throws Exception for css

    /***************************************************************************
     * Change to true if a build is being pushed to the master branch for public release.
     ***************************************************************************/
    public static final boolean MASTER_RELEASE = true;
    /*************************************************************************************/
    /* Update this when you are pushing a new release version, must match the GitHub release name!
    **************************************************************************************/
    public static final String version = "v0.74-beta";


    public static boolean DEBUG = true;

    private static final String DEBUG_BRANCH_NAME = "development";
    private static final String RELEASE_BRANCH_NAME = "master";
    public static String BRANCH_NAME = MASTER_RELEASE ? RELEASE_BRANCH_NAME : DEBUG_BRANCH_NAME;
    private static final String REPO_OWNER = "karakasis";
    public static String directory;
    public static String gemsJSONFileName;
    public static String gemsTimeFileName;
    public static String dataJSONFileName;
    public static String dataTimeFileName;
    public static String gemsIconsLocation;
    public static ArrayList<Build> buildsLoaded;
    private static final Logger m_logger = Logger.getLogger(POELevelFx.class.getName());
    private Stage zone;
    private Stage exp;
    private Stage main;
    private Stage editor;
    private Stage leveling;
    private static GithubHelper.ReleaseInfo newReleaseInfo = null;
    private static boolean is_new_version;
    //v0.5-alpha <- between

    public void update() {
        assert (newReleaseInfo != null);
        URL url;

        String outFileName = "PathOfLeveling-" + newReleaseInfo.version + ".jar";
        try {
            url = new URL(newReleaseInfo.downloadURL);
            HttpURLConnection httpConnection = (HttpURLConnection) (url.openConnection());
            BufferedInputStream in = new java.io.BufferedInputStream(httpConnection.getInputStream());
            FileOutputStream fos = new java.io.FileOutputStream(outFileName);
            int bufferSize = 8192;
            BufferedOutputStream bout = new BufferedOutputStream(fos, bufferSize);
            byte[] data = new byte[bufferSize];
            long downloadedFileSize = 0;
            int x;
            while ((x = in.read(data, 0, bufferSize)) >= 0) {
                if (UpdaterController.cancelDownload) {
                    bout.close();
                    in.close();
                    File file = new File(outFileName);
                    file.delete();
                    try {
                        init();
                    } catch (Exception ex) {
                        Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    break;
                }
                downloadedFileSize += x;

                notifyPreloader(new UpdatePreloader.ProgressNotification(downloadedFileSize));


                //System.out.println(downloadedFileSize);
                bout.write(data, 0, x);
            }
            bout.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    private KeyCombination setKeybinds(Properties prop, String propertyName, String defaultValue){
        prop.setProperty(propertyName, defaultValue);
        KeyCombination keyCombination = null;
        try{
            keyCombination = KeyCombination.keyCombination(defaultValue);
            System.out.println("-POELevelFx- Setting keybind for : " + keyCombination.getName() +" for " + propertyName);
        }catch(Exception e){
            System.out.println("-POELevelFx- Setting keybind for :" + propertyName + " failed.");
            keyCombination = KeyCombination.NO_MATCH;
        }
        return keyCombination;
    }

    private KeyCombination loadKeybinds(Properties prop, String propertyName, String defaultValue){
        //check if hotkey is null, on older versions
        String loadProp = prop.getProperty(propertyName);
        KeyCombination keyCombination = null;
        //this should load the keybind on the controller but not overwrite
        if(loadProp == null){
            //loadProp = defaultValue; <- load default or
            return KeyCombination.NO_MATCH;
        }
        try{
            keyCombination = KeyCombination.keyCombination(loadProp);
            System.out.println("-POELevelFx- Loading keybind " + keyCombination.getName() +" for " + propertyName);
        }catch(Exception e){
            System.out.println("-POELevelFx- Loading keybind for " + propertyName+ " failed.");
            keyCombination = KeyCombination.NO_MATCH;
        }
        return keyCombination;
    }

    @Override
    public void init() throws Exception {

        boolean restart = false;
        setUpFonts();
        if(is_new_version){


            while(true) {
                if(UpdaterController.allowUpdate){
                    UpdaterController.allowUpdate = false;
                    update();
                    break;
                }
                if(UpdaterController.declUpdate){
                    is_new_version = false;
                    restart = true;
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ie) {
                    // ignore
                }
            }
            if(restart) {
                //LauncherImpl.launchApplication(POELevelFx.class, NewFXPreloader.class, null);
                init();
            }
            //System.exit(-10);
        }else{

            HashMap<String,String> hotkeyDefaults;
            //changed the default keys
            hotkeyDefaults = new HashMap<>();
            hotkeyDefaults.put("zones-hotkey-show_hide","F4");
            hotkeyDefaults.put("level-hotkey-remind","F5");
            hotkeyDefaults.put("recipe-hotkey-mark","Page Down");
            hotkeyDefaults.put("recipe-hotkey-preview","Page Up");
            hotkeyDefaults.put("level-hotkey-beta-next","Right");
            hotkeyDefaults.put("level-hotkey-beta-previous","Left");
            hotkeyDefaults.put("lock-keybinds","F12");

            if (!new File(POELevelFx.directory + "\\Path of Leveling\\config.properties").isFile()) {
            new File(POELevelFx.directory + "\\Path of Leveling\\config.properties").createNewFile();

            Properties prop = new Properties();
            OutputStream output = null;


            try {
                    output = new FileOutputStream(POELevelFx.directory + "\\Path of Leveling\\config.properties");

                    // set the properties value
                    prop.setProperty("zones-toggle", "true");
                    Preferences_Controller.zones_toggle = true;
                    prop.setProperty("zones-text-toggle", "true");
                    Preferences_Controller.zones_text_toggle = true;
                    prop.setProperty("zones-trial-toggle", "true");
                    Preferences_Controller.zones_trial_toggle = true;
                    prop.setProperty("zones-passive-toggle", "true");
                    Preferences_Controller.zones_passive_toggle = true;
                    prop.setProperty("zones-recipe-toggle", "true");
                    Preferences_Controller.zones_recipe_toggle = true;
                    prop.setProperty("zones-images-toggle", "true");
                    Preferences_Controller.zones_images_toggle = true;
                    prop.setProperty("gem-beta-UI-toggle", "true");
                    Preferences_Controller.gem_UI_toggle = true;

                    double a = 10;
                    prop.setProperty("zones-slider", String.valueOf(a));
                    Preferences_Controller.zones_slider = a;

                    a = 15;
                    Preferences_Controller.level_slider = a;
                    prop.setProperty("level-slider", String.valueOf(a));

                    prop.setProperty("poe-dir","");
                    Preferences_Controller.poe_log_dir = "";

                Preferences_Controller.zones_hotkey_show_hide_key = setKeybinds(
                            prop
                            ,"zones-hotkey-show_hide"
                            ,hotkeyDefaults.get("zones-hotkey-show_hide")
                    );
                Preferences_Controller.level_hotkey_remind_key = setKeybinds(
                            prop
                            ,"level-hotkey-remind"
                            ,hotkeyDefaults.get("level-hotkey-remind")
                    );
                Preferences_Controller.recipe_hotkey_mark_key = setKeybinds(
                            prop
                            ,"recipe-hotkey-mark"
                            ,hotkeyDefaults.get("recipe-hotkey-mark")
                    );
                Preferences_Controller.recipe_hotkey_preview_key = setKeybinds(
                            prop
                            ,"recipe-hotkey-preview"
                            ,hotkeyDefaults.get("recipe-hotkey-preview")
                    );
                Preferences_Controller.level_hotkey_beta_next_key = setKeybinds(
                            prop
                            ,"level-hotkey-beta-next"
                            ,hotkeyDefaults.get("level-hotkey-beta-next")
                    );
                Preferences_Controller.level_hotkey_beta_previous_key = setKeybinds(
                            prop
                            ,"level-hotkey-beta-previous"
                            ,hotkeyDefaults.get("level-hotkey-beta-previous")
                    );
                Preferences_Controller.lock_keybinds_hotkey_key = setKeybinds(
                        prop
                        ,"lock-keybinds"
                        ,hotkeyDefaults.get("lock-keybinds")
                );


                    //new changes

                    prop.setProperty("gems-overlay-pos", "-200.0,-200.0");
                    Preferences_Controller.updateGemsPos(-200.0, -200.0);
                    prop.setProperty("level-overlay-pos", "-200.0,-200.0");
                    Preferences_Controller.updateLevelPos(-200.0, -200.0);
                    prop.setProperty("zones-overlay-pos", "-200.0,-200.0");
                    Preferences_Controller.updateZonesPos(-200.0, -200.0);

                    // save properties to project root folder
                    prop.store(output, null);

                } catch (IOException io) {
                        io.printStackTrace();
                } finally {
                        if (output != null) {
                                try {
                                        output.close();
                                } catch (IOException e) {
                                        e.printStackTrace();
                                }
                        }

                }
            }else{
                    boolean patchKeybind = false;
                  Properties prop = new Properties();
                  InputStream input = null;
                  try {

                          input = new FileInputStream(POELevelFx.directory + "\\Path of Leveling\\config.properties");

                          // load a properties file
                          prop.load(input);
                          Preferences_Controller.zones_toggle = Boolean.parseBoolean(prop.getProperty("zones-toggle"));
                          Preferences_Controller.zones_text_toggle = Boolean.parseBoolean(prop.getProperty("zones-text-toggle"));
                          Preferences_Controller.zones_images_toggle = Boolean.parseBoolean(prop.getProperty("zones-images-toggle"));

                          //this 3 settings were added in later versions so im trying to avoid errors.
                          String parseRecipe = prop.getProperty("zones-recipe-toggle");
                          if(parseRecipe == null){
                              Preferences_Controller.zones_recipe_toggle = true; //default
                          }else{
                              Preferences_Controller.zones_recipe_toggle = Boolean.parseBoolean(prop.getProperty("zones-recipe-toggle"));
                          }
                          String parseTrial = prop.getProperty("zones-trial-toggle");
                          if(parseTrial == null){
                              Preferences_Controller.zones_trial_toggle = true; //default
                          }else{
                              Preferences_Controller.zones_trial_toggle = Boolean.parseBoolean(prop.getProperty("zones-trial-toggle"));
                          }
                          String parseGemUI = prop.getProperty("gem-beta-UI-toggle");
                          if(parseTrial == null){
                              Preferences_Controller.gem_UI_toggle = true; //default
                          }else{
                              Preferences_Controller.gem_UI_toggle = Boolean.parseBoolean(prop.getProperty("gem-beta-UI-toggle"));
                          }

                          Preferences_Controller.zones_passive_toggle = Boolean.parseBoolean(prop.getProperty("zones-passive-toggle"));

                          Preferences_Controller.zones_slider = Double.parseDouble(prop.getProperty("zones-slider"));
                          Preferences_Controller.level_slider = Double.parseDouble(prop.getProperty("level-slider"));

                          if(!(prop.getProperty("poe-dir")==null || prop.getProperty("poe-dir").equals(""))){
                              System.out.println("Selected Path of exile installation : " + prop.getProperty("poe-dir"));
                              Preferences_Controller.poe_log_dir = prop.getProperty("poe-dir") + "\\logs\\Client.txt";
                              System.out.println("Selected Path of exile log location : " + Preferences_Controller.poe_log_dir);
                          }else{
                              System.out.println("Path of exile log location is not set.");
                          }

                          //API
                          Preferences_Controller.poe_account_name = prop.getProperty("poe-account-name", "");

                            //new changes to overlay positions persist
                            //a bug is introduced at this point. users with older versions will not have
                            //those lines in their prop files and a null error will pop up
                            //quick fix if null add the line manually from code.
                            String[] zones_pos = null;
                            try{
                                zones_pos = prop.getProperty("zones-overlay-pos").toString().split(",");
                            }catch(NullPointerException e){
                                //hopefully the update..Pos method will write the values without crashing.
                                //my only issue is that i am already opening the prop file in read mode, and then from within
                                //im calling a write function.
                                System.out.println("zones-overlay-pos was missing from config.properties and is manually added.");
                                zones_pos= new String[2];
                                zones_pos[0] = "-200.0";
                                zones_pos[1] = "-200.0";
                            }
                            Preferences_Controller.updateZonesPos(Double.parseDouble(zones_pos[0])
                                    , Double.parseDouble(zones_pos[1]));

                            String[] level_pos = null;
                            try{
                                 level_pos = prop.getProperty("level-overlay-pos").toString().split(",");
                            }catch(NullPointerException e){
                                System.out.println("level-overlay-pos was missing from config.properties and is manually added.");
                                level_pos= new String[2];
                                level_pos[0] = "-200.0";
                                level_pos[1] = "-200.0";
                            }
                            Preferences_Controller.updateLevelPos(Double.parseDouble(level_pos[0])
                                    , Double.parseDouble(level_pos[1]));

                            String[] gem_pos = null;
                            try{
                                gem_pos = prop.getProperty("gems-overlay-pos").toString().split(",");
                            }catch(NullPointerException e){
                                System.out.println("gems-overlay-pos was missing from config.properties and is manually added.");
                                gem_pos= new String[2];
                                gem_pos[0] = "-200.0";
                                gem_pos[1] = "-200.0";
                            }
                            Preferences_Controller.updateGemsPos(Double.parseDouble(gem_pos[0])
                                    , Double.parseDouble(gem_pos[1]));

                      Preferences_Controller.zones_hotkey_show_hide_key = loadKeybinds(
                                prop
                                ,"zones-hotkey-show_hide"
                                ,hotkeyDefaults.get("zones-hotkey-show_hide")
                        );
                      Preferences_Controller.level_hotkey_remind_key = loadKeybinds(
                              prop
                              ,"level-hotkey-remind"
                              ,hotkeyDefaults.get("level-hotkey-remind")
                      );
                      Preferences_Controller.recipe_hotkey_mark_key = loadKeybinds(
                              prop
                              ,"recipe-hotkey-mark"
                              ,hotkeyDefaults.get("recipe-hotkey-mark")
                      );
                      Preferences_Controller.recipe_hotkey_preview_key = loadKeybinds(
                              prop
                              ,"recipe-hotkey-preview"
                              ,hotkeyDefaults.get("recipe-hotkey-preview")
                      );
                      Preferences_Controller.level_hotkey_beta_next_key = loadKeybinds(
                              prop
                              ,"level-hotkey-beta-next"
                              ,hotkeyDefaults.get("level-hotkey-beta-next")
                      );
                      Preferences_Controller.level_hotkey_beta_previous_key = loadKeybinds(
                              prop
                              ,"level-hotkey-beta-previous"
                              ,hotkeyDefaults.get("level-hotkey-beta-previous")
                      );
                      Preferences_Controller.lock_keybinds_hotkey_key = loadKeybinds(
                              prop
                              ,"lock-keybinds"
                              ,hotkeyDefaults.get("lock-keybinds")
                      );
                      if(prop.containsKey("level-hotkey-beta-next") && prop.containsKey("level-hotkey-beta-previous"))
                      if(prop.getProperty("level-hotkey-beta-next").equals("Left")
                       && prop.getProperty("level-hotkey-beta-previous").equals("Right")) {
                          patchKeybind = true;
                      }

                  } catch (IOException ex) {
                          ex.printStackTrace();
                  } finally {
                      if (input != null) {
                              try {
                                      input.close();
                              } catch (IOException e) {
                                      e.printStackTrace();
                              }
                      }
                  }
                  if(patchKeybind) {
                      OutputStream output = null;


                      try {
                          output = new FileOutputStream(POELevelFx.directory + "\\Path of Leveling\\config.properties");
                          prop.setProperty("level-hotkey-beta-next","Right");
                          prop.setProperty("level-hotkey-beta-previous","Left");
                          prop.store(output, null);
                      }    // save properties to project root folder
                      catch (IOException io) {
                          io.printStackTrace();
                      } finally {
                          if (output != null) {
                              try {
                                  output.close();
                              } catch (IOException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                  }

                }


            // Only check for new JSON files when running in release mode
            if (!DEBUG) {
                checkForNewJSON();
            }

            //StringBuilder raw = readRawToString();
            try {
                loadActsFromMemory();
                loadGemsFromMemory();
                //GemHolder.getInstance().init_remaining_in_pool();
                loadBuildsFromMemory();
                loadRecipesProperties();
            } catch (Exception e) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, "Error during Path of Leveling start up: " + e.getMessage(), e);
                notifyPreloader(new Preloader.ErrorNotification("init", "Error during Path of Leveling start up: " + e.getMessage(), e));

            }

        }



        /*
              ArrayList<String[]> mergeTags = mergeTags();
              ArrayList<Gem> gems = GemHolder.getInstance().gems;
              boolean active = false;
              for(int i=0; i<mergeTags.size(); i++){
                  if(mergeTags.get(i)[0].equals("Abyssal Cry")){
                      active = true;
                  }
                  boolean found = false;
                  for(int j=0; j<gems.size(); j++){
                      if(mergeTags.get(i)[0].equals(gems.get(j).name)){
                          gems.get(j).isActive = active;
                          gems.get(j).isSupport = !active;
                          for(int k = 1 ; k< mergeTags.get(i).length; k++){
                              gems.get(j).tags.add( mergeTags.get(i)[k]);
                          }
                          found = true;
                          break;
                      }
                  }
                  if(!found){
                      System.out.println("not found : " + mergeTags.get(i)[0]);
                  }
                  found = false;
              }

              ArrayList<Gem> gems2 = GemHolder.getInstance().gems;
              for(Gem g : gems2){
                  if(g.isActive == g.isSupport){
                      System.out.println("same active : " + g.name);

                  }
                  if(g.tags == null || g.tags.size() == 0){
                      System.out.println("no tag : " + g.name);
                  }
              }
              System.out.println(gems2);

              GemHolder.getInstance().init_remaining_in_pool();
              */

     /*
        StringBuilder hack = hack();
        String[] split = hack.toString().split("\r\n|\t");

        ArrayList<String> s = new ArrayList<>();
        ArrayList<ArrayList<String>> s_col = new ArrayList<>();
        GemHolder.getInstance().pool();
        for(int i=0; i<split.length;i++){
            String name = split[i];
            s.add(name.replace("\\", ""));
            if(name.equals("Act 6 after Fallen from Grace from Lilly Roth with any character.")){
                s_col.add(s);
                GemHolder.getInstance().updateGemInfo(s);
                s = new ArrayList<>();
            }

        }

        GemHolder.getInstance().init_remaining_in_pool();


        s_col.size();
        */
    }

    private StringBuilder readRawToString(){
        BufferedReader br = null;
        StringBuilder sb = null;
        try {
            br = new BufferedReader(new FileReader("raw.txt"));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            sb = new StringBuilder();
            String line = null;
            try {
                line = br.readLine();
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }

            while (line != null) {
                sb.append(line);
                sb.append(System.lineSeparator());
                try {
                    line = br.readLine();
                } catch (IOException ex) {
                    Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            String everything = sb.toString();
        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return sb;
    }

    private ArrayList<String[]> mergeTags(){
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("tags.txt"));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
        }
        ArrayList<String[]> list_tags = new ArrayList<>();
        try {
            String line = null;
            try {

                line = br.readLine();
                while(line != null){
                    String[] tags = line.split(",");
                    list_tags.add(tags);
                    line = br.readLine();
                }
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }

        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return list_tags;
    }

    public void close(){
        //exp.close();
        main.close();
        //zone.close();
    }

    private void loadRecipesProperties(){
        if (!new File(POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties").isFile()) {
            try {
                new File(POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties").createNewFile();
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }

            Properties prop = new Properties();
            OutputStream output = null;
            try{
                output = new FileOutputStream(POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties");
            } catch (FileNotFoundException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }

            for(Zone z : ActHandler.getInstance().getZonesWithRecipes()){
                String thanksGGG = z.name + " [L" + z.getZoneLevel() + "]";
                //System.out.println(thanksGGG);
                prop.setProperty(thanksGGG, "false");
                ActHandler.getInstance().recipeMap.put(z, false);
            }

            try {
                // save properties to project root folder
                prop.store(output, null);
                System.out.println("Recipe properties file created successfully in " + POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties");
            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                    if (output != null) {
                            try {
                                    output.close();
                            } catch (IOException e) {
                                    e.printStackTrace();
                            }
                    }

            }


        }else{
            Properties prop = new Properties();
            InputStream input = null;
            boolean writeStream = false;
            try {

                input = new FileInputStream(POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties");

                // load a properties file
                prop.load(input);
                for(Zone z : ActHandler.getInstance().getZonesWithRecipes()){
                    String thanksGGG = z.name + " [L" + z.getZoneLevel() + "]";
                    writeStream = writeStream || !prop.containsKey(thanksGGG);
                    String propVal = prop.getProperty(thanksGGG, "false");
                    prop.put(thanksGGG, propVal);
                    ActHandler.getInstance().recipeMap.put(z, Boolean.parseBoolean(propVal));
                }

            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                if (input != null) {
                    try {
                        input.close();
                        System.out.println("Recipe properties loaded successfully ");

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            if(writeStream){
                System.out.println("Need to update recipes");
                OutputStream output = null;
                try{
                    output = new FileOutputStream(POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties");
                    prop.store(output, null);
                    System.out.println("Recipe properties file PATCHED successfully in " + POELevelFx.directory + "\\Path of Leveling\\recipesFound.properties");
                } catch (IOException ex) {
                    Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    if (output != null) {
                        try {
                            output.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                }
            }

        }
    }

    private void loadActsFromMemory() throws IOException {
        InputStream in = new FileInputStream(dataJSONFileName);
        Scanner s = new Scanner(in).useDelimiter("\\A");
        String jsonstring = s.hasNext() ? s.next() : "";


        JSONObject obj = new JSONObject(jsonstring);
        JSONArray arr = obj.getJSONArray("acts");

        //ActHandler actsH = new ActHandler();
        for (int i = 0; i < arr.length(); i++)
        {
            JSONObject actobj  = arr.getJSONObject(i);
            String actname = actobj.getString("act");
            int actid = actobj.getInt("actid");
            JSONArray zonesAr = actobj.getJSONArray("zones");
            Act a = new Act(actid, actname);
            for(int j= 0; j<zonesAr.length();j++){
                JSONObject zoneObj = zonesAr.getJSONObject(j);
                ArrayList<String> zoneImages = new ArrayList<>();
                JSONArray zonesImagAr = zoneObj.getJSONArray("image");
                for(int k=0; k<zonesImagAr.length(); k++){
                    zoneImages.add(zonesImagAr.getString(k));
                }
                Zone z = new Zone(
                    zoneObj.getString("name"),
                    zoneObj.getInt("level"),
                    zoneImages,
                    zoneObj.getString("altimage"),
                    zoneObj.getString("note"),
                    zoneObj.getBoolean("haspassive"),
                    zoneObj.getBoolean("hastrial"),
                    zoneObj.getString("quest"),
                    zoneObj.getBoolean("questRewardsSkills"),
                    actname,actid
                );
                //manual zone recipe load
                z.hasRecipe = zoneObj.getBoolean("hasRecipe");
                if(z.hasRecipe){
                    //recipeInfo rInfo = z.new recipeInfo();
                    JSONObject recipeObj = zoneObj.getJSONObject("recipe");
                    if(recipeObj != null){
                        z.rInfo.tooltip = recipeObj.getString("tooltip");
                        /* // we wont be using this information so we might as well
                        //not use space for no reason.
                        JSONArray recipeMods = recipeObj.getJSONArray("mods");
                        rInfo.mods = new ArrayList<>();
                        for(int k=0; k<recipeMods.length(); k++){
                            rInfo.mods.add(recipeMods.getString(k));
                        }*/
                    }
                    ActHandler.getInstance().putZone(z);
                }
                a.putZone(z);
            }
            ActHandler.getInstance().putAct(a);

        }
        System.out.println("Zone data loaded.");
    }

    private void loadGemsFromMemory() throws FileNotFoundException {

        InputStream inG = new FileInputStream(gemsJSONFileName);
        Scanner sG = new Scanner(inG).useDelimiter("\\A");
        String jsonstringG = sG.hasNext() ? sG.next() : "";

        //JSONObject objG = new JSONObject(jsonstringG);
        JSONArray arrG = new JSONArray(jsonstringG);

        //GemHolder gemsH = new GemHolder();
        for (int i = 0; i < arrG.length(); i++)
        {
            JSONObject gemObj  = arrG.getJSONObject(i);
            Gem gem = new Gem(gemObj.getString("name"));

            gem.required_lvl= gemObj.getInt("required_lvl");
            gem.isVaal=gemObj.getBoolean("isVaal");
            //gem.id = gemObj.getInt("id");
            gem.id = -1;
            gem.color= gemObj.getString("color");
            gem.iconPath= gemObj.getString("iconPath");
            gem.isRewarded = gemObj.getBoolean("isReward");
            JSONArray alt_name = gemObj.optJSONArray("alt_name");
            if (alt_name != null) {
                gem.addAltNamesFromJSON(alt_name.iterator());
            }

            if(gem.isRewarded){
                JSONObject rewardObj  = gemObj.getJSONObject("reward");
                gem.reward = gem.new Info();
                try{
                    JSONArray chars = rewardObj.getJSONArray("available_to");
                    ArrayList<String> chars_list = new ArrayList<>();
                    for(int j=0;j<chars.length();j++){
                        chars_list.add(chars.getString(j));
                        //gem.putChar(chars.getString(j));

                    }
                    gem.reward.available_to = chars_list;
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    gem.reward.quest_name= rewardObj.getString("quest_name");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    gem.reward.npc= rewardObj.getString("npc");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    gem.reward.act = rewardObj.getInt("act");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    gem.reward.town= rewardObj.getString("town");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
            }
            JSONArray buy_arrays = gemObj.getJSONArray("buy");
            gem.buy = new ArrayList<>();
            for(int j=0;j<buy_arrays.length();j++){
                JSONObject buyObj  = buy_arrays.getJSONObject(j);
                Info buy_info = gem.new Info();
                try{
                    JSONArray chars = buyObj.getJSONArray("available_to");
                    ArrayList<String> chars_list = new ArrayList<>();
                    for(int k=0;k<chars.length();k++){
                        chars_list.add(chars.getString(k));
                        //gem.putChar(chars.getString(j));

                    }
                    buy_info.available_to = chars_list;
                }catch(JSONException e){
                    e.printStackTrace();
                    logErrorGem(gem.getGemName());
                }
                try{
                    buy_info.quest_name= buyObj.getString("quest_name");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    buy_info.npc= buyObj.getString("npc");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    buy_info.act = buyObj.getInt("act");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                try{
                    buy_info.town= buyObj.getString("town");
                }catch(JSONException e){
                    logErrorGem(gem.getGemName());
                }
                gem.buy.add(buy_info);
            }

            BufferedImage img;
            try {
                File gemFile = new File(gemsIconsLocation + gem.getGemName() + ".png");
                if (gemFile.exists()) {
                    img = ImageIO.read(gemFile);
                } else {
                    if (DEBUG) {
                        // If we're debugging, all icons should exist, since we're pointing at the local directory
                        notifyPreloader(new Preloader.ErrorNotification("loadGemsFromMemory", "Missing gem icon for: " + gem.getGemName() + "! Is your repo up to date?", null));
                    }
                    //TODO
                    notifyPreloader(new GemDownloadNotification(gem.getGemName()));
                    m_logger.info("Gem " + gemFile.getName() + " doesn't exist in " + gemsIconsLocation + " downloading");
                    img = downloadGemIcon(gem, true);
                    if (img == null) {
                        m_logger.info("Gem " + gemFile.getName() + " Failed to download from Github, trying " + gem.iconPath);
                        img = downloadGemIcon(gem, false);
                    }
                }
                if (img != null) {
                    gem.gemIcon = SwingFXUtils.toFXImage(img, null);
                    gem.resizeImage();
                } else {
                    m_logger.warning("Failed to get the gem icon for: " + gem.getGemName());
                    gem.gemIcon = null;
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

            //load tags - new feature
            gem.isActive = gemObj.getBoolean("isActive");
            gem.isSupport = gemObj.getBoolean("isSupport");
            JSONArray tags = gemObj.getJSONArray("gemTags");
            for(int j=0;j<tags.length();j++){
                gem.tags.add(tags.getString(j));
            }

            GemHolder.getInstance().putGem(gem);
            double a = (double)i/arrG.length();
            a = a * 100.0 ;

            notifyPreloader(new NewFXPreloader.ProgressNotification(a));
        }
        System.out.println("Gem data loaded");


    }

    private BufferedImage downloadGemIcon(Gem gem, boolean fromGithub) {
        BufferedImage image = null;
        try {
            URL url;
            if (fromGithub) {
                url = new URL("https://raw.githubusercontent.com/" + REPO_OWNER + "/Path-of-Leveling/" + BRANCH_NAME + "/gems/" + gem.getGemName().replaceAll(" ", "%20") + ".png");
            } else {
                url = new URL(gem.iconPath);
            }
            image = ImageIO.read(url);

            ImageIO.write(image, "png", new File(gemsIconsLocation + gem.getGemName() + ".png"));
        } catch (IOException e) {
            m_logger.log(Level.SEVERE, "IOException while read/writing the new gem icon", e);
        }
        return image;
    }


    private void logErrorGem(String gemName){
        System.out.println("Gem : " +gemName+ " had errors in loading.");
    }

    public static void reloadBuilds(){
        loadBuildsFromMemory();
    }

    private static void loadBuildsFromMemory(){
        //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
        buildsLoaded = new ArrayList<>();
        String stringValueBase64Encoded = "";
        if (new File(POELevelFx.directory+"\\Path of Leveling\\Builds\\builds.txt").exists()){
            BufferedReader br = null;
            FileReader fr = null;
            try {
                fr = new FileReader(POELevelFx.directory+"\\Path of Leveling\\Builds\\builds.txt");
                br = new BufferedReader(fr);
                stringValueBase64Encoded = br.readLine();


            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
            if (br != null)
                    br.close();
            if (fr != null)
                    fr.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            byte[] byteValueBase64Decoded = null;
            try{
            byteValueBase64Decoded = Base64.getDecoder().decode(stringValueBase64Encoded);
        }catch(java.lang.IllegalArgumentException e){
            e.printStackTrace();
            return;
        }
        catch(Exception e){
            e.printStackTrace();
            return;
        }
        //byte[] byteValueBase64Decoded = Base64.getDecoder().decode(stringValueBase64Encoded);
        String stringValueBase64Decoded = new String(byteValueBase64Decoded);

        //JSONArray obj = new JsonParser().parse(stringValueBase64Encoded).getAsJsonArray();
        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. poelevelfx load");
                    ascName = "Assassin";
                }else{
                    ascName = bObj.getString("ascendancyName");
                }
                Build build = new Build(
                        bObj.getString("buildName"),
                        bObj.getString("className"),
                        ascName
                );
                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;
                }
                build.setCharacterName(bObj.getString("characterName"));
                build.setCharacterLevel(bObj.getInt("level"));
                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"));
                    try{
                        //bObj.get("hasPob");
                        sg.addNote(sObj.getString("note"));
                    }catch(org.json.JSONException e){
                        sg.addNote("");
                    }
                    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;
                    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) {
                            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;
                        } else {
                            System.out.println("-POELevelFX- Trying to read"+gemName + " from GemHolder was a name mismatch for class "+build.getClassName());
                        }
                    }
                    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);
        }

        //System.out.println(stringValueBase64Encoded  + " when decoded is: " + stringValueBase64Decoded);
          System.out.println("Loaded builds successfully from " + POELevelFx.directory + "\\Path of Leveling\\Builds\\builds.txt");

        }
    }

    private static 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 static boolean saveBuildsToMemory(){

        JSONArray builds_array = new JSONArray();
        HashSet<Integer> unique_ids = new HashSet<>();
        for( Build build : buildsLoaded){
            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. poelevelfx issue");
            bObj.put("ascendancyName",build.getAsc());
            bObj.put("level", build.getCharacterLevel()); //<change
            bObj.put("characterName",build.getCharacterName());
            bObj.put("isValid", build.isValid);
            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());
                sObj.put("note",sg.getNote());
                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);
                }
                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


            builds_array.put(bObj);
        }

        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;
        boolean done = false;
        try {
            fw = new FileWriter(POELevelFx.directory+"\\Path of Leveling\\Builds\\builds.txt");
            bw = new BufferedWriter(fw);
            bw.write(stringValueBase64Encoded);
            System.out.println("Svaed builds to "+ POELevelFx.directory+"\\Path of Leveling\\Builds\\builds.txt");
            done = true;
        } catch (IOException e) {
            e.printStackTrace();
            done = false;
        } finally {
            try {
                if (bw != null)
                    bw.close();
                if (fw != null)
                    fw.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return done;
    }

    @Override
    public void start(Stage stage) throws Exception {
        Application.setUserAgentStylesheet(Application.STYLESHEET_MODENA);
        //StyleManager.getInstance().addUserAgentStylesheet(getClass().getResource("/styles/modena_dark.css").toExternalForm());
        StyleManager.getInstance().addUserAgentStylesheet(getClass().getResource("/styles/style.css").toExternalForm());

        if(is_new_version){
            new UpdaterStage();
        }else{
            main = new Main_Stage(this);
        }
        //zone = new ZoneOverlay_Stage();
        //exp = new LevelOverlay_Stage();
    }

    public void editor(){
        main.close();
        editor = new Editor_Stage(this);
        editor.setMaximized(true);
    }

    public void launcher(){
        editor.close();
        main = new Main_Stage(this);
    }

    public void start(boolean zone_b, boolean xp, boolean level){
        /*
        main.close();
        if(zone_b){
            this.zone = new ZoneOverlay_Stage();
        }
        if(xp){
            this.exp = new LevelOverlay_Stage();
        }
        if(level){
            this.leveling = new GemOverlay_Stage(Main_Stage.buildLoaded);
        }




        Platform.runLater(new Runnable(){


            @Override
            public void run() {
                Controller controller = new Controller(zone, exp, leveling, Main_Stage.buildLoaded);
            }
         });
        */
        main.close();
        Platform.runLater(new Runnable(){
                @Override
                public void run() {
                    //Hook.run();
                    GlobalKeyListener.run();
                }
        });
        controller = new Controller(zone_b, xp, level, Main_Stage.buildLoaded);

    }
    Controller controller;

    private void addTrayIcon() throws AWTException {
    final TrayIcon trayIcon = new TrayIcon(new ImageIcon(getClass().getResource("/icons/The_Explorer_card_art.png")).getImage(), "Path of Leveling");

    // Create a pop-up menu components
    final PopupMenu popup = new PopupMenu();
    final MenuItem shutdownItem = new MenuItem("Exit");
    final MenuItem settingsItem = new MenuItem("Settings");

    //Add components to pop-up menu
    popup.add(settingsItem);
    popup.add(shutdownItem);

    trayIcon.setPopupMenu(popup);
    trayIcon.setImageAutoSize(true); //So the icon auto-sizes

    SystemTray.getSystemTray().add(trayIcon);

    trayIcon.addMouseListener(new MouseAdapter() {
        @Override public void mouseClicked(java.awt.event.MouseEvent e) {
            if (e.getButton() == java.awt.event.MouseEvent.BUTTON1) { //Left click on tray icon (single click !)
                Platform.runLater(() -> {
                    //code to show things...
                });
            }
        }
    });

    shutdownItem.addActionListener(evnt -> {
        //code to exit the program
        //save stuff
        if(saveBuildsToMemory()){
            System.out.println("Successfully saved checkpoint");
        }else{
            System.out.println("Checkpoint save failed");
        }
        try {
                GlobalScreen.unregisterNativeHook();
        } catch (NativeHookException e1) {
                e1.printStackTrace();
        }
        System.exit(20);
    });

    settingsItem.addActionListener(evnt -> {
        //code to exit the program
        //save stuff
        if(controller!=null){
            controller.settings_event();
        }
    });
}


    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Negate the following if you want to test release mode
        // .gitignore shouldn't exist in the release environment, if it doesn't exist, then play nice, no debuggery.
        if (Files.exists(Paths.get("./.gitignore"))) {
            System.out.println("Detected that we're running in a development environment");
            DEBUG = true;
            BRANCH_NAME = DEBUG_BRANCH_NAME;
        } else {
            System.out.println("Running in release mode");
        }

        setUpDirectories();

        if (!DEBUG) {
            setUpLog();
            GithubHelper.checkRateLimited();
            if (checkForNewVersion()) {
                is_new_version = true;
                // New release info should NEVER be null here!
                assert (newReleaseInfo != null);
                UpdaterController.newReleaseInfo = newReleaseInfo;
                LauncherImpl.launchApplication(POELevelFx.class, UpdatePreloader.class, args);
            } else {
                is_new_version = false;
                LauncherImpl.launchApplication(POELevelFx.class, NewFXPreloader.class, args);
            }
        } else {
            is_new_version = false;
            LauncherImpl.launchApplication(POELevelFx.class, NewFXPreloader.class, args);
        }
    }

    public static void setUpDirectories(){
        POELevelFx.directory = new JFileChooser().getFileSystemView().getDefaultDirectory().toString();

        System.out.println(POELevelFx.directory + "\\Path of Leveling");
        if (!new File(POELevelFx.directory + "\\Path of Leveling").exists()) {
            new File(POELevelFx.directory + "\\Path of Leveling").mkdirs();
        }
        if (!new File(POELevelFx.directory + "\\Path of Leveling\\Builds").exists()) {
            new File(POELevelFx.directory + "\\Path of Leveling\\Builds").mkdirs();
        }
        createFileIfNotExists(POELevelFx.directory + "\\Path of Leveling\\settings.txt");
        createFileIfNotExists(POELevelFx.directory + "\\Path of Leveling\\log.txt");

        if (!DEBUG) {
            gemsJSONFileName = POELevelFx.directory + "\\Path of Leveling\\json\\gems.json";
            gemsTimeFileName = POELevelFx.directory + "\\Path of Leveling\\json\\gemsjson.time";
            dataJSONFileName = POELevelFx.directory + "\\Path of Leveling\\json\\data.json";
            dataTimeFileName = POELevelFx.directory + "\\Path of Leveling\\json\\datajson.time";
            if (!new File(POELevelFx.directory + "\\Path of Leveling\\json").exists()) {
                if (new File(POELevelFx.directory + "\\Path of Leveling\\json").mkdirs()) {
                    try {
                        Files.setAttribute(Paths.get(POELevelFx.directory + "\\Path of Leveling\\json"), "dos:hidden", true);
                    } catch (IOException ex) {
                        Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, "Exception while attempting to set json directory as hidden", ex);
                    }
                } else {
                    m_logger.severe("Failed to create JSON directory!");
                }
            }
            gemsIconsLocation = POELevelFx.directory + "\\Path of Leveling\\gems\\icons\\";
            if (!new File(gemsIconsLocation).exists()) {
                if (new File(gemsIconsLocation).mkdirs()) {
                    try {
                        Files.setAttribute(Paths.get(POELevelFx.directory + "\\Path of Leveling\\gems\\"), "dos:hidden", true);
                    } catch (IOException ex) {
                        Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, "Exception while attempting to set gems directory as hidden", ex);
                    }
                } else {
                    m_logger.severe("Failed to create gems directory!");
                }
            }
        } else {
            gemsJSONFileName = "json\\gems.json";
            gemsTimeFileName = "json\\gemsjson.time";
            dataJSONFileName = "json\\data.json";
            dataTimeFileName = "json\\datajson.time";
            gemsIconsLocation = "gems\\";
        }

    }

    private static boolean createFileIfNotExists(String path) {
        File filePath = new File(path);
        if (!filePath.isFile()) {
            try {
                return filePath.createNewFile();
            } catch (IOException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return false;
    }

    public void setUpFonts() throws Exception{
        Platform.setImplicitExit( false );
        addTrayIcon();
        Font.loadFont(POELevelFx.class.getResource("/fonts/Fontin-Regular.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/Fontin-Italic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/Fontin-Bold.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/Fontin-SmallCaps.ttf").toExternalForm(), 10);

        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Thin.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-ThinItalic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Regular.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-ThinItalic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-MediumItalic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Medium.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Light.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-LightItalic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Italic.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-Bold.ttf").toExternalForm(), 10);
        Font.loadFont(POELevelFx.class.getResource("/fonts/AlegreyaSansSC-BoldItalic.ttf").toExternalForm(), 10);
    }

    public static void setUpLog(){
        File f = new File(POELevelFx.directory + "\\Path of Leveling\\log.txt");
        if(f.length()>= 53287796 ){//that would be about 50mbs
            /* implement a good delete top N lines to empty some space.
            BufferedReader br = null;
            FileReader fr = null;
                ArrayList<String> restoffile = new ArrayList<>();
            try {

                fr = new FileReader(POELevelFx.directory + "\\Path of Leveling\\log.txt");
                br = new BufferedReader(fr);

                String sCurrentLine;

                br = new BufferedReader(fr);
                int counter = 0;
                while ((sCurrentLine = br.readLine()) != null) {
                    //System.out.println(sCurrentLine);
                    counter++;
                    if(counter >= 50000){
                        restoffile.add(sCurrentLine);
                        restoffile.
                        //System.out.println(restoffile.size());
                    }
                }

            } catch (IOException e) {

                e.printStackTrace();

            } finally {

                try {

                    if (br != null)
                        br.close();

                    if (fr != null)
                        fr.close();

                } catch (IOException ex) {

                    ex.printStackTrace();

                }

            }
            */ //until then just delete all.
            PrintWriter writer = null;
            try {
                writer = new PrintWriter(f);
            } catch (FileNotFoundException ex) {
                Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
            }
            writer.print("");
            writer.close();
        }
        PrintStream printStream= null;
        try {
            printStream = new PrintStream(new FileOutputStream(POELevelFx.directory + "\\Path of Leveling\\log.txt", true));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.setOut(printStream);
        System.setErr(printStream);
        //System.setOut(new PrintStream(f));
        System.out.println();
        System.out.println();
        System.out.println("=====new launch=====");
        System.out.println(new Timestamp(System.currentTimeMillis()).toString());
        System.out.println();
        System.out.println();
    }

    public static boolean checkForNewVersion(){
        GithubHelper.ReleaseInfo releaseInfo = GithubHelper.getLatestReleaseInfo();
        if (releaseInfo == null) {
            return false;
        }

        System.out.println("Current Version: " + POELevelFx.version);
        System.out.println("New Version: " + releaseInfo.version);
        if(!POELevelFx.version.equalsIgnoreCase(releaseInfo.version)){
            POELevelFx.newReleaseInfo = releaseInfo;
            return true;
        } else {
            return false;
        }
    }

    public void checkForNewJSON() {
        GithubHelper gh = new GithubHelper(REPO_OWNER, BRANCH_NAME);
        gh.init();
        File gemsJSONFile = new File(gemsJSONFileName);
        File gemsTimeFile = new File(gemsTimeFileName);
        try {
            gh.downloadGemsJsonFileIfNeeded(gemsJSONFile, gemsTimeFile);
        } catch (IOException e) {
            Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, "IOException trying to update gems.json", e);
        }
        File dataJSONFile = new File(dataJSONFileName);
        File dataTimeFile = new File(dataTimeFileName);
        try {
            gh.downloadDataJsonFileIfNeeded(dataJSONFile, dataTimeFile);
        } catch (IOException e) {
            Logger.getLogger(POELevelFx.class.getName()).log(Level.SEVERE, "IOException trying to update data.json", e);
        }
    }




}