package cn.citycraft.AuthMe.datasource;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import cn.citycraft.AuthMe.AuthMe;
import cn.citycraft.AuthMe.ConsoleLogger;
import cn.citycraft.AuthMe.cache.auth.PlayerAuth;
import cn.citycraft.AuthMe.cache.auth.PlayerCache;
import cn.citycraft.AuthMe.settings.Settings;

public class FlatFile implements DataSource {

	/*
	 * file layout:
	 *
	 * PLAYERNAME:HASHSUM:IP:LOGININMILLIESECONDS:LASTPOSX:LASTPOSY:LASTPOSZ:
	 * LASTPOSWORLD:EMAIL
	 *
	 * Old but compatible:
	 * PLAYERNAME:HASHSUM:IP:LOGININMILLIESECONDS:LASTPOSX:LASTPOSY
	 * :LASTPOSZ:LASTPOSWORLD PLAYERNAME:HASHSUM:IP:LOGININMILLIESECONDS
	 * PLAYERNAME:HASHSUM:IP PLAYERNAME:HASHSUM
	 */
	private final File source;

	public FlatFile() {
		source = Settings.AUTH_FILE;
		try {
			source.createNewFile();
		} catch (final IOException e) {
			ConsoleLogger.showError(e.getMessage());
			if (Settings.isStopEnabled) {
				ConsoleLogger.showError("无法读取用户数据文件... 关闭服务器...");
				AuthMe.getInstance().getServer().shutdown();
			}
			if (!Settings.isStopEnabled) {
				AuthMe.getInstance().getServer().getPluginManager().disablePlugin(AuthMe.getInstance());
			}
			e.printStackTrace();
		}
	}

