import java.net.*;
import java.io.*;
import java.text.*;
import java.util.*;
//import java.util.concurrent.*;
import java.awt.image.*;

/*
 * This class initializes the server and then waits for a connection from the portal.
 */

public class LostHavenServer {	
	public HashMap<String, Player> registered;
	public HashMap<String, Client> online;
	public PriorityQueue<String> orderedReg;
	public PriorityQueue<String> orderedOnline;
	public HashMap<LandType, Land> landMap;
	public HashMap<StructureType, Structure> structMap;
	public Map map;
	
	public boolean running = false;
	
	private ServerSocket serverSocket;
	private ServerSocket portalSocket;
	public ProcessingThread process;
	
	public LostHavenServer() {
		Runtime.getRuntime().addShutdownHook(new ShutdownHook(this));
		online = new HashMap<String, Client>();
		registered = new HashMap<String, Player>();
        orderedReg = new PriorityQueue<String>(11, new PlayerComparator());
        orderedOnline = new PriorityQueue<String>(11, new PlayerComparator());
        loadRegistered();
        loadMap();
        map = new Map("mapInfo.txt", "structInfo.txt", landMap, structMap);
        updateLog("serverlog.txt", "Application opened on " + dateString());
        
        try {
        	portalSocket = new ServerSocket(5829);
	    	while(true) {
	    		new PortalInterfaceThread(portalSocket.accept(), this).start();
	        }
    	} catch(SocketException se) {
    	} catch(IOException ioe) {
    		ioe.printStackTrace();
    	}
	}
	
	public void start() {
		try {
			if(!running) {
				running = true;
				process = new ProcessingThread(this);
				process.start();
	            serverSocket = new ServerSocket(5729);
	            new ClientListener(serverSocket, this).start();
	            updateLog("serverlog.txt", "Server started on " + dateString());
			}
        }catch (IOException ioe) {
            ioe.printStackTrace();
        }
	}
	
	public void stop() {
		try {
			if(running) {
				running = false;
				saveRegistered();
				serverSocket.close();
				updateLog("serverlog.txt", "Server shut down on " + dateString());
			}
		}catch (IOException ioe) {
            ioe.printStackTrace();
        }
	}
	
	public void addRegList(String str, Player player) {
		registered.put(str, player);
		orderedReg.add(str);
	}
	
	public void addOnlineList(String str, Client client) {
		online.put(str, client);
		orderedOnline.add(str);
	}
	
	public void removeOnlineList(String str) {
		orderedOnline.remove(str);
		online.remove(str);
	}
	
	public void sendAll(MessageType type, String s)
	{
		Iterator<Client> iter = online.values().iterator();
		Client cur;

		while(iter.hasNext())
		{
			cur = iter.next();
			cur.getOut().println(type);
			cur.getOut().println(s);
		}
	}
	
	private void loadRegistered() {
		BufferedReader in = null;
		Player player;
		int id;
    	String name, pass;
		
        registered.clear();
        online.clear();
        orderedReg.clear();
        orderedOnline.clear();
    	
		try {
			in = new BufferedReader(new FileReader("chars.txt"));
			
			while(in.ready()) {
				id = Integer.parseInt(in.readLine());
				name = in.readLine();
				pass = in.readLine();
				player = new Player(id, name, pass);
				
				player.setGender(Gender.valueOf(in.readLine()));
				player.setSpeed(Integer.parseInt(in.readLine()));
				player.setSpeed(6);
				player.setAttackSpeed(Integer.parseInt(in.readLine()));
				player.setJob(Job.valueOf(in.readLine()));
				player.setLevel(Integer.parseInt(in.readLine()));
				player.setStrength(Integer.parseInt(in.readLine()));
				player.setDexterity(Integer.parseInt(in.readLine()));
				player.setConstitution(Integer.parseInt(in.readLine()));
				player.setCharisma(Integer.parseInt(in.readLine()));
				player.setWisdom(Integer.parseInt(in.readLine()));
				player.setIntelligence(Integer.parseInt(in.readLine()));
				player.setDamage(Integer.parseInt(in.readLine()));
				player.setExperience(Integer.parseInt(in.readLine()));
				player.setHitpoints(Integer.parseInt(in.readLine()));
				player.setMaxHitpoints(Integer.parseInt(in.readLine()));
				player.setManapoints(Integer.parseInt(in.readLine()));
				player.setMaxManapoints(Integer.parseInt(in.readLine()));
				player.setGold(Integer.parseInt(in.readLine()));
				
				addRegList(name, player);
			}

			in.close();
		} catch(IOException ioe) {
			ioe.printStackTrace();
		}
	}
	
