package szakdolgozat.server;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import szakdolgozat.client.Services;
import szakdolgozat.client.serverevents.GameProposalEvent;
import szakdolgozat.client.serverevents.MessageEvent;
import szakdolgozat.client.serverevents.MoveEvent;
import szakdolgozat.client.serverevents.UserLoginEvent;
import szakdolgozat.client.serverevents.UserLogoutEvent;
import szakdolgozat.shared.User;
import szakdolgozat.shared.UserStats;

import de.novanic.eventservice.client.event.Event;
import de.novanic.eventservice.client.event.domain.Domain;
import de.novanic.eventservice.client.event.domain.DomainFactory;
import de.novanic.eventservice.service.RemoteEventServiceServlet;
import de.novanic.eventservice.service.registry.EventRegistry;
import de.novanic.eventservice.service.registry.EventRegistryFactory;

public class ServicesImpl extends RemoteEventServiceServlet implements Services {

	public final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
	public final String PROTOCOL = "jdbc:derby:users;create=true";
	private final Domain GAME_DOMAIN = DomainFactory.getDomain("gamedom");
	private HashMap<String, String> games = new HashMap<String, String>();

	@Override
	public ArrayList<User> getLoggedInUsersList() {
		return DB.getLoggedInUsers();
	}

	@Override
	public UserStats getUserStats(User user) {
		Connection con = initConnectionToDB();
		try {
			Statement s = con.createStatement();
			PreparedStatement stm = con.prepareStatement("SELECT * FROM userstats WHERE playerID = ?");
			stm.setInt(1, user.getId());
			ResultSet rs = stm.executeQuery();
			List result = ObjectFactory.convertToObjects(rs, UserStats.class);
			return (UserStats) (result.get(0));
		} catch (SQLException e) {
			System.err.println("getUserStats: " + e.getLocalizedMessage());
		} finally {
			try {
				con.close();
			} catch (SQLException ignore) {
			}
		}
		return null;
	}

	@Override
	public synchronized int isUserPasswordValid(User user) {
		Connection con = initConnectionToDB();
		int code = -1;
		try {
			PreparedStatement s = con.prepareStatement(
					"SELECT * FROM users WHERE username = ?",
					ResultSet.TYPE_SCROLL_INSENSITIVE,
					ResultSet.CONCUR_READ_ONLY);

			s.setString(1, user.getUsername());
			ResultSet rs = s.executeQuery();

			List result = ObjectFactory.convertToObjects(rs, User.class);
			User userFromDB = (User) (result.get(0));

			if (user.getPassword().equals(userFromDB.getPassword())) {
				for (User u : getLoggedInUsersList())
					if (u.getUsername().equals(user.getUsername())) {
						code = -2;
						con.close();
						return code;
					}

				PreparedStatement update = con
						.prepareStatement("UPDATE userstats SET logincount = logincount + 1, logintime = ? WHERE playerID = ?");

				update.setDate(1, new Date(user.getLoginDate().getTime()));
				update.setInt(2, userFromDB.getId());
				update.execute();

				user.setPassword("&");
				user.setId(userFromDB.getId());

				DB.registerUserLogin(user);
				code = user.getId();
				rs = con.createStatement().executeQuery(
						"SELECT * FROM userstats WHERE playerID = "
								+ user.getId() + "");
				result = ObjectFactory.convertToObjects(rs, UserStats.class);
				UserStats us = (UserStats) (result.get(0));

				addEvent(GAME_DOMAIN, new UserLoginEvent(user, us));

			}
		} catch (SQLException ignore) {
			System.err.println(ignore.getLocalizedMessage());
		} finally {
			try {
				con.close();
			} catch (SQLException ignore) {
			}
		}
		return code;
	}