	@Override
	public List<String> autoPurgeDatabase(final long until) {
		BufferedReader br = null;
		BufferedWriter bw = null;
		final ArrayList<String> lines = new ArrayList<>();
		final List<String> cleared = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length >= 4) {
					if (Long.parseLong(args[3]) >= until) {
						lines.add(line);
						continue;
					}
				}
				cleared.add(args[0]);
			}
			bw = new BufferedWriter(new FileWriter(source));
			for (final String l : lines) {
				bw.write(l + "\n");
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return cleared;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return cleared;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
			if (bw != null) {
				try {
					bw.close();
				} catch (final IOException ex) {
				}
			}
		}
		return cleared;
	}

	@Override
	public synchronized void close() {
	}

	@Override
	public int getAccountsRegistered() {
		BufferedReader br = null;
		int result = 0;
		try {
			br = new BufferedReader(new FileReader(source));
			while ((br.readLine()) != null) {
				result++;
			}
		} catch (final Exception ex) {
			ConsoleLogger.showError(ex.getMessage());
			return result;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		return result;
	}

	@Override
	public List<PlayerAuth> getAllAuths() {
		BufferedReader br = null;
		final List<PlayerAuth> auths = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				switch (args.length) {
				case 2:
					auths.add(new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]));
					break;
				case 3:
					auths.add(new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]));
					break;
				case 4:
					auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]));
					break;
				case 7:
					auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
							.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0]));
					break;
				case 8:
					auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
							.parseDouble(args[6]), args[7], "your@email.com", args[0]));
					break;
				case 9:
					auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
							.parseDouble(args[6]), args[7], args[8], args[0]));
					break;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return auths;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return auths;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		return auths;
	}

	@Override
	public List<String> getAllAuthsByEmail(final String email) {
		BufferedReader br = null;
		final List<String> countEmail = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 8 && args[8].equals(email)) {
					countEmail.add(args[0]);
				}
			}
			return countEmail;
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
	}

	@Override
	public List<String> getAllAuthsByIp(final String ip) {
		BufferedReader br = null;
		final List<String> countIp = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 3 && args[2].equals(ip)) {
					countIp.add(args[0]);
				}
			}
			return countIp;
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
	}

	@Override
	public List<String> getAllAuthsByName(final PlayerAuth auth) {
		BufferedReader br = null;
		final List<String> countIp = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 3 && args[2].equals(auth.getIp())) {
					countIp.add(args[0]);
				}
			}
			return countIp;
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return new ArrayList<>();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
	}

	@Override
	public synchronized PlayerAuth getAuth(final String user) {
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args[0].equalsIgnoreCase(user)) {
					switch (args.length) {
					case 2:
						return new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]);
					case 3:
						return new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]);
					case 4:
						return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]);
					case 7:
						return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0]);
					case 8:
						return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], "your@email.com", args[0]);
					case 9:
						return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], args[8], args[0]);
					}
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return null;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return null;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		return null;
	}

	@Override
	public int getIps(final String ip) {
		BufferedReader br = null;
		int countIp = 0;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 3 && args[2].equals(ip)) {
					countIp++;
				}
			}
			return countIp;
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return 0;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return 0;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
	}

	@Override
	public List<PlayerAuth> getLoggedPlayers() {
		return new ArrayList<>();
	}

	@Override
	public DataSourceType getType() {
		return DataSourceType.FILE;
	}

	@Override
	public synchronized boolean isAuthAvailable(final String user) {
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 1 && args[0].equalsIgnoreCase(user)) {
					return true;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		return false;
	}

	@Override
	public boolean isLogged(final String user) {
		return PlayerCache.getInstance().isAuthenticated(user);
	}

	@Override
	public void purgeBanned(final List<String> banned) {
		BufferedReader br = null;
		BufferedWriter bw = null;
		final ArrayList<String> lines = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				try {
					if (banned.contains(args[0])) {
						lines.add(line);
					}
				} catch (NullPointerException | ArrayIndexOutOfBoundsException exc) {
				}
			}
			bw = new BufferedWriter(new FileWriter(source));
			for (final String l : lines) {
				bw.write(l + "\n");
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
			if (bw != null) {
				try {
					bw.close();
				} catch (final IOException ex) {
				}
			}
		}
		return;
	}

	@Override
	public int purgeDatabase(final long until) {
		BufferedReader br = null;
		BufferedWriter bw = null;
		final ArrayList<String> lines = new ArrayList<>();
		int cleared = 0;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length >= 4) {
					if (Long.parseLong(args[3]) >= until) {
						lines.add(line);
						continue;
					}
				}
				cleared++;
			}
			bw = new BufferedWriter(new FileWriter(source));
			for (final String l : lines) {
				bw.write(l + "\n");
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return cleared;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return cleared;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
			if (bw != null) {
				try {
					bw.close();
				} catch (final IOException ex) {
				}
			}
		}
		return cleared;
	}

	@Override
	public void purgeLogged() {
	}

	@Override
	public void reload() {
	}

	@Override
	public synchronized boolean removeAuth(final String user) {
		if (!isAuthAvailable(user)) {
			return false;
		}
		BufferedReader br = null;
		BufferedWriter bw = null;
		final ArrayList<String> lines = new ArrayList<>();
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args.length > 1 && !args[0].equals(user)) {
					lines.add(line);
				}
			}
			bw = new BufferedWriter(new FileWriter(source));
			for (final String l : lines) {
				bw.write(l + "\n");
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
			if (bw != null) {
				try {
					bw.close();
				} catch (final IOException ex) {
				}
			}
		}
		return true;
	}

	@Override
	public synchronized boolean saveAuth(final PlayerAuth auth) {
		if (isAuthAvailable(auth.getNickname())) {
			return false;
		}
		BufferedWriter bw = null;
		try {
			bw = new BufferedWriter(new FileWriter(source, true));
			bw.write(auth.getNickname() + ":" + auth.getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":"
					+ auth.getWorld() + ":" + auth.getEmail() + "\n");
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (bw != null) {
				try {
					bw.close();
				} catch (final IOException ex) {
				}
			}
		}
		return true;
	}

	@Override
	public void setLogged(final String user) {
	}

	@Override
	public void setUnlogged(final String user) {
	}

	@Override
	public boolean updateEmail(final PlayerAuth auth) {
		if (!isAuthAvailable(auth.getNickname())) {
			return false;
		}
		PlayerAuth newAuth = null;
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line = "";
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args[0].equals(auth.getNickname())) {
					newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], auth
							.getEmail(), args[0]);
					break;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		if (newAuth != null) {
			removeAuth(auth.getNickname());
			saveAuth(newAuth);
		}
		return true;
	}

	@Override
	public void updateName(final String oldone, final String newone) {
		final PlayerAuth auth = this.getAuth(oldone);
		auth.setName(newone);
		this.saveAuth(auth);
		this.removeAuth(oldone);
	}

	@Override
	public synchronized boolean updatePassword(final PlayerAuth auth) {
		if (!isAuthAvailable(auth.getNickname())) {
			return false;
		}
		PlayerAuth newAuth = null;
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args[0].equals(auth.getNickname())) {
					switch (args.length) {
					case 4: {
						newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
						break;
					}
					case 7: {
						newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), "world", "your@email.com", args[0]);
						break;
					}
					case 8: {
						newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], "your@email.com", args[0]);
						break;
					}
					case 9: {
						newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], args[8], args[0]);
						break;
					}
					default: {
						newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
						break;
					}
					}
					break;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		if (newAuth != null) {
			removeAuth(auth.getNickname());
			saveAuth(newAuth);
		}
		return true;
	}

	@Override
	public boolean updateQuitLoc(final PlayerAuth auth) {
		if (!isAuthAvailable(auth.getNickname())) {
			return false;
		}
		PlayerAuth newAuth = null;
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args[0].equalsIgnoreCase(auth.getNickname())) {
					newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld(), auth.getEmail(), args[0]);
					break;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		if (newAuth != null) {
			removeAuth(auth.getNickname());
			saveAuth(newAuth);
		}
		return true;
	}

	@Override
	public boolean updateSalt(final PlayerAuth auth) {
		return false;
	}

	@Override
	public boolean updateSession(final PlayerAuth auth) {
		if (!isAuthAvailable(auth.getNickname())) {
			return false;
		}
		PlayerAuth newAuth = null;
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(source));
			String line;
			while ((line = br.readLine()) != null) {
				final String[] args = line.split(":");
				if (args[0].equalsIgnoreCase(auth.getNickname())) {
					switch (args.length) {
					case 4: {
						newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]);
						break;
					}
					case 7: {
						newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), "world", "your@email.com", args[0]);
						break;
					}
					case 8: {
						newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], "your@email.com", args[0]);
						break;
					}
					case 9: {
						newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double
								.parseDouble(args[6]), args[7], args[8], args[0]);
						break;
					}
					default: {
						newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]);
						break;
					}
					}
					break;
				}
			}
		} catch (final FileNotFoundException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} catch (final IOException ex) {
			ConsoleLogger.showError(ex.getMessage());
			return false;
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (final IOException ex) {
				}
			}
		}
		if (newAuth != null) {
			removeAuth(auth.getNickname());
			saveAuth(newAuth);
		}
		return true;
	}
}
