public class AuthMe extends JavaPlugin {

    private DataSource database;
    private Settings settings;
    private Utils utils;
    private Messages m;
	public Management management;
    public static Server server;
    public static Permission permission;
	private static AuthMe instance;
    

    @Override
    public void onEnable() {
    	instance = this;
        /*
         *  Metric part from Hidendra Stats
         */
        try {
        Metrics metrics = new Metrics();
        metrics.beginMeasuringPlugin(this);
            } catch (IOException e) {
            // Failed to submit the stats :-(
        }
         
        settings = new Settings(this);
        settings.loadConfigOptions();
        
        m = Messages.getInstance();
        
        server = getServer();
        
        /*
         *  Back style on start if avaible
         */
        if(Settings.isBackupActivated && Settings.isBackupOnStart) {
        Boolean Backup = new PerformBackup().PerformBackup();
        if(Backup) ConsoleLogger.info("Backup Complete");
            else ConsoleLogger.showError("Error while making Backup");
        }
        
        /*
         * Backend MYSQL - FILE - SQLITE
         */
        switch (settings.getDataSource) {
            case FILE:
                try {
                    database = new FileDataSource();
                } catch (IOException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                }
                break;
            case MYSQL:
                try {
                    database = new MySQLDataSource();
                } catch (ClassNotFoundException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                } catch (SQLException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                } catch(TimeoutException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                }
                break;
            case SQLITE:
                try {
                     database = new SqliteDataSource();
                } catch (ClassNotFoundException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                } catch (SQLException ex) {
                    ConsoleLogger.showError(ex.getMessage());
                    this.getServer().getPluginManager().disablePlugin(this);
                    return;
                }
                break;
        }

        if (settings.isCachingEnabled) {
            database = new CacheDataSource(database);
        }
        
        management =  new Management(database);
        
        PluginManager pm = getServer().getPluginManager();
        pm.registerEvents(new AuthMePlayerListener(this,database),this);
        pm.registerEvents(new AuthMeBlockListener(database),this);
        pm.registerEvents(new AuthMeEntityListener(database),this);
        if (pm.isPluginEnabled("Spout")) 
        	pm.registerEvents(new AuthMeSpoutListener(database),this);
        
        //Find Permissions
        if(settings.isPermissionCheckEnabled) {
        RegisteredServiceProvider<Permission> permissionProvider =
                getServer().getServicesManager().getRegistration(net.milkbowl.vault.permission.Permission.class);
        if (permissionProvider != null)
            permission = permissionProvider.getProvider();
        else {
            
            ConsoleLogger.showError("Vault and Permissions plugins is needed for enable AuthMe Reloaded!");
            this.getServer().getPluginManager().disablePlugin(this);   
            }
        }
        
        //System.out.println("[debug perm]"+permission);
        
        this.getCommand("authme").setExecutor(new AdminCommand(database));
        this.getCommand("register").setExecutor(new RegisterCommand(database));
        this.getCommand("login").setExecutor(new LoginCommand());
        this.getCommand("changepassword").setExecutor(new ChangePasswordCommand(database));
        this.getCommand("logout").setExecutor(new LogoutCommand(this,database));
        this.getCommand("unregister").setExecutor(new UnregisterCommand(this, database));
        this.getCommand("passpartu").setExecutor(new PasspartuCommand(database));

        
        //
        // Check for correct sintax in config file!
        //
        
        if(!settings.isForceSingleSessionEnabled) {
            ConsoleLogger.info("ATTENTION by disabling ForceSingleSession Your server protection is set to low");
        }
        
        //onReload(this.getServer().getOnlinePlayers());
        ConsoleLogger.info("Authme " + this.getDescription().getVersion() + " enabled");
    }

    @Override
    public void onDisable() {
        if (database != null) {
            database.close();
        }
        //utils = Utils.getInstance();
        
        /*
         *  Back style on start if avaible
         */
        if(Settings.isBackupActivated && Settings.isBackupOnStop) {
        Boolean Backup = new PerformBackup().PerformBackup();
        if(Backup) ConsoleLogger.info("Backup Complete");
            else ConsoleLogger.showError("Error while making Backup");
        }       
        ConsoleLogger.info("Authme " + this.getDescription().getVersion() + " disabled");
        
        
    }

    private void onReload(Player[] players) {
      ConsoleLogger.showError("AuthMe dont support /reload command yet, please use /authme relaod");
        return;
         /*for (Player player : players) {
            String name = player.getName().toLowerCase();
            String ip = player.getAddress().getAddress().getHostAddress();

            boolean authAvail = database.isAuthAvailable(name);

            if (authAvail) {
                if (settings.isSessionsEnabled) {
                    PlayerAuth auth = database.getAuth(name);
                    if (auth.getNickname().equals(name) && auth.getIp().equals(ip)) {
                        PlayerCache.getInstance().addPlayer(auth);
                        player.sendMessage(m._("valid_session"));
                        break;
                    }
                }
            } else if (!settings.isForcedRegistrationEnabled) {
                break;
            } else if (settings.isKickNonRegisteredEnabled) {
                player.kickPlayer(m._("reg_only"));
                break;
            }

            LimboCache.getInstance().addLimboPlayer(player);
            player.getInventory().setArmorContents(new ItemStack[0]);
            player.getInventory().setContents(new ItemStack[36]);

            if (settings.isTeleportToSpawnEnabled) {
                player.teleport(player.getWorld().getSpawnLocation());
            }

            String msg = authAvail ? m._("login_msg") : m._("reg_msg");
            int time = settings.getRegistrationTimeout * 20;
            int msgInterval = settings.getWarnMessageInterval;
            BukkitScheduler sched = this.getServer().getScheduler();
            if (time != 0) {
                int id = sched.scheduleSyncDelayedTask(this, new TimeoutTask(this, name), time);
                LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id);
            }
            sched.scheduleSyncDelayedTask(this, new MessageTask(this, name, msg, msgInterval));
        } 
          * */
    }
    
