package be.jeffcheasey88.peeratcode;

import static be.jeffcheasey88.peeratcode.framework.RequestType.OPTIONS;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.regex.Matcher;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

import be.jeffcheasey88.peeratcode.framework.Client;
import be.jeffcheasey88.peeratcode.framework.HttpReader;
import be.jeffcheasey88.peeratcode.framework.HttpWriter;
import be.jeffcheasey88.peeratcode.framework.Locker;
import be.jeffcheasey88.peeratcode.framework.Response;
import be.jeffcheasey88.peeratcode.framework.Route;
import be.jeffcheasey88.peeratcode.framework.Router;
import be.jeffcheasey88.peeratcode.framework.User;
import be.jeffcheasey88.peeratcode.model.Completion;
import be.jeffcheasey88.peeratcode.model.Group;
import be.jeffcheasey88.peeratcode.repository.DatabaseRepository;
import be.jeffcheasey88.peeratcode.routes.BadgeDetails;
import be.jeffcheasey88.peeratcode.routes.ChapterElement;
import be.jeffcheasey88.peeratcode.routes.ChapterList;
import be.jeffcheasey88.peeratcode.routes.DynamicLeaderboard;
import be.jeffcheasey88.peeratcode.routes.Leaderboard;
import be.jeffcheasey88.peeratcode.routes.Login;
import be.jeffcheasey88.peeratcode.routes.PlayerDetails;
import be.jeffcheasey88.peeratcode.routes.PuzzleElement;
import be.jeffcheasey88.peeratcode.routes.PuzzleResponse;
import be.jeffcheasey88.peeratcode.routes.Register;
import be.jeffcheasey88.peeratcode.routes.Result;
import be.jeffcheasey88.peeratcode.routes.groups.GroupCreate;
import be.jeffcheasey88.peeratcode.routes.groups.GroupJoin;
import be.jeffcheasey88.peeratcode.routes.groups.GroupList;
import be.jeffcheasey88.peeratcode.routes.groups.GroupQuit;

public class Main{
	public static void main(String[] args) throws Exception{
		Configuration config = new Configuration("config.txt");
		config.load();

		Class.forName("com.mysql.cj.jdbc.Driver");

		Router router = new Router(new DatabaseRepository(config), config.getTokenIssuer(),
				config.getTokenExpiration());

		router.setDefault((matcher, user, reader, writer) -> {
			writer.response(404, "Access-Control-Allow-Origin: *");
			writer.write("404 not Found.\n");
			writer.flush();
			writer.close();
		});
		
		router.register(new Response(){
			@Route(path = "^(.*)$", type = OPTIONS)
			@Override
			public void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception {
				writer.response(200, "Access-Control-Allow-Origin: *",
						"Access-Control-Allow-Methods: *", "Access-Control-Allow-Headers: *");
			}
		});
		
		initRoutes(router, config);

//		RouteExtracter extracter = new RouteExtracter(router);
//		extracter.extract();
		
		startWebServer(config, router);
	}

	private static void initRoutes(Router router, Configuration config){
		router.register(new ChapterElement(router.getDataBase()));
		router.register(new ChapterList(router.getDataBase()));
		router.register(new PuzzleElement(router.getDataBase()));
		router.register(new Register(router.getDataBase(), router, config.getUsersFiles()));
		router.register(new Login(router.getDataBase(), router));
		router.register(new Result(router.getDataBase()));
		router.register(new Leaderboard(router.getDataBase()));
		router.register(new PlayerDetails(router.getDataBase()));
		router.register(new BadgeDetails(router.getDataBase()));
		
		Locker<Group> groupLock = new Locker<>();
		router.register(new GroupCreate(router.getDataBase(), groupLock, config.getGroupJoinMinutes()));
		
		DynamicLeaderboard dlb = new DynamicLeaderboard(router.getDataBase());
		router.register(dlb);
		
		Locker<Completion> leaderboard = dlb.getLocker();
		
		router.register(new PuzzleResponse(router.getDataBase(), config.getUsersFiles(), leaderboard));
		router.register(new GroupList(router.getDataBase()));
		router.register(new GroupJoin(router.getDataBase(), config.getGroupJoinMinutes(), config.getGroupQuitMinutes(), leaderboard));
		router.register(new GroupQuit(router.getDataBase(), config.getGroupJoinMinutes(), leaderboard));
		
//		Bot bot = new Bot(config, router.getDataBase(), groupLock);
//		bot.start();
	}

	private static void startWebServer(Configuration config, Router router) throws IOException {
		if (config.useSsl()) { // Not needed with the use of a proxy
			SSLServerSocket server = null;
			try {
				System.setProperty("javax.net.ssl.keyStore", config.getSslKeystore());
				System.setProperty("javax.net.ssl.keyStorePassword", config.getSslKeystorePasswd());

				SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
				server = (SSLServerSocket) ssf.createServerSocket(config.getTcpPort());

				while (!server.isClosed()) {
					Socket socket = server.accept();
					Client client = new Client(socket, router);
					client.start();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (server != null) {
					server.close();
				}
			}
		} else {
			try (ServerSocket server = new ServerSocket(config.getTcpPort())) {
				while (!server.isClosed()) {
					Socket socket = server.accept();
					Client client = new Client(socket, router);
					client.start();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
