package org.hso.texturesyncclient.controller

import javafx.application.Platform
import javafx.collections.ObservableList
import javafx.event.EventHandler
import javafx.stage.DirectoryChooser
import org.hso.texturesyncclient.alerts.JFXOkayCancelAlert
import org.hso.texturesyncclient.controller.net.AutoConnect
import org.hso.texturesyncclient.controller.net.Connection
import org.hso.texturesyncclient.model.GUIModel
import org.hso.texturesyncclient.model.Sha256
import org.hso.texturesyncclient.model.Texture
import org.hso.texturesyncclient.model.TextureFormat
import org.hso.texturesyncclient.view.importView.ImportView
import org.hso.texturesyncclient.view.importView.ImportViewController
import org.hso.texturesyncclient.view.mainView.MainView
import org.hso.texturesyncclient.view.mainView.MainViewController
import org.hso.texturesyncclient.view.startupView.StartupView
import org.hso.texturesyncclient.view.startupView.StartupViewController
import tornadofx.Controller
import tornadofx.observable
import java.net.InetAddress
import java.util.Calendar
import java.io.File
import javax.imageio.ImageIO
import java.util.UUID
import java.nio.file.Files
import java.io.FileOutputStream


class RootController : Controller() {

    private val mvc: MainViewController by inject()
    private val svc: StartupViewController by inject()
    private val ivc: ImportViewController by inject()

    private lateinit var con: Connection

    private var lastExportDir: String = System.getProperty("user.home")

    /**
     * calculate the resolution, get today's date -> upload to server
     * @param path the absolute path of the file on the client's system
     * @param name the file name
     * @param tags all tags for the file
     */
    fun importTexture(path: String, name: String, tags: ObservableList<String>) {
        val data = Files.readAllBytes(File(path).toPath()) // this is the image as byte array

        val uuid = UUID.randomUUID()
        val format = if (File(path).extension.toLowerCase() == "png") TextureFormat.PNG else TextureFormat.JPEG
        val bimg = ImageIO.read(File(path)) //image for obtaining resolution
        val resolution = Pair(bimg.height, bimg.width)
        val cal = Calendar.getInstance() //calendar obj with current time
        val hash = Sha256(data)

        //Todo free image

        val newTexture = Texture(uuid, name, tags.toTypedArray(), format, resolution, cal, hash)

        try {
            con.uploadTexture(newTexture, data)
            println("Texture upload successful")
        } catch (e: Exception) {
            println(e)
        }
    }

    /**
     * Initialize connection to server and switch to MainView if connected
     * @param name server name as IP or domain
     */
    fun initConnection(name: String) {
        if (name == " ") {
            //no user input, try automatic connect or restore address from settings file
            println("try auto connect")
            try {
                val foundServer = AutoConnect.searchServer()
                if (foundServer != null) {
                    println("[auto] server found")
                    con = foundServer
                    con.ping()
                    println("auto Connection to Server successful")
                    switchStartupToMain()
                } else {
                    println("[auto] no server found")
                }
            } catch (e: Exception) {
                println(e)
                println("auto Connection to Server NOT successful")
            }
            if (SettingsController.serverAddressIsSet()) {
                println("[file] try connect with settings file")
                try {
                    svc.setServerAddress(SettingsController.getServerAddress())
                    con = Connection(InetAddress.getByName(SettingsController.getServerAddress()))
                    con.ping()
                    println("[file] Connection to Server successful")
                    switchStartupToMain()
                } catch (e: Exception) {
                    println(e)
                    println("[file] Connection to Server NOT successful")
                }
            }else{
                println("[file] no address in settings file")
            }
        } else {
            //try to connect with user input
            try {
                println("try connect with user input")
                con = Connection(InetAddress.getByName(name))
                con.ping()
                println("Connection to Server successful")
                SettingsController.setServerAddress(name) //store address in settings file
                switchStartupToMain()
                println("swithing to MainView @ initCon")
            } catch (e: Exception) {
                println(e)
                println("Connection to Server NOT successful")
            }
        }
    }

    fun search(tags: ObservableList<String>): ArrayList<GUIModel> {
        val previewList = arrayListOf<GUIModel>()

        try {
            con.query(tags.toTypedArray()).forEach {
                previewList.add(GUIModel(it, con.getTexturePreview(it.textureHash)))
            }
        } catch (e: Exception) {
            println(e)
        }

        println(previewList.size)

        return previewList
    }

    // TODO this could be a companion object
    fun switchStartupToMain() {
        Platform.runLater {
            find(StartupView::class).replaceWith(MainView::class, sizeToScene = true, centerOnScreen = true)
        }
    }

    // These runLater calls should be unnecessary
    fun switchMainToImport() {
        Platform.runLater {
            find(MainView::class).replaceWith(ImportView::class, sizeToScene = true, centerOnScreen = true)
        }
    }

    fun switchImportToMain() {
        Platform.runLater {
            find(ImportView::class).replaceWith(MainView::class, sizeToScene = true, centerOnScreen = true)
        }
    }

    /**
     * save the texture file to a local directory
     * @param data the texture as meta element
     */
    fun exportTexture(data: Texture) {
        val directoryChooser = DirectoryChooser()
        directoryChooser.title = "Export Verzeichnis wählen"
        directoryChooser.initialDirectory = File(lastExportDir)

        val dir = directoryChooser.showDialog(primaryStage)
        if (dir != null) {
            val extension = if (data.format == TextureFormat.PNG) ".png" else ".jpeg" //get file format
            val filePath = "$dir/${data.name}$extension" //build absolute exported texture path
            val exportedFile = File(filePath) //create file
            val fileout = FileOutputStream(exportedFile)
            fileout.write(con.getTextureFile(data.textureHash)) //write bytes in file
            fileout.close()

            lastExportDir = dir.absolutePath //store last user chosen dir
        }
    }

    /**
     * show the detailed meta information in the DetailView
     * @param data the texture as meta element
     */
    fun showDetail(data: Texture) {
        mvc.setPreview3DTexture(con.getTexturePreview(data.textureHash))
        mvc.setMeta(data.name, data.resolution.toString(), "")
        mvc.setTags(data.tags.toList().observable())
    }

    /**
     * remove a texture from the FolderView and the server
     * @param data the texture as meta element
     */
    fun deleteTexture(data: Texture) {
        val dialogDelete = JFXOkayCancelAlert(
            "Löschen",
            "Wirklich löschen?",
            "-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
        )
        dialogDelete.okayAction = EventHandler {
            mvc.removeTextureFromView(data)
            con.deleteTexture(data)
        }
        dialogDelete.cancelAction = EventHandler {
            // Do nothing
        }
        dialogDelete.showAndWait()
    }

}