	public static AuthMe getInstance() {
		return instance;
	}
   
}
 public class DataFileCache {


	private ItemStack[] inventory;
	private ItemStack[] armor;	
        private String group;
        private boolean operator;

	public DataFileCache(ItemStack[] inventory, ItemStack[] armor){
		this.inventory = inventory;
		this.armor = armor;
                
	}

	public DataFileCache(ItemStack[] inventory, ItemStack[] armor, String group, boolean operator){
		this.inventory = inventory;
		this.armor = armor;
                this.group = group;
                this.operator = operator;
	}        
	public ItemStack[] getInventory(){
		return inventory;
	}

	public ItemStack[] getArmour(){
		return armor;
	}
        
	public String getGroup(){
		return group;
	}
        
	public Boolean getOperator(){
                return operator;
	}         
}    
 public class FileCache {
        //private HashMap<Enchantment, Integer> ench;
            
        
	public FileCache() {
		final File folder = new File("cache");
		if (!folder.exists()) {
			folder.mkdirs();
		}
	}

	public void createCache(String playername, DataFileCache playerData, String group, boolean operator) {
		final File file = new File("cache/" + playername
				+ ".cache");

		if (file.exists()) {
			return;
		}

		FileWriter writer = null;
		try {
			file.createNewFile();

			writer = new FileWriter(file);
                        
                        // put player group in cache
                        // put if player is an op or not 1: is op 0: isnet op!
                        // line format Group|OperatorStatus
                        
                        if(operator)
                            writer.write(group+";1\r\n");
                        else writer.write(group+";0\r\n");
                        
                        writer.flush();
                        
			ItemStack[] invstack = playerData.getInventory();

			for (int i = 0; i < invstack.length; i++) {

				int itemid = 0;
				int amount = 0;
				int durability = 0;
                                String enchList = "";
                                //ench = new HashMap<Enchantment, Integer>();

				if (invstack[i] != null) {
					itemid = invstack[i].getTypeId();
					amount = invstack[i].getAmount();
					durability = invstack[i].getDurability();
                                                   
                                        
                                                for(Enchantment e : invstack[i].getEnchantments().keySet())
                                                 {
                                                    //System.out.println("enchant "+e.getName()+" bog "+invstack[i].getEnchantmentLevel(e));
                                                    enchList = enchList.concat(e.getName()+":"+invstack[i].getEnchantmentLevel(e)+":");
                                                    //System.out.println(enchList);
                                                    
                                                 }
                                        }
                                    
				writer.write("i" + ":" + itemid + ":" + amount + ":"
						+ durability + ":"+ enchList + "\r\n");
				writer.flush();
			}

			ItemStack[] armorstack = playerData.getArmour();

			for (int i = 0; i < armorstack.length; i++) {
				int itemid = 0;
				int amount = 0;
				int durability = 0;
                                String enchList = "";
                                
				if (armorstack[i] != null) {
					itemid = armorstack[i].getTypeId();
					amount = armorstack[i].getAmount();
					durability = armorstack[i].getDurability();
                                        
                                                for(Enchantment e : armorstack[i].getEnchantments().keySet())
                                                 {
                                                    //System.out.println("enchant "+e.getName()+" bog "+armorstack[i].getEnchantmentLevel(e));
                                                    enchList = enchList.concat(e.getName()+":"+armorstack[i].getEnchantmentLevel(e)+":");
                                                    //System.out.println(enchList);
                                                    
                                                 }                                        
				}

				writer.write("w" + ":" + itemid + ":" + amount + ":"
						+ durability + ":"+ enchList + "\r\n");
				writer.flush();
			}

			writer.close();
		} catch (final Exception e) {
			e.printStackTrace();
		}
	}

	public DataFileCache readCache(String playername) {
		final File file = new File("cache/" + playername
				+ ".cache");

		ItemStack[] stacki = new ItemStack[36];
		ItemStack[] stacka = new ItemStack[4];
                String group = null;
                boolean op = false;
                
		if (!file.exists()) {
			return new DataFileCache(stacki, stacka);
		}

		Scanner reader = null;
		try {
			reader = new Scanner(file);

			int i = 0;
			int a = 0;
			while (reader.hasNextLine()) {
				final String line = reader.nextLine();

				if (!line.contains(":")) {
                                   // the fist line rapresent the player group and operator status
                                    final String[] playerInfo = line.split(";");
                                    group = playerInfo[0];
                                    
                                        if (Integer.parseInt(playerInfo[1]) == 1) {
                                            op = true;
                                        } else op = false;
                                        
                                    continue;
				}

				final String[] in = line.split(":");

				/*if (in.length != 4) {
					continue;
				} */
                                
				if (!in[0].equals("i") && !in[0].equals("w")) {
					continue;
				}
                        // can enchant item? size ofstring in file - 4  all / 2 = number of enchant
				if (in[0].equals("i")) {
                                    Enchantment e = null;  
                                    
					stacki[i] = new ItemStack(Integer.parseInt(in[1]),
							Integer.parseInt(in[2]), Short.parseShort((in[3])));
					// qui c'e' un problema serio!
                                        if(in.length > 4 && !in[4].isEmpty()) {
                                             for(int k=4;k<in.length-1;k++) {
                                                //System.out.println("enchant "+in[k]);
                                                stacki[i].addUnsafeEnchantment(e.getByName(in[k]) ,Integer.parseInt(in[k+1]));
                                                k++;
                                             }
                                        }
                                        i++;
				} else {
                                    Enchantment e = null;  
					stacka[a] = new ItemStack(Integer.parseInt(in[1]),
							Integer.parseInt(in[2]), Short.parseShort((in[3])));
                                        if(in.length > 4 && !in[4].isEmpty()) {
                                           for(int k=4;k<in.length-1;k++) {
                                                //System.out.println("enchant "+in[k]);
                                                stacka[a].addUnsafeEnchantment(e.getByName(in[k]) ,Integer.parseInt(in[k+1]));
                                                k++;
                                           }
                                        }
					a++;
				}

			}
		} catch (final Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				reader.close();
			}
		}
		return new DataFileCache(stacki, stacka, group, op);
	}

	public void removeCache(String playername) {
		final File file = new File("cache/" + playername
				+ ".cache");

		if (file.exists()) {
			file.delete();
		}
	}

	public boolean doesCacheExist(String playername) {
		final File file = new File("cache/" + playername
				+ ".cache");

		if (file.exists()) {
			return true;
		}
		return false;
	}

    
}
 public class Utils {
     //private Settings settings = Settings.getInstance();
     private Player player;
     private String currentGroup;
     private static Utils singleton;
     private String unLoggedGroup = Settings.getUnloggedinGroup;
  /*   
  public Utils(Player player) {
      this.player = player;
      
  }
  */
  public void setGroup(Player player, groupType group) {
    if (!player.isOnline())
        return;
    if(!Settings.isPermissionCheckEnabled)
        return;
    
        switch(group) {
            case UNREGISTERED: {
                currentGroup = AuthMe.permission.getPrimaryGroup(player);
                AuthMe.permission.playerRemoveGroup(player, currentGroup);
                AuthMe.permission.playerAddGroup(player, Settings.unRegisteredGroup);
                break;
            }
            case REGISTERED: {
                currentGroup = AuthMe.permission.getPrimaryGroup(player);
                AuthMe.permission.playerRemoveGroup(player, currentGroup);
                AuthMe.permission.playerAddGroup(player, Settings.getRegisteredGroup);
                break;
            }                
        }
        return;
    }
  
    public String removeAll(Player player) {
        if(!Utils.getInstance().useGroupSystem()){
            return null;
        }
        
        /*if (AuthMe.permission.playerAdd(this.player,"-*") ) {
            AuthMe.permission.playerAdd(this.player,"authme.login");
            return true;
        }*/
        
        
        //System.out.println("permissions? "+ hasPerm);
        if( !Settings.getJoinPermissions.isEmpty() ) {
            hasPermOnJoin(player);
        }
        
        this.currentGroup = AuthMe.permission.getPrimaryGroup(player.getWorld(),player.getName().toString());
        //System.out.println("current grop" + currentGroup);
        if(AuthMe.permission.playerRemoveGroup(player.getWorld(),player.getName().toString(), currentGroup) && AuthMe.permission.playerAddGroup(player.getWorld(),player.getName().toString(),this.unLoggedGroup)) {
            
            return currentGroup;
        }
        
        return null;
        
    }
    
    public boolean addNormal(Player player, String group) {
       if(!Utils.getInstance().useGroupSystem()){
            return false;
        }
       // System.out.println("in add normal");
       /* if (AuthMe.permission.playerRemove(this.player, "-*"))
            return true;
       */      
        if(AuthMe.permission.playerRemoveGroup(player.getWorld(),player.getName().toString(),this.unLoggedGroup) && AuthMe.permission.playerAddGroup(player.getWorld(),player.getName().toString(),group)) {
        //System.out.println("vecchio "+this.unLoggedGroup+ "nuovo" + group);
            return true;
        
        }
        return false;
    }    

    private String hasPermOnJoin(Player player) {
       /* if(Settings.getJoinPermissions.isEmpty())
            return null; */
              Iterator<String> iter = Settings.getJoinPermissions.iterator();
                while (iter.hasNext()) {
                    String permission = iter.next();
                 // System.out.println("permissions? "+ permission);
                     
                   if(AuthMe.permission.playerHas(player, permission)){
                     //  System.out.println("player has permissions " +permission);
                       AuthMe.permission.playerAddTransient(player, permission);
                   }
                }
           return null;
    }
    
    public boolean isUnrestricted(Player player) {
        
        
        if(Settings.getUnrestrictedName.isEmpty() || Settings.getUnrestrictedName == null)
            return false;
        
     //   System.out.println("name to escape "+player.getName());
        if(Settings.getUnrestrictedName.contains(player.getName())) {
       //     System.out.println("name to escape correctly"+player.getName());
            return true;
        }
        
        return false;
        
         
    }
     public static Utils getInstance() {
        
            singleton = new Utils();
        
        return singleton;
    } 
    
    private boolean useGroupSystem() {
        
        if(Settings.isPermissionCheckEnabled && !Settings.getUnloggedinGroup.isEmpty()) {
            return true;
        } return false;
            
    }
    
    /*
     * Random Token for passpartu
     * 
     */
    public boolean obtainToken() {
        File file = new File("plugins/AuthMe/passpartu.token");

	if (file.exists())
            file.delete();

		FileWriter writer = null;
		try {
			file.createNewFile();
			writer = new FileWriter(file);
                        String token = generateToken();
                        writer.write(token+":"+System.currentTimeMillis()/1000+"\r\n");
                        writer.flush();
                        System.out.println("[AuthMe] Security passpartu token: "+ token);
                        writer.close();
                        return true;
                } catch(Exception e) {
                    e.printStackTrace(); 
                }
                
        
        return false;
    }
    
    /*
     * Read Toekn
     */
    public boolean readToken(String inputToken) {
        File file = new File("plugins/AuthMe/passpartu.token");
        
	if (!file.exists()) 	
            return false;
        
        if (inputToken.isEmpty() )
            return false;
        
		Scanner reader = null;
		try {
			reader = new Scanner(file);

			while (reader.hasNextLine()) {
				final String line = reader.nextLine();

				if (line.contains(":")) {   
                                   String[] tokenInfo = line.split(":");
                                   //System.err.println("Authme input token "+inputToken+" saved token "+tokenInfo[0]);
                                   //System.err.println("Authme time "+System.currentTimeMillis()/1000+"saved time "+Integer.parseInt(tokenInfo[1]));
                                   if(tokenInfo[0].equals(inputToken) && System.currentTimeMillis()/1000-30 <= Integer.parseInt(tokenInfo[1]) ) { 
                                       file.delete();
                                       reader.close();
                                       return true;
                                   }
                                } 
                        }
                } catch(Exception e) {
                    e.printStackTrace(); 
                }
                
	reader.close();        
        return false;
    }
    /*
     * Generate Random Token
     */
    private String generateToken() {
           // obtain new random token 
           Random rnd = new Random ();
            char[] arr = new char[5];

            for (int i=0; i<5; i++) {
                    int n = rnd.nextInt (36);
                    arr[i] = (char) (n < 10 ? '0'+n : 'a'+n-10);
            }
            
            return new String(arr);        
    }
    public enum groupType {
        UNREGISTERED, REGISTERED, NOTLOGGEDIN, LOGGEDIN
    }
    
}
 public class PlayerAuth {

    private String nickname;
    private String hash;
    private String ip;
    private long lastLogin;
    private int x,y,z;
    private String salt = "";
    private String vBhash = null;
    private int groupId;

    public PlayerAuth(String nickname, String hash, String ip, long lastLogin) {
        this.nickname = nickname;
        this.hash = hash;
        this.ip = ip;
        this.lastLogin = lastLogin;
        
    }
    
    public PlayerAuth(String nickname, int x, int y, int z) {
        this.nickname = nickname;
        this.x = x;
        this.y = y;
        this.z = z;
    }
    
    public PlayerAuth(String nickname, String hash, String ip, long lastLogin, int x, int y, int z) {
        this.nickname = nickname;
        this.hash = hash;
        this.ip = ip;
        this.lastLogin = lastLogin;
        this.x = x;
        this.y = y;
        this.z = z;   
    }
    
    //
    // This constructor is needed for Vbulletin board Auth!
    //
    public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip, long lastLogin, int x, int y, int z) {
        this.nickname = nickname;
        this.hash = hash;
        this.ip = ip;
        this.lastLogin = lastLogin;
        this.x = x;
        this.y = y;
        this.z = z;   
        this.salt = salt;
        this.groupId = groupId;
        //System.out.println("[Authme Debug] password hashed from db"+hash);
        //System.out.println("[Authme Debug] salt from db"+salt);        
    }
    
    public String getIp() {
        return ip;
    }

    public String getNickname() {
        return nickname;
    }

    public String getHash() {
        if(!salt.isEmpty())
            // Compose Vbullettin Hash System!
            return this.vBhash = "$MD5vb$"+salt+"$"+hash;
        else
            return hash;
    }
    
    //
    // GroupId for unactivated User on Vbullettin Board
    //
    public int getGroupId() {
        return groupId;
    }
    
    public int getQuitLocX() {
        return x;
    }
    public int getQuitLocY() {
        return y;
    }
    public int getQuitLocZ() {
        return z;
    }
    public void setQuitLocX(int x) {
        this.x = x;
    }
    public void setQuitLocY(int y) {
        this.y = y;
    }
    public void setQuitLocZ(int z) {
        this.z = z;
    }  
    public long getLastLogin() {
        return lastLogin;
    }

    public void setHash(String hash) {
        this.hash = hash;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public void setLastLogin(long lastLogin) {
        this.lastLogin = lastLogin;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof PlayerAuth)) {
            return false;
        }
        PlayerAuth other = (PlayerAuth) obj;
        
        return other.getIp().equals(this.ip) && other.getNickname().equals(this.nickname);
    }

    @Override
    public int hashCode() {
        int hashCode = 7;
        hashCode = 71 * hashCode + (this.nickname != null ? this.nickname.hashCode() : 0);
        hashCode = 71 * hashCode + (this.ip != null ? this.ip.hashCode() : 0);
        return hashCode;
    }
}
 public class LimboPlayer {

    private String name;
    private ItemStack[] inventory;
    private ItemStack[] armour;
    private Location loc;
    private int timeoutTaskId = -1;
    private int gameMode = 0;
    private boolean operator;
    private String group;

    public LimboPlayer(String name, Location loc, ItemStack[] inventory, ItemStack[] armour, int gameMode, boolean operator, String group) {
        this.name = name;
        this.loc = loc;
        this.inventory = inventory;
        this.armour = armour;
        this.gameMode = gameMode;
        this.operator = operator;
        this.group = group;
        //System.out.println("il gruppo in limboPlayer "+group);
    }
    
    public LimboPlayer(String name, String group) {
        this.name = name;
        this.group = group;
    }
    
    public String getName() {
        return name;
    }

    public Location getLoc() {
        return loc;
    }

    public ItemStack[] getArmour() {
        return armour;
    }

    public ItemStack[] getInventory() {
        return inventory;
    }

    public int getTimeoutTaskId() {
        return timeoutTaskId;
    }

    public void setTimeoutTaskId(int timeoutTaskId) {
        this.timeoutTaskId = timeoutTaskId;
    }

    public int getGameMode() {
        return gameMode;
    }
    
    public boolean getOperator() {
        return operator;
    }
    
    public String getGroup() {
        return group;
    }    
}
 public class LimboCache {

    private static LimboCache singleton = null;
    private HashMap<String, LimboPlayer> cache;
   //private Settings settings = Settings.getInstance();
    private FileCache playerData = new FileCache();
    
    private LimboCache() {
        this.cache = new HashMap<String, LimboPlayer>();
    }

    public void addLimboPlayer(Player player) {
        String name = player.getName().toLowerCase();
        Location loc = player.getLocation();
        int gameMode = player.getGameMode().getValue();
        ItemStack[] arm;
        ItemStack[] inv;
        boolean operator;
        String playerGroup = "";
        
        if (playerData.doesCacheExist(name)) {
            //DataFileCache playerInvArmor =  playerData.readCache(name);    
             inv =  playerData.readCache(name).getInventory();
             arm =  playerData.readCache(name).getArmour();
             playerGroup = playerData.readCache(name).getGroup();
             operator = playerData.readCache(name).getOperator();
        } else {
        inv =  player.getInventory().getContents();
        arm =  player.getInventory().getArmorContents();
            
        // check if player is an operator, then save it to ram if cache dosent exist!
        
            if(player.isOp() ) {
                //System.out.println("player is an operator in limboCache");
                operator = true;
                }
                   else operator = false;      
        }

       
        
        if(Settings.isForceSurvivalModeEnabled) {
            if(Settings.isResetInventoryIfCreative && gameMode != 0 ) {
               player.sendMessage("Your inventory has been cleaned!");
               inv = new ItemStack[36];
               arm = new ItemStack[4];
            }
            gameMode = 0;
        } 
        if(player.isDead()) {
        	loc = player.getWorld().getSpawnLocation();
        }
        
        if(cache.containsKey(name) && playerGroup.isEmpty()) {
            //System.out.println("contiene il player "+name);
            LimboPlayer groupLimbo = cache.get(name);
            playerGroup = groupLimbo.getGroup();
        }
        
        cache.put(player.getName().toLowerCase(), new LimboPlayer(name, loc, inv, arm, gameMode, operator, playerGroup));
    //System.out.println("il gruppo in limboChace "+playerGroup);
    }
    
    public void addLimboPlayer(Player player, String group) {
        
        cache.put(player.getName().toLowerCase(), new LimboPlayer(player.getName().toLowerCase(), group));
   //System.out.println("il gruppo in limboChace "+group);
    }
    
    public void deleteLimboPlayer(String name) {
        cache.remove(name);
    }

    public LimboPlayer getLimboPlayer(String name) {
        return cache.get(name);
    }

    public boolean hasLimboPlayer(String name) {
        return cache.containsKey(name);
    }
    
    
    public static LimboCache getInstance() {
        if (singleton == null) {
            singleton = new LimboCache();
        }
        return singleton;
    }
}
 public class CitizensCommunicator {

    public static boolean isNPC(Entity player) {
        PluginManager pm = player.getServer().getPluginManager();
        Plugin plugin = pm.getPlugin("Citizens");
         
        if(plugin != null) {
            /*
             *  Checl the Citizens Version 1.1.6 or 2.0
             */
            String Ver = plugin.getDescription().getVersion();
            String[] args = Ver.split("\\.");
            
            if(args[0].equals("1")) 
                return CitizensManager.isNPC(player);
            else return CitizensAPI.getNPCRegistry().isNPC(player);
            /* old method
            try {
                if( Class.forName("net.citizensnpcs.api.CitizensManager") != null)
                    return CitizensManager.isNPC(player);
                else return CitizensAPI.getNPCManager().isNPC(player);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(CitizensCommunicator.class.getName()).log(Level.SEVERE, null, ex);
            }
             */
        }
        return false;
    }
}
 public interface DataSource {

    public enum DataSourceType {

        MYSQL, FILE, SQLITE
    }

    boolean isAuthAvailable(String user);

    PlayerAuth getAuth(String user);

    boolean saveAuth(PlayerAuth auth);

    boolean updateSession(PlayerAuth auth);

    boolean updatePassword(PlayerAuth auth);

    int purgeDatabase(long until);

    boolean removeAuth(String user);
   
    boolean updateQuitLoc(PlayerAuth auth);
    
    int getIps(String ip);
    
    void close();

    void reload();
}
 public abstract class CombatTagComunicator {
        static CombatTagApi combatApi;
        
	public CombatTagComunicator(Player player) {	
        if(player.getServer().getPluginManager().getPlugin("CombatTag") != null){
			combatApi = new CombatTagApi((CombatTag)player.getServer().getPluginManager().getPlugin("CombatTag")); 
		}
        }
	/**
	 * Checks to see if the player is in combat. The combat time can be configured by the server owner
	 * If the player has died while in combat the player is no longer considered in combat and as such will return false
	 * @param playerName
	 * @return true if player is in combat
	 */
	public abstract boolean isInCombat(String player);
	
         /**
	 * Checks to see if the player is in combat. The combat time can be configured by the server owner
	 * If the player has died while in combat the player is no longer considered in combat and as such will return false
	 * @param player
	 * @return true if player is in combat
	 */
        public abstract boolean isInCombat(Player player);

         /**
	 * Returns the time before the tag is over
	 *  -1 if the tag has expired
	 *  -2 if the player is not in combat
	 * @param player
	 * @return
	 */
	public abstract long getRemainingTagTime(String player);

         //(Implemented in 3.8)
         /**
	 * Returns if the entity is an NPC
	 * @param player
	 * @return true if the player is an NPC
	 */
        public static boolean isNPC(Entity player) {
            if(player.getServer().getPluginManager().getPlugin("CombatTag") != null){
			combatApi = new CombatTagApi((CombatTag)player.getServer().getPluginManager().getPlugin("CombatTag")); 
                        return combatApi.isNPC(player);
            } 

        return false;
        }
}
 public class Messages extends CustomConfiguration {

    private static Messages singleton = null;
    private HashMap<String, String> map;
    

    public Messages() {
        
        super(new File(Settings.MESSAGE_FILE+"_"+Settings.messagesLanguage+".yml"));
        loadDefaults();
        loadFile();
        singleton = this;
        
    }
    
    private void loadDefaults() {
        this.set("logged_in", "&cAlready logged in!");
        this.set("not_logged_in", "&cNot logged in!");
        this.set("reg_disabled", "&cRegistration is disabled");
        this.set("user_regged", "&cUsername already registered");
        this.set("usage_reg", "&cUsage: /register password ConfirmPassword");
        this.set("usage_log", "&cUsage: /login password");
        this.set("user_unknown", "&cUsername not registered");
        this.set("pwd_changed", "&cPassword changed!");
        this.set("reg_only", "Registered players only! Please visit http://example.com to register");
        this.set("valid_session", "&cSession login");
        this.set("login_msg", "&cPlease login with \"/login password\"");
        this.set("reg_msg", "&cPlease register with \"/register password ConfirmPassword\"");
        this.set("timeout", "Login Timeout");
        this.set("wrong_pwd", "&cWrong password");
        this.set("logout", "&cSuccessful logout");
        this.set("usage_unreg", "&cUsage: /unregister password");
        this.set("registered", "&cSuccessfully registered!");
        this.set("unregistered", "&cSuccessfully unregistered!");
        this.set("login", "&cSuccessful login!");
        this.set("no_perm", "&cNo Permission");
        this.set("same_nick", "Same nick is already playing");
        this.set("reg_voluntarily", "You can register your nickname with the server with the command \"/register password ConfirmPassword\"");
        this.set("reload", "Configuration and database has been reloaded");
        this.set("error", "An error ocurred; Please contact the admin");
        this.set("unknown_user", "User is not in database");
        this.set("unsafe_spawn","Your Quit location was unsafe, teleporting you to World Spawn");
        this.set("unvalid_session","Session Dataes doesnt corrispond Plaese wait the end of session");
        this.set("max_reg","You have Exeded the max number of Registration for your Account"); 
        this.set("password_error","Password doesnt match");
        this.set("pass_len","Your password dind't reach the minimum length or exeded the max length");
        this.set("vb_nonActiv","Your Account isent Activated yet check your Emails!");
        this.set("usage_changepassword", "Usage: /changepassword oldPassword newPassword");
        
    }

	private void loadFile() {
        this.load();
        this.save();
       
    }

    public String _(String msg) {
        String loc = this.getString(msg);
        if (loc != null) {
            return loc.replace("&", "\u00a7");
        }
        return msg;
    }
    
    
    public static Messages getInstance() {
        if (singleton == null) {
            singleton = new Messages();
        }        
        return singleton;
    }
       
}
 public final class Settings extends YamlConfiguration {

    public static final String PLUGIN_FOLDER = "./plugins/AuthMe";
    public static final String CACHE_FOLDER = Settings.PLUGIN_FOLDER + "/cache";
    public static final String AUTH_FILE = Settings.PLUGIN_FOLDER + "/auths.db";
    public static final String MESSAGE_FILE = Settings.PLUGIN_FOLDER + "/messages";
    public static final String SETTINGS_FILE = Settings.PLUGIN_FOLDER + "/config.yml";
    public static List<String> getJoinPermissions = null;
    public static List<String> getUnrestrictedName = null;
    private static List<String> getRestrictedIp;
   
    private int numSettings = 59;
    public final Plugin plugin;
    private final File file;    
    
    public static DataSourceType getDataSource;
    public static HashAlgorithm getPasswordHash;
    
    public static Boolean isPermissionCheckEnabled, isRegistrationEnabled, isForcedRegistrationEnabled,
            isTeleportToSpawnEnabled, isSessionsEnabled, isChatAllowed, isAllowRestrictedIp, 
            isMovementAllowed, isKickNonRegisteredEnabled, isForceSingleSessionEnabled,
            isForceSpawnLocOnJoinEnabled, isForceExactSpawnEnabled, isSaveQuitLocationEnabled,
            isForceSurvivalModeEnabled, isResetInventoryIfCreative, isCachingEnabled, isKickOnWrongPasswordEnabled,
            getEnablePasswordVerifier, protectInventoryBeforeLogInEnabled, isBackupActivated, isBackupOnStart,
            isBackupOnStop, enablePasspartu;
            
            
    public static String getNickRegex, getUnloggedinGroup, getMySQLHost, getMySQLPort, 
            getMySQLUsername, getMySQLPassword, getMySQLDatabase, getMySQLTablename, 
            getMySQLColumnName, getMySQLColumnPassword, getMySQLColumnIp, getMySQLColumnLastLogin,
            getMySQLColumnSalt, getMySQLColumnGroup, unRegisteredGroup, backupWindowsPath,
            getcUnrestrictedName, getRegisteredGroup, messagesLanguage;
            
    
    public static int getWarnMessageInterval, getSessionTimeout, getRegistrationTimeout, getMaxNickLength,
            getMinNickLength, getPasswordMinLen, getMovementRadius, getmaxRegPerIp, getNonActivatedGroup,
            passwordMaxLength;
                    
    protected static YamlConfiguration configFile;
    
   public Settings(Plugin plugin) {
        //super(new File(Settings.PLUGIN_FOLDER + "/config.yml"), this.plugin);
        this.file = new File(plugin.getDataFolder(),"config.yml");
        
        this.plugin = plugin;

              

        //options().indent(4); 
        // Override to always indent 4 spaces
        if(exists()) {
            load();         
         }
        else {
            loadDefaults(file.getName());
            load();
        }
        
        configFile = (YamlConfiguration) plugin.getConfig();
        
        //saveDefaults();
        
    }
   
   public void loadConfigOptions() {
       
        plugin.getLogger().info("Loading Configuration File...");
        
        mergeConfig();
        
        messagesLanguage = checkLang(configFile.getString("settings.messagesLanguage","en"));
        isPermissionCheckEnabled = configFile.getBoolean("permission.EnablePermissionCheck", false);
        isForcedRegistrationEnabled  = configFile.getBoolean("settings.registration.force", true);
        isRegistrationEnabled = configFile.getBoolean("settings.registration.enabled", true);
        isTeleportToSpawnEnabled = configFile.getBoolean("settings.restrictions.teleportUnAuthedToSpawn",false);
        getWarnMessageInterval = configFile.getInt("settings.registration.messageInterval",5);
        isSessionsEnabled = configFile.getBoolean("settings.sessions.enabled",false);
        getSessionTimeout = configFile.getInt("settings.sessions.timeout",10);
        getRegistrationTimeout = configFile.getInt("settings.restrictions.timeout",30);
        isChatAllowed = configFile.getBoolean("settings.restrictions.allowChat",false);
        getMaxNickLength = configFile.getInt("settings.restrictions.maxNicknameLength",20);
        getMinNickLength = configFile.getInt("settings.restrictions.minNicknameLength",3);
        getPasswordMinLen = configFile.getInt("settings.security.minPasswordLength",4);
        getNickRegex = configFile.getString("settings.restrictions.allowedNicknameCharacters","[a-zA-Z0-9_?]*");
        isAllowRestrictedIp = configFile.getBoolean("settings.restrictions.AllowRestrictedUser",false);
        getRestrictedIp = configFile.getStringList("settings.restrictions.AllowedRestrictedUser");
        isMovementAllowed = configFile.getBoolean("settings.restrictions.allowMovement",false);
        getMovementRadius = configFile.getInt("settings.restrictions.allowedMovementRadius",100);
        getJoinPermissions = configFile.getStringList("GroupOptions.Permissions.PermissionsOnJoin");
        isKickOnWrongPasswordEnabled = configFile.getBoolean("settings.restrictions.kickOnWrongPassword",false);
        isKickNonRegisteredEnabled = configFile.getBoolean("settings.restrictions.kickNonRegistered",false);
        isForceSingleSessionEnabled = configFile.getBoolean("settings.restrictions.ForceSingleSession",true);
        isForceSpawnLocOnJoinEnabled = configFile.getBoolean("settings.restrictions.ForceSpawnLocOnJoinEnabled",false);
        isSaveQuitLocationEnabled = configFile.getBoolean("settings.restrictions.SaveQuitLocation", false);
        isForceSurvivalModeEnabled = configFile.getBoolean("settings.GameMode.ForceSurvivalMode", false);
        isResetInventoryIfCreative = configFile.getBoolean("settings.GameMode.ResetInventotyIfCreative",false);
        getmaxRegPerIp = configFile.getInt("settings.restrictions.maxRegPerIp",1);
        getPasswordHash = getPasswordHash();
        getUnloggedinGroup = configFile.getString("settings.security.unLoggedinGroup","unLoggedInGroup");
        getDataSource = getDataSource();
        isCachingEnabled = configFile.getBoolean("DataSource.caching",true);
        getMySQLHost = configFile.getString("DataSource.mySQLHost","127.0.0.1");
        getMySQLPort = configFile.getString("DataSource.mySQLPort","3306");
        getMySQLUsername = configFile.getString("DataSource.mySQLUsername","authme");
        getMySQLPassword = configFile.getString("DataSource.mySQLPassword","12345");
        getMySQLDatabase = configFile.getString("DataSource.mySQLDatabase","authme");
        getMySQLTablename = configFile.getString("DataSource.mySQLTablename","authme");
        getMySQLColumnName = configFile.getString("DataSource.mySQLColumnName","username");
        getMySQLColumnPassword = configFile.getString("DataSource.mySQLColumnPassword","password");
        getMySQLColumnIp = configFile.getString("DataSource.mySQLColumnIp","ip");
        getMySQLColumnLastLogin = configFile.getString("DataSource.mySQLColumnLastLogin","lastlogin");
        getMySQLColumnSalt = configFile.getString("ExternalBoardOptions.mySQLColumnSalt");
        getMySQLColumnGroup = configFile.getString("ExternalBoardOptions.mySQLColumnGroup","");
        getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1);
        unRegisteredGroup = configFile.getString("GroupOptions.UnregisteredPlayerGroup","");
        getUnrestrictedName = configFile.getStringList("settings.unrestrictions.UnrestrictedName");
        getRegisteredGroup = configFile.getString("GroupOptions.RegisteredPlayerGroup","");
        getEnablePasswordVerifier = configFile.getBoolean("settings.restrictions.enablePasswordVerifier" , true);
        protectInventoryBeforeLogInEnabled = configFile.getBoolean("settings.restrictions.ProtectInventoryBeforeLogIn", true);
        passwordMaxLength = configFile.getInt("settings.security.passwordMaxLength", 20);
        isBackupActivated = configFile.getBoolean("BackupSystem.ActivateBackup",false);
        isBackupOnStart = configFile.getBoolean("BackupSystem.OnServerStart",false);
        isBackupOnStop = configFile.getBoolean("BackupSystem.OnServeStop",false);
        backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
        enablePasspartu = configFile.getBoolean("Passpartu.enablePasspartu",false);


        saveDefaults();
            
            //System.out.println("[AuthMe debug] Config " + getEnablePasswordVerifier.toString());
            //System.out.println("[AuthMe debug] Config " + getEnablePasswordVerifier.toString());

 
   }
   
   public static void reloadConfigOptions(YamlConfiguration newConfig) {
       configFile = newConfig;
              
       //plugin.getLogger().info("RELoading Configuration File...");
        messagesLanguage = checkLang(configFile.getString("settings.messagesLanguage","en"));
        isPermissionCheckEnabled = configFile.getBoolean("permission.EnablePermissionCheck", false);
        isForcedRegistrationEnabled = configFile.getBoolean("settings.registration.force", true);
        isRegistrationEnabled = configFile.getBoolean("settings.registration.enabled", true);
        isTeleportToSpawnEnabled = configFile.getBoolean("settings.restrictions.teleportUnAuthedToSpawn",false);
        getWarnMessageInterval = configFile.getInt("settings.registration.messageInterval",5);
        isSessionsEnabled = configFile.getBoolean("settings.sessions.enabled",false);
        getSessionTimeout = configFile.getInt("settings.sessions.timeout",10);
        getRegistrationTimeout = configFile.getInt("settings.restrictions.timeout",30);
        isChatAllowed = configFile.getBoolean("settings.restrictions.allowChat",false);
        getMaxNickLength = configFile.getInt("settings.restrictions.maxNicknameLength",20);
        getMinNickLength = configFile.getInt("settings.restrictions.minNicknameLength",3);
        getPasswordMinLen = configFile.getInt("settings.security.minPasswordLength",4);
        getNickRegex = configFile.getString("settings.restrictions.allowedNicknameCharacters","[a-zA-Z0-9_?]*");
        isAllowRestrictedIp = configFile.getBoolean("settings.restrictions.AllowRestrictedUser",false);
        getRestrictedIp = configFile.getStringList("settings.restrictions.AllowedRestrictedUser");
        isMovementAllowed = configFile.getBoolean("settings.restrictions.allowMovement",false);
        getMovementRadius = configFile.getInt("settings.restrictions.allowedMovementRadius",100);
        getJoinPermissions = configFile.getStringList("GroupOptions.Permissions.PermissionsOnJoin");
        isKickOnWrongPasswordEnabled = configFile.getBoolean("settings.restrictions.kickOnWrongPassword",false);
        isKickNonRegisteredEnabled = configFile.getBoolean("settings.restrictions.kickNonRegistered",false);
        isForceSingleSessionEnabled = configFile.getBoolean("settings.restrictions.ForceSingleSession",true);
        isForceSpawnLocOnJoinEnabled = configFile.getBoolean("settings.restrictions.ForceSpawnLocOnJoinEnabled",false);     
        isSaveQuitLocationEnabled = configFile.getBoolean("settings.restrictions.SaveQuitLocation",false);
        isForceSurvivalModeEnabled = configFile.getBoolean("settings.GameMode.ForceSurvivalMode",false);
        isResetInventoryIfCreative = configFile.getBoolean("settings.GameMode.ResetInventotyIfCreative",false);
        getmaxRegPerIp = configFile.getInt("settings.restrictions.maxRegPerIp",1);
        getPasswordHash = getPasswordHash();
        getUnloggedinGroup = configFile.getString("settings.security.unLoggedinGroup","unLoggedInGroup");
        getDataSource = getDataSource();
        isCachingEnabled = configFile.getBoolean("DataSource.caching",true);
        getMySQLHost = configFile.getString("DataSource.mySQLHost","127.0.0.1");
        getMySQLPort = configFile.getString("DataSource.mySQLPort","3306");
        getMySQLUsername = configFile.getString("DataSource.mySQLUsername","authme");
        getMySQLPassword = configFile.getString("DataSource.mySQLPassword","12345");
        getMySQLDatabase = configFile.getString("DataSource.mySQLDatabase","authme");
        getMySQLTablename = configFile.getString("DataSource.mySQLTablename","authme");
        getMySQLColumnName = configFile.getString("DataSource.mySQLColumnName","username");
        getMySQLColumnPassword = configFile.getString("DataSource.mySQLColumnPassword","password");
        getMySQLColumnIp = configFile.getString("DataSource.mySQLColumnIp","ip");
        getMySQLColumnLastLogin = configFile.getString("DataSource.mySQLColumnLastLogin","lastlogin");
        getMySQLColumnSalt = configFile.getString("ExternalBoardOptions.mySQLColumnSalt","");
        getMySQLColumnGroup = configFile.getString("ExternalBoardOptions.mySQLColumnGroup","");
        getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1);
        unRegisteredGroup = configFile.getString("GroupOptions.UnregisteredPlayerGroup","");
        getUnrestrictedName = configFile.getStringList("settings.unrestrictions.UnrestrictedName");
        getRegisteredGroup = configFile.getString("GroupOptions.RegisteredPlayerGroup",""); 
        getEnablePasswordVerifier = configFile.getBoolean("settings.restrictions.enablePasswordVerifier" , true);
        protectInventoryBeforeLogInEnabled = configFile.getBoolean("settings.restrictions.ProtectInventoryBeforeLogIn", true);
        passwordMaxLength = configFile.getInt("settings.security.passwordMaxLength", 20);
        isBackupActivated = configFile.getBoolean("BackupSystem.ActivateBackup",false);
        isBackupOnStart = configFile.getBoolean("BackupSystem.OnServerStart",false);
        isBackupOnStop = configFile.getBoolean("BackupSystem.OnServeStop",false);     
        backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
        enablePasspartu = configFile.getBoolean("Passpartu.enablePasspartu",false);
       
        //System.out.println(getMySQLDatabase);
        
         
   }
   
   public void mergeConfig() {
      
       //options().copyDefaults(false);
       //System.out.println("merging config?"+contains("settings.restrictions.ProtectInventoryBeforeLogIn")+checkDefaults());
       if(!contains("settings.restrictions.ProtectInventoryBeforeLogIn")) {
           set("settings.restrictions.enablePasswordVerifier", true);
           set("settings.restrictions.ProtectInventoryBeforeLogIn", true);
       } 
       
       if(!contains("settings.security.passwordMaxLength")) {
           set("settings.security.passwordMaxLength", 20);
       }
       
       if(!contains("BackupSystem.ActivateBackup")) {
           set("BackupSystem.ActivateBackup",false);
           set("BackupSystem.OnServerStart",false);
           set("BackupSystem.OnServeStop",false);
       }
       
       
       if(!contains("BackupSystem.MysqlWindowsPath")) {
           set("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
       }
       
       if(!contains("settings.messagesLanguage")) {
           set("settings.messagesLanguage","en");
       }
       
       if(!contains("passpartu.enablePasspartu")) {
           set("Passpartu.enablePasspartu",false);
       } else return;
       
       plugin.getLogger().info("Merge new Config Options..");
       plugin.saveConfig();
       
       return;
   }
   /** 
    * 
    * 
    * 
    */   
    private static HashAlgorithm getPasswordHash() {
        String key = "settings.security.passwordHash";

        try {
            return PasswordSecurity.HashAlgorithm.valueOf(configFile.getString(key,"SHA256").toUpperCase());
        } catch (IllegalArgumentException ex) {
            ConsoleLogger.showError("Unknown Hash Algorithm; defaulting to SHA256");
            return PasswordSecurity.HashAlgorithm.SHA256;
        }
    }
    
   /** 
    * 
    * 
    * 
    */
    private static DataSourceType getDataSource() {
        String key = "DataSource.backend";

        try {
            return DataSource.DataSourceType.valueOf(configFile.getString(key).toUpperCase());
        } catch (IllegalArgumentException ex) {
            ConsoleLogger.showError("Unknown database backend; defaulting to file database");
            return DataSource.DataSourceType.FILE;
        }
    }

    /**
     * Config option for setting and check restricted user by
     * username;ip , return false if ip and name doesnt amtch with
     * player that join the server, so player has a restricted access
    */   
    public static Boolean getRestrictedIp(String name, String ip) {
                 
              Iterator<String> iter = getRestrictedIp.iterator();
                while (iter.hasNext()) {
                   String[] args =  iter.next().split(";");
                  //System.out.println("name restricted "+args[0]+"name 2:"+name+"ip"+args[1]+"ip2"+ip);
                   if(args[0].equalsIgnoreCase(name) ) {
                           if(args[1].equalsIgnoreCase(ip)) {
                       //System.out.println("name restricted "+args[0]+"name 2:"+name+"ip"+args[1]+"ip2"+ip);
                           return true;
                            } else return false;
                        } 
                }
            return true;
    }

    
    /**
     * Loads the configuration from disk
     *
     * @return True if loaded successfully
     */
    public final boolean load() {
        try {
            load(file);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    
    public final void reload() {
        load();
        loadDefaults(file.getName());
    }

    /**
     * Saves the configuration to disk
     *
     * @return True if saved successfully
     */
    public final boolean save() {
        try {
            save(file);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * Simple function for if the Configuration file exists
     *
     * @return True if configuration exists on disk
     */
    public final boolean exists() {
        return file.exists();
    }

    /**
     * Loads a file from the plugin jar and sets as default
     *
     * @param filename The filename to open
     */
    public final void loadDefaults(String filename) {
        InputStream stream = plugin.getResource(filename);
        if(stream == null) return;

        setDefaults(YamlConfiguration.loadConfiguration(stream));
    }

    /**
     * Saves current configuration (plus defaults) to disk.
     *
     * If defaults and configuration are empty, saves blank file.
     *
     * @return True if saved successfully
     */
    public final boolean saveDefaults() {
        options().copyDefaults(true);
        options().copyHeader(true);
        boolean success = save();
        options().copyDefaults(false);
        options().copyHeader(false);

        return success;
    }


    /**
     * Clears current configuration defaults
     */
    public final void clearDefaults() {
        setDefaults(new MemoryConfiguration());
    }

    /**
* Check loaded defaults against current configuration
*
* @return false When all defaults aren't present in config
*/
    public boolean checkDefaults() {
        if (getDefaults() == null) {
            return true;
        }
        return getKeys(true).containsAll(getDefaults().getKeys(true));
    }
 /*   
    public static Settings getInstance() {
        if (singleton == null) {
            singleton = new Settings();
        }
        return singleton;
    }
*/
    public static String checkLang(String lang) {
        for(messagesLang language: messagesLang.values()) {
            //System.out.println(language.toString());
            if(lang.toLowerCase().contains(language.toString())) {
                ConsoleLogger.info("Set Language: "+lang);
                return lang;
            }    
        }
        ConsoleLogger.info("Set Default Language: En ");
        return "en";
    }
    
    public enum messagesLang {
        en, de, br, cz
    } 
}
 public class TimeoutTask implements Runnable {

    private JavaPlugin plugin;
    private String name;
    private Messages m = Messages.getInstance();
    private Utils utils = Utils.getInstance();
    private FileCache playerCache = new FileCache();

    public TimeoutTask(JavaPlugin plugin, String name) {
        this.plugin = plugin;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void run() {
        if (PlayerCache.getInstance().isAuthenticated(name)) {
            return;
        }

        for (Player player : plugin.getServer().getOnlinePlayers()) {
            if (player.getName().toLowerCase().equals(name)) {
                if (LimboCache.getInstance().hasLimboPlayer(name)) {
                    LimboPlayer inv = LimboCache.getInstance().getLimboPlayer(name);
                    player.getServer().getScheduler().cancelTask(inv.getTimeoutTaskId());
                    /*
                    player.getInventory().setArmorContents(inv.getArmour());
                    player.getInventory().setContents(inv.getInventory());
                    utils.addNormal(player, inv.getGroup());
                    player.setOp(inv.getOperator());
                    //System.out.println("debug timout group reset "+inv.getGroup());
                    LimboCache.getInstance().deleteLimboPlayer(name); */
                    if(playerCache.doesCacheExist(name)) {
                        playerCache.removeCache(name);
                    }   
                } 

                player.kickPlayer(m._("timeout"));
                break;
            }
        }
    }
}

import com.trc202.CombatTag.CombatTag;
import java.util.Date;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerPreLoginEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import uk.org.whoami.authme.AuthMe;
import uk.org.whoami.authme.cache.backup.DataFileCache;
import uk.org.whoami.authme.cache.backup.FileCache;
import uk.org.whoami.authme.Utils;
import uk.org.whoami.authme.cache.auth.PlayerAuth;
import uk.org.whoami.authme.cache.auth.PlayerCache;
import uk.org.whoami.authme.cache.limbo.LimboPlayer;
import uk.org.whoami.authme.cache.limbo.LimboCache;
import uk.org.whoami.authme.plugin.manager.CitizensCommunicator;
import uk.org.whoami.authme.datasource.DataSource;
import uk.org.whoami.authme.plugin.manager.CombatTagComunicator;
import uk.org.whoami.authme.settings.Messages;
import uk.org.whoami.authme.settings.Settings;
import uk.org.whoami.authme.task.MessageTask;
import uk.org.whoami.authme.task.TimeoutTask;

            event.disallow(Result.KICK_OTHER, "Your nickname contains illegal characters. Allowed chars: " + regex);
           // utils.addNormal(player, limbo.getGroup());
          //  LimboCache.getInstance().deleteLimboPlayer(name);
            return;
        }
 
       /* OLD METHOD 
        for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) {
            System.out.println("[Debug name 3] "+player.getName());
            if (onlinePlayer.getName().equalsIgnoreCase(player.getName())) {
                System.out.println("[Debug name 2] "+onlinePlayer.getName());
                player.kickPlayer(m._("same_nick"));
                
                event.disallow(PlayerLoginEvent.Result.KICK_OTHER, m._("same_nick"));
                return;
            }
        }
       */
        if (Settings.isKickNonRegisteredEnabled) {
            if (!data.isAuthAvailable(name)) {    
                event.disallow(Result.KICK_OTHER, m._("reg_only"));
                return;
            }
        }
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerJoin(PlayerJoinEvent event) {
        if (event.getPlayer() == null) {
            return;
        }

        Player player = event.getPlayer();
        String name = player.getName().toLowerCase();

       
        if (CitizensCommunicator.isNPC(player) || Utils.getInstance().isUnrestricted(player) || CombatTagComunicator.isNPC(player)) {
            return;
        }
        /* 
         * Why it has to return if a player is already authenticated? when should this happen?       
        if (PlayerCache.getInstance().isAuthenticated(name)) {      
            return;
        }
        */
        String ip = player.getAddress().getAddress().getHostAddress();
        //System.out.println("Debug Restricted: "+Settings.isAllowRestrictedIp.toString());
        // kick player that ip doesnt correspond to config.yml list!
            if(Settings.isAllowRestrictedIp && !Settings.getRestrictedIp(name, ip)) {
                player.kickPlayer("You are not the Owner of this account, please try another name!");
                return;           
                }         
        
        if (data.isAuthAvailable(name)) {    
       
            
            if (Settings.isSessionsEnabled) {
                PlayerAuth auth = data.getAuth(name);
                long timeout = Settings.getSessionTimeout * 60000;
                long lastLogin = auth.getLastLogin();
                long cur = new Date().getTime();

            //
            // TODO: rewrite how session work!
            //
             if((cur - lastLogin < timeout || timeout == 0) && !auth.getIp().equals("198.18.0.1") ) {
                if (auth.getNickname().equalsIgnoreCase(name) && auth.getIp().equals(ip) ) {
                  //  System.out.println("[Debug same name] "+auth.getNickname()+ "  "+name);
                  //  System.out.println("[Debug same ip] "+auth.getIp()+ "  "+ip);
                    PlayerCache.getInstance().addPlayer(auth);
                    player.sendMessage(m._("valid_session"));
                    return;
                } else {          
                    player.kickPlayer(m._("unvalid_session"));
                    return;
                }
            } else {
                 // TODO:
                 // Reset player data when session is ended, possible usses is, that player change ip
                 // and session isent ended he is kick out for invalid session, even if he is the rigth
                 // player
                PlayerCache.getInstance().removePlayer(name);
                LimboCache.getInstance().addLimboPlayer(player , utils.removeAll(player));
                //LimboCache.getInstance().addLimboPlayer(player);
                }
          } 
          // isent in session or session was ended correctly
          LimboCache.getInstance().addLimboPlayer(player);
          DataFileCache playerData = new DataFileCache(player.getInventory().getContents(),player.getInventory().getArmorContents());      
          playerBackup.createCache(name, playerData, LimboCache.getInstance().getLimboPlayer(name).getGroup(),LimboCache.getInstance().getLimboPlayer(name).getOperator());                      
        } else {  
            if(!Settings.unRegisteredGroup.isEmpty()){
               utils.setGroup(player, Utils.groupType.UNREGISTERED);
            }
            if (!Settings.isForcedRegistrationEnabled) {
                return;
            }
        }



        if(Settings.protectInventoryBeforeLogInEnabled) {
            player.getInventory().setArmorContents(new ItemStack[4]);
            player.getInventory().setContents(new ItemStack[36]);
        }
        
        player.setGameMode(GameMode.SURVIVAL);
        
        if(player.isOp()) 
            player.setOp(false);

        if (Settings.isTeleportToSpawnEnabled || Settings.isForceSpawnLocOnJoinEnabled) {
            player.teleport(player.getWorld().getSpawnLocation());  
        }

        String msg = data.isAuthAvailable(name) ? m._("login_msg") : m._("reg_msg");
        int time = Settings.getRegistrationTimeout * 20;
        int msgInterval = Settings.getWarnMessageInterval;
        BukkitScheduler sched = plugin.getServer().getScheduler();
        if (time != 0) {

            int id = sched.scheduleSyncDelayedTask(plugin, new TimeoutTask(plugin, name), time);
