package de.tobias.playpad.server

import java.nio.file.{Files, Path, Paths}
import java.sql.{Connection, DriverManager}

import com.j256.ormlite.dao.{Dao, DaoManager}
import com.j256.ormlite.jdbc.JdbcConnectionSource
import com.j256.ormlite.table.TableUtils
import de.thecodelabs.logger.{FileOutputOption, LogLevelFilter, Logger, Slf4JLoggerAdapter}
import de.thecodelabs.utils.application.ApplicationUtils
import de.thecodelabs.utils.application.container.PathType
import de.tobias.playpad.server.account.{Account, Session}
import de.tobias.playpad.server.plugin.Plugin
import de.tobias.playpad.server.server.SqlHelper
import de.tobias.playpad.server.server.account._
import de.tobias.playpad.server.server.plugin.{PluginGet, PluginList}
import de.tobias.playpad.server.server.project._
import de.tobias.playpad.server.server.project.sync.ProjectSyncHandler
import de.tobias.playpad.server.settings.{Settings, SettingsHandler, SettingsLoader}
import de.tobias.playpad.server.sql.SqlBackgroundRunner
import de.tobias.playpad.server.transformer.JsonTransformer
import spark.Spark._

/**
 * Created by tobias on 29.01.17.
 */
object PlayPadServer extends App {

	private var settingsLoader: SettingsLoader = _
	private var settingsPath: Path = _
	private var settings: Settings = _

	private var databaseUrl: String = _
	private var connectionSource: JdbcConnectionSource = _
	private var databaseConnection: Connection = _
	private var databaseBackgroundThread: Thread = _

	try {
		val app = ApplicationUtils.registerMainApplication(getClass)
		ApplicationUtils.addAppListener(_ => {
			Logger.init(app.getPath(PathType.LOG))
			Slf4JLoggerAdapter.disableSlf4jDebugPrints()
			Logger.setLevelFilter(LogLevelFilter.DEBUG)
			Logger.setFileOutput(FileOutputOption.COMBINED)
		})
		app.start(args)

		// Load Config
		settingsLoader = SettingsHandler.loader
		settingsPath = Paths.get("deploy/settings.properties")

		if (Files.notExists(settingsPath)) {
			SettingsHandler.saver.default(settingsPath)
		}

		settings = settingsLoader.load(settingsPath)

		databaseUrl = s"jdbc:mysql://${settings.db_host}:${settings.db_port}/${settings.db_database}?" +
			s"autoReconnect=true&wait_timeout=86400&serverTimezone=Europe/Berlin"
		connectionSource = new JdbcConnectionSource(databaseUrl)
		connectionSource.setUsername(settings.db_username)
		connectionSource.setPassword(settings.db_password)

		databaseConnection = DriverManager.getConnection(databaseUrl, settings.db_username, settings.db_password)

		val pluginDao: Dao[Plugin, Int] = DaoManager.createDao(connectionSource, classOf[Plugin])
		val accountDao: Dao[Account, Int] = DaoManager.createDao(connectionSource, classOf[Account])
		val sessionDao: Dao[Session, Int] = DaoManager.createDao(connectionSource, classOf[Session])

		// Management Tables
		TableUtils.createTableIfNotExists(connectionSource, classOf[Plugin])
		TableUtils.createTableIfNotExists(connectionSource, classOf[Account])
		TableUtils.createTableIfNotExists(connectionSource, classOf[Session])
		SqlHelper.createTables(databaseConnection)

		val runner = new SqlBackgroundRunner(databaseConnection)
		databaseBackgroundThread = new Thread(runner)
		databaseBackgroundThread.start()


		// Setup Http Server
		port(settings.server_port)
		threadPool(8, 2, 60 * 60 * 1000)

		val externalPath = Paths.get(settings.download_folder).toAbsolutePath.toString
		externalStaticFileLocation(externalPath)

		if (settings.server_secure) {
			secure(settings.server_keystore_path, settings.server_keystore_password, null, null)
		}

		// PlayWall Cloud
		webSocket("/project", new ProjectSyncHandler(sessionDao, databaseConnection))

		// Project
		val transformer = new JsonTransformer

		get("/projects/:id", new ProjectGet(databaseConnection, sessionDao), transformer)
		get("/projects", new ProjectList(databaseConnection, sessionDao), transformer)
		post("/projects", new ProjectPost(databaseConnection, sessionDao), transformer)
		delete("/projects", new ProjectDelete(databaseConnection, sessionDao), transformer)

		// Plugins
		get("/plugins/:id", new PluginGet(pluginDao), transformer)
		get("/plugins", new PluginList(pluginDao), transformer)

		// Account
		post("/accounts", new AccountPost(accountDao), transformer)
		put("/accounts", new AccountPut(accountDao), transformer)

		post("/sessions", new SessionPost(accountDao), transformer)
		delete("/sessions", new SessionDelete(accountDao), transformer)
		get("/sessions", new SessionGet(accountDao), transformer)

		SettingsHandler.saver.save(settings, settingsPath)

		Runtime.getRuntime.addShutdownHook(new Thread(() => {
			databaseBackgroundThread.interrupt()

			databaseConnection.close()
			connectionSource.close()

			stop()
		}))
	} catch {
		case e: Exception => e.printStackTrace()
	}
}