//     NEC Laboratories Europe GmbH
//
//     PROPRIETARY INFORMATION
//
// The software and its source code contain valuable trade secrets and
// shall be maintained in confidence and treated as confidential
// information. The software may only be used for evaluation and/or
// testing purposes, unless otherwise explicitly stated in a written
// agreement with NEC Laboratories Europe GmbH.
//
// Any unauthorized publication, transfer to third parties or
// duplication of the object or source code - either totally or in
// part - is strictly prohibited.
//
//          Copyright (c) 2021 NEC Laboratories Europe GmbH
//          All Rights Reserved.
//
// Authors: Konstantin Munichev <konstantin.munichev@neclab.eu>
//
//
// NEC Laboratories Europe GmbH DISCLAIMS ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND THE
// WARRANTY AGAINST LATENT DEFECTS, WITH RESPECT TO THE PROGRAM AND
// THE ACCOMPANYING DOCUMENTATION.
//
// NO LIABILITIES FOR CONSEQUENTIAL DAMAGES: IN NO EVENT SHALL NEC
// Laboratories Europe GmbH or ANY OF ITS SUBSIDIARIES BE LIABLE FOR
// ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR
// LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF
// INFORMATION, OR OTHER PECUNIARY LOSS AND INDIRECT, CONSEQUENTIAL,
// INCIDENTAL, ECONOMIC OR PUNITIVE DAMAGES) ARISING OUT OF THE USE OF
// OR INABILITY TO USE THIS PROGRAM, EVEN IF NEC Laboratories Europe
// GmbH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
//
// THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.

package http

import fabric.FabricConnector
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import proto.Config
import proto.Config.DltConfig
import proto.Config.DltRecord

class Server {
    var connector: FabricConnector? = null
    val port = 8080
    val mutex = Mutex()
}

fun checkException(e: Exception): String {
    if (e.message == null) return ""
    return e.message!!
}

fun main() {
    val server = Server()
    embeddedServer(Netty, port = server.port) {
        install(ContentNegotiation)
        routing {
            post("/dlt/configure") {
                withContext(Dispatchers.IO) {
                    try {
                        val data = call.receiveStream()
                        val config = DltConfig.parseFrom(data)
                        println(config)
                        server.mutex.lock()
                        server.connector = FabricConnector(config)
                        server.mutex.unlock()
                        call.response.status(HttpStatusCode.Created)
                    }
                    // TODO: catch exceptions one by one
                    catch (e: Exception) {
                        call.respond(HttpStatusCode.BadRequest, checkException(e))
                        e.printStackTrace()
                    }
                }
            }
            get("/dlt/configure") {
                withContext(Dispatchers.IO) {
                    server.mutex.lock()
                    if (server.connector == null) {
                        server.mutex.unlock()
                        call.respond(HttpStatusCode.NotFound, "Not initialized")
                    } else {
                        val configBytes = server.connector!!.config.toByteArray()
                        server.mutex.unlock()
                        call.respond(HttpStatusCode.OK, configBytes)
                    }
                }
            }
            post("/dlt/record") {
                withContext(Dispatchers.IO) {
                    server.mutex.lock()
                    try {
                        if (server.connector == null) {
                            call.respond(HttpStatusCode.NotFound, "Not initialized")
                        } else {
                            val record = DltRecord.parseFrom(call.receiveStream())
                            when (record.operation) {
                                Config.DltRecordOperation.ADD -> {
                                    val result = server.connector!!.putData(record)
                                    call.respond(HttpStatusCode.Created, result)
                                }
                                Config.DltRecordOperation.UPDATE -> {
                                    val result = server.connector!!.updateData(record)
                                    call.respond(HttpStatusCode.OK, result)
                                }
                                // TODO: Disable should require only uuid
                                Config.DltRecordOperation.DISABLE -> {
                                    val result = server.connector!!.deleteData(record.id.uuid)
                                    call.respond(HttpStatusCode.OK, result)
                                }
                                else -> {
                                    call.respond(HttpStatusCode.BadRequest, "Invalid operation")
                                }
                            }
                        }
                    }
                    // TODO: catch exceptions one by one
                    catch (e: Exception) {
                        call.respond(HttpStatusCode.BadRequest, checkException(e))
                        e.printStackTrace()
                    }
                    server.mutex.unlock()
                }
            }
            get("/dlt/record") {
                withContext(Dispatchers.IO) {
                    server.mutex.lock()
                    try {
                        if (server.connector == null) {
                            call.respond(HttpStatusCode.NotFound)
                        } else {
                            val uuid = call.receiveText()
                            println("Uuid request: $uuid")
                            val result = server.connector!!.getData(uuid)
                            call.respond(HttpStatusCode.OK, result.toByteArray())
                        }
                    }
                    // TODO: catch exceptions one by one
                    catch (e: Exception) {
                        call.respond(HttpStatusCode.NotFound, checkException(e))
                        e.printStackTrace()
                    }
                    server.mutex.unlock()
                }
            }
        }
    }.start(wait = true)
}
