//     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) 2022 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.

import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import proto.ContextOuterClass
import proto.Dlt
import proto.DltServiceGrpcKt
import java.io.Closeable
import java.util.*
import java.util.concurrent.TimeUnit

class DltServiceClient(private val channel: ManagedChannel) : Closeable {
    private val stub: DltServiceGrpcKt.DltServiceCoroutineStub = DltServiceGrpcKt.DltServiceCoroutineStub(channel)

    suspend fun putData(data: Dlt.DltRecord) {
        println("Sending record ${data.recordId}...")
        val response = stub.recordToDlt(data)
        println("Response: ${response.recordId}")
    }

    suspend fun getData(id: Dlt.DltRecordId) {
        println("Requesting record $id...")
        val response = stub.getFromDlt(id)
        println("Got data: $response")
    }

    fun subscribe(filter: Dlt.DltRecordSubscription) {
        val subscription = stub.subscribeToDlt(filter)
        GlobalScope.launch {
            subscription.collect {
                println("Got subscription event")
                println(it)
            }
        }
    }

    override fun close() {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS)
    }
}


fun main() = runBlocking {
    val port = 50051
    val channel = ManagedChannelBuilder.forAddress("localhost", port).usePlaintext().build()

    val client = DltServiceClient(channel)

    val domainUuid = UUID.randomUUID().toString()
    val recordUuid = UUID.randomUUID().toString()
    println("New domain uuid $domainUuid")
    println("New record uuid $recordUuid")

    val id = Dlt.DltRecordId.newBuilder()
        .setDomainUuid(
            ContextOuterClass.Uuid.newBuilder()
                .setUuid(domainUuid)
        )
        .setRecordUuid(
            ContextOuterClass.Uuid.newBuilder()
                .setUuid(recordUuid)
        )
        .setType(Dlt.DltRecordTypeEnum.DLTRECORDTYPE_SERVICE)
        .build()

    val subscription = Dlt.DltRecordSubscription.newBuilder()
        .addType(Dlt.DltRecordTypeEnum.DLTRECORDTYPE_CONTEXT)
        .addType(Dlt.DltRecordTypeEnum.DLTRECORDTYPE_LINK)
        .addType(Dlt.DltRecordTypeEnum.DLTRECORDTYPE_SERVICE)
        .addOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_ADD)
        .addOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE)
        .addOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_DELETE)
        .build()

    client.subscribe(subscription)

    Thread.sleep(5000)

    val data = Dlt.DltRecord.newBuilder()
        .setRecordId(id)
        .setOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_ADD)
        .setDataJson("{}")
        .build()

    println("sending new record")
    client.putData(data)
    client.getData(id)

    Thread.sleep(5000)

    val updateData = Dlt.DltRecord.newBuilder()
        .setRecordId(id)
        .setOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE)
        .setDataJson("{\"name\": \"test\"}")
        .build()

    println("updating record")
    client.putData(updateData)
    client.getData(id)

    Thread.sleep(5000)

    val removeData = Dlt.DltRecord.newBuilder()
        .setRecordId(id)
        .setOperation(Dlt.DltRecordOperationEnum.DLTRECORDOPERATION_DELETE)
        .setDataJson("{\"name\": \"test\"}")
        .build()

    println("removing record")
    client.putData(removeData)
    try {
        client.getData(id)
    } catch (e: Exception) {
        println(e.toString())
    }
    Thread.sleep(5000)
}
