package net.taehui.twilight

import io.netty.channel.epoll.EpollEventLoopGroup
import io.netty.channel.epoll.EpollServerSocketChannel
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.ServerSocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import net.taehui.twilight.qwilight.QwilightBoot
import net.taehui.twilight.site.SiteBoot
import net.taehui.twilight.system.*
import net.taehui.twilight.taehui.TaehuiBoot
import net.taehui.twilight.www.WwwBoot
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
import org.apache.commons.lang3.SystemUtils
import org.fusesource.jansi.AnsiConsole
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Paths
import java.security.KeyStore
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import kotlin.io.path.absolute

object Twilight {
    @JvmStatic
    fun main(args: Array<String>) {
        try {
            Files.createDirectories(TwilightComponent.NOTE_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.COMMENT_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.BUNDLE_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.DEFAULT_NOTE_ENTRY)
            Files.createDirectories(TwilightComponent.DEFAULT_UI_ENTRY)
            Files.createDirectories(TwilightComponent.TITLE_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.LEVEL_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.VOTE_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.ABILITY_ENTRY_PATH)
            Files.createDirectories(TwilightComponent.EDGE_ENTRY_PATH)
        } catch (e: IOException) {
            System.err.println("Cannot run Twilight from ${Paths.get("").absolute()}")
            Runtime.getRuntime().exit(1)
        }

        val o = Options()
        o.addOption(Option("t", "test", false, "Test"))
        val isTest = DefaultParser().parse(o, args).hasOption("test")

        val eventLoopGroup = if (SystemUtils.IS_OS_LINUX) EpollEventLoopGroup() else NioEventLoopGroup()
        val eventChannel: Class<out ServerSocketChannel> =
            if (SystemUtils.IS_OS_LINUX) EpollServerSocketChannel::class.java else NioServerSocketChannel::class.java
        try {
            AnsiConsole.systemInstall()
            Configure.loadConfigure()
            DB.loadDB()
            AbilityClassSystem.loadAbilityClasses()
            AbilitySystem.loadAbility()
            BannedIP.loadBannedIP()
            BannedNoteFile.loadBannedNoteFile()
            EdgeSystem.loadEdge()
            AvatarIPSystem.loadAvatarIP()
            LevelSystem.loadLevel()
            TitleSystem.loadTitle()
            VoteSystem.loadVote()
            AutoSystem.handleSystem()
            Files.newInputStream(Paths.get("Twilight.jks")).use {
                val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
                keyStore.load(it, Configure.javaCipherStore.pw0)
                val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
                keyManagerFactory.init(keyStore, Configure.javaCipherStore.pw1)
                val sslContext = SSLContext.getInstance("TLS")
                sslContext.init(keyManagerFactory.keyManagers, null, null)
                QwilightBoot(eventLoopGroup, eventChannel, sslContext).run()
                SiteBoot(eventLoopGroup, eventChannel).run()
                WwwBoot(eventLoopGroup, eventChannel).run()
                TaehuiBoot(eventLoopGroup, eventChannel).run()
            }
            TVSystem.handleSystem()
            NoteFilesSystem.loadNoteFiles()
            PlatformIDSystem.loadPlatformID()
            PlatformSystem.handleSystem()
            WitSystem.handleSystem()
            QwilightNamesSystem.loadQwilightNames()
            AwilightHandler.eventLoopGroup = eventLoopGroup
            AwilightHandler.setAwilightCount(Configure.awilightCount)
            if (isTest) {
                AvatarHandler.sendClose("")
            } else {
                AvatarHandler.sendClose(IO.handleSystem())
            }
        } finally {
            WitSystem.dispose()
            PlatformIDSystem.savePlatformID()
            PlatformSystem.dispose()
            TVSystem.dispose()
            AutoSystem.dispose()
            AvatarIPSystem.saveAvatarIP()
            BannedIP.saveBannedIP()
            BannedNoteFile.saveBannedNoteFile()
            Configure.saveConfigure()
            eventLoopGroup.shutdownGracefully()
        }
    }
}