	private Connection initConnectionToDB() {
		Connection con = null;
		try {
			Class.forName(DRIVER).newInstance();
			con = DriverManager.getConnection(PROTOCOL);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return con;
	}

	@Override
	public void logoutUser(User user) {
		ArrayList<User> users = DB.getLoggedInUsers();
		for (User u : users)
			if (u.getUsername().equals(user.getUsername())) {
				DB.registerUserLogout(u);
				UserLogoutEvent e = new UserLogoutEvent();
				e.setUser(u);
				addEvent(GAME_DOMAIN, e);
				break;
			}
		return;
	}

	@Override
	public boolean registerNewUser(User user) {
		Connection con = initConnectionToDB();
		try {
			con.setAutoCommit(false);
			PreparedStatement insertUserStatement = con
					.prepareStatement("INSERT into users(id,username,password) values (DEFAULT,?,?)");
			PreparedStatement insertStatsStatement = con
					.prepareStatement("INSERT into userStats(playerID,loginCount,gamesPlayed,gamesWon) values (DEFAULT,0,0,0)");
			insertUserStatement.setString(1, user.getUsername());
			insertUserStatement.setString(2, user.getPassword());
			insertUserStatement.executeUpdate();
			insertStatsStatement.executeUpdate();
			con.commit();
			con.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				con.close();
			} catch (SQLException ignore) {
			}
		}

		return false;
	}

	@Override
	public void sendMessageToGamePartner(User partner, String message) {
		EventRegistryFactory reg = EventRegistryFactory.getInstance();
		EventRegistry re = reg.getEventRegistry();
		Event event = new MessageEvent(message, partner);
		Domain dom = DomainFactory.getDomain(partner.getUsername()
				+ "-gamelistener");		
		this.addEvent(dom, event);
	}

	@Override
	public String proposeGameTo(final User from, final User to) {
		GameProposalEvent proposal = new GameProposalEvent();
		proposal.setFrom(from);
		proposal.setTo(to);

		this.addEvent(GAME_DOMAIN, proposal);

		String answer = "";
		games.put(to.getUsername(), answer);
		int i = 0;
		while (games.get(to.getUsername()).equals("") && i < 30) {
			try {
				Thread.sleep(1000);
				i++;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if (i == 30)
			return "Timeout";

		return games.get(to.getUsername());
	}

	@Override
	public void replyToProposal(User opponent, boolean answer) {
		games.remove(opponent);
		games.put(opponent.getUsername(), Boolean.toString(answer));
		return;
	}

	@Override
	public synchronized void sendMoveToOpponent(User opponent, int row, int col) {		
		Event event = new MoveEvent(row, col);
		Domain dom = DomainFactory.getDomain(opponent.getUsername()
				+ "-gamelistener");
		this.addEvent(dom, event);
		return;
	}

	private int getUserID(User u) {
		Connection con = initConnectionToDB();
		Statement s;
		try {
			s = con.createStatement();
			ResultSet rs = s
					.executeQuery("SELECT * FROM users WHERE username = '"
							+ u.getUsername() + "'");
			List result = ObjectFactory.convertToObjects(rs, User.class);
			return ((User) result.get(0)).getId();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return -1;
	}

	@Override
	public void gameStarted(User player1, User player2) {
		Connection con = initConnectionToDB();
		try {
			PreparedStatement update = con
					.prepareStatement("UPDATE userstats SET gamesplayed = gamesplayed + 1 WHERE playerID = ? OR playerID = ?");
			update.setInt(1, getUserID(player1));
			update.setInt(2, getUserID(player2));
			update.execute();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	@Override
	public void gameWonByUser(User user) {
		Connection con = initConnectionToDB();
		try {
			PreparedStatement update = con
					.prepareStatement("UPDATE userstats SET gameswon = gameswon + 1 WHERE playerID = ?");
			update.setInt(1, getUserID(user));
			update.execute();
		} catch (SQLException e) {
			System.err.println(e.getLocalizedMessage());
		} finally {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	@Override
	public ArrayList<UserStats> getLoggedInUsersStats() {
		Connection con = initConnectionToDB();
		ArrayList<UserStats> loggedinUserStats = new ArrayList<UserStats>();
		try {
			PreparedStatement s = con
					.prepareStatement("SELECT * FROM userstats WHERE playerid = ?");

			for (User user : getLoggedInUsersList()) {
				s.setInt(1, user.getId());
				ResultSet rs = s.executeQuery();
				List result = ObjectFactory.convertToObjects(rs,
						UserStats.class);
				loggedinUserStats.addAll(result);
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return loggedinUserStats;
	}
}