	private void loadMap() {
    	landMap = new HashMap<LandType, Land>();
    	structMap = new HashMap<StructureType, Structure>();
    	BufferedImage nullImg = null;
    	
    	landMap.put(LandType.Ocean, new Land(LandType.Ocean, nullImg, false));
    	landMap.put(LandType.Grass, new Land(LandType.Grass, nullImg, true));
    	
    	structMap.put(StructureType.None, new Structure(StructureType.None, nullImg, false));
    	structMap.put(StructureType.BlueOrb, new Structure(StructureType.BlueOrb, nullImg, false));
    	structMap.put(StructureType.Cave, new Structure(StructureType.Cave, nullImg, false));
    	structMap.put(StructureType.Gravestone, new Structure(StructureType.Gravestone, nullImg, false));
    	structMap.put(StructureType.GraveyardFence1, new Structure(StructureType.GraveyardFence1, nullImg, false));
    	structMap.put(StructureType.GraveyardFence2, new Structure(StructureType.GraveyardFence2, nullImg, false));
    	structMap.put(StructureType.PicketFence1, new Structure(StructureType.PicketFence1, nullImg, false));
    	structMap.put(StructureType.PicketFence2, new Structure(StructureType.PicketFence2, nullImg, false));
    	structMap.put(StructureType.Hut, new Structure(StructureType.Hut, nullImg, false));
    	structMap.put(StructureType.WitchHut, new Structure(StructureType.WitchHut, nullImg, false));
    	structMap.put(StructureType.Tent, new Structure(StructureType.Tent, nullImg, false));
    	structMap.put(StructureType.LargeTent, new Structure(StructureType.LargeTent, nullImg, false));
    	structMap.put(StructureType.House, new Structure(StructureType.House, nullImg, false));
    	structMap.put(StructureType.Tree, new Structure(StructureType.Tree, nullImg, false));
    	structMap.put(StructureType.BlueOrb, new Structure(StructureType.BlueOrb, nullImg, false));
    	structMap.put(StructureType.RedOrb, new Structure(StructureType.RedOrb, nullImg, false));
    	structMap.put(StructureType.LoginPedestal, new Structure(StructureType.LoginPedestal, nullImg, true));
    	structMap.put(StructureType.RejuvenationPedestal, new Structure(StructureType.RejuvenationPedestal, nullImg, true));
    	structMap.put(StructureType.LifePedestal, new Structure(StructureType.LifePedestal, nullImg, true));
    	structMap.put(StructureType.ManaPedestal, new Structure(StructureType.ManaPedestal, nullImg, true));
    }
	
	private void saveRegistered() {
		PrintWriter out = null;
		Iterator<String> iter = orderedReg.iterator();
		Player cur;
		
		try {
			out = new PrintWriter(new FileWriter("chars.txt"));
		} catch(IOException ioe) {
			ioe.printStackTrace();
		}
		
		while(iter.hasNext())
		{
			cur = registered.get(iter.next());
			out.println(cur.getId());
			out.println(cur.getName());
			out.println(cur.getPass());
			out.println(cur.getGender());
			out.println(cur.getSpeed());
			out.println(cur.getAttackSpeed());
			out.println(cur.getJob());
			out.println(cur.getLevel());
			out.println(cur.getStrength());
			out.println(cur.getDexterity());
			out.println(cur.getConstitution());
			out.println(cur.getCharisma());
			out.println(cur.getWisdom());
			out.println(cur.getIntelligence());
			out.println(cur.getDamage());
			out.println(cur.getExperience());
			out.println(cur.getHitpoints());
			out.println(cur.getMaxHitpoints());
			out.println(cur.getManapoints());
			out.println(cur.getMaxManapoints());
			out.println(cur.getGold());
		}
		
		out.close();
	}
	
	public void updateLog(String fileName, String message) {
		try {
			PrintWriter out = new PrintWriter(new FileWriter(fileName, true));
			out.println(message);
			out.close();
		} catch(IOException ioe) {
			ioe.printStackTrace();
		}
	}
	
	public String dateString() {
		return new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(new Date());
	}
	
    public static void main(String[] args) {
    	new LostHavenServer();
    }
    
    private class PlayerComparator implements Comparator<String> {
		public int compare(String str1, String str2) {
			return registered.get(str1).getId() - registered.get(str2).getId();
		}
	}
    
    private class ShutdownHook extends Thread {
    	LostHavenServer server;
    	
    	public ShutdownHook(LostHavenServer server) {
    		this.server = server;
    	}
    	
    	public void run() {
    		server.stop();
    		updateLog("serverlog.txt", "Application closed on " + dateString());
    	}
    }
}
