//API
import { Remote } from '../lib/remote';
import DatalayerSystem from '../lib/datalayersystem';
import IClient from '../lib/client';
import INotifyItem from '../lib/notifyitem';
import VariantTypes from '../lib/varianttypes';
import IResult from '../lib/result';

//Flatbuffers
import * as flatbuffers from 'flatbuffers';
import { Metadata } from 'ctrlx-datalayer-flatbuffers/dist/comm/datalayer/metadata';
import { SubscriptionSettings } from 'ctrlx-datalayer-flatbuffers/dist/comm/datalayer/subscription-settings';
import { SubscriptionProperties } from 'ctrlx-datalayer-flatbuffers/dist/comm/datalayer/subscription-properties';
import { NotifyInfo } from 'ctrlx-datalayer-flatbuffers/dist/comm/datalayer/notify-info';

//Reads a value from a ctrlX Data Layer node
/*
 * SPDX-FileCopyrightText: Bosch Rexroth AG
 *
 * SPDX-License-Identifier: MIT
 */

async function read() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Read value
    const variant = await client.read('framework/metrics/system/cpu-utilisation-percent');
    console.log(variant);

    //Stop the DatalayerSystem
    await system.stop();
}

//Reads a value from a ctrlX Data Layer node
async function readMetadata() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Read metadata
    const variant = await client.readMetadata('framework/metrics/system/cpu-utilisation-percent');

    const bb = new flatbuffers.ByteBuffer(variant.value);
    const metaData = Metadata.getRootAsMetadata(bb);

    console.log('description:', metaData.description());
    console.log('descriptionUrl:', metaData.descriptionUrl());
    console.log('displayName:', metaData.displayName());
    console.log('nodeClass:', metaData.nodeClass());
    console.log('unit:', metaData.unit());

    const operations = metaData?.operations();
    console.log('create:', operations?.create());
    console.log('delete:', operations?.delete_());
    console.log('browse:', operations?.browse());
    console.log('read:', operations?.read());
    console.log('write:', operations?.write());

    //Stop the DatalayerSystem
    await system.stop();
}

//Reads a the metadata (flatbuffers) from datalyer node 'datalayer/subscriptions/settings'
async function readFlatbuffers() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Read value
    const variant = await client.read('datalayer/subscriptions/settings');
    console.log(variant);

    //Read into a ByteBuffer to create flatbuffer type from
    const bb = new flatbuffers.ByteBuffer(variant.value);
    const subscriptionSettings = SubscriptionSettings.getRootAsSubscriptionSettings(bb);

    console.log(subscriptionSettings.maximumBufferSize());
    console.log(subscriptionSettings.minimumErrorInterval());
    console.log(subscriptionSettings.minimumPublishInterval());
    console.log(subscriptionSettings.minimumSampleInterval());

    //Stop the DatalayerSystem
    await system.stop();
}

//Writes a value to a ctrlX Data Layer node
async function write() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Write the value
    const writeValue = 42;
    const variant = await client.write('samples/dotnet/all-data/static/int32', writeValue);
    console.log(variant);

    //Stop the DatalayerSystem
    await system.stop();
}

//Writes a value to a ctrlX Data Layer node with converting the type
async function writeWithImplicitConversion() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Write the value
    const writeValue = 42;
    const variant = await client.write('samples/dotnet/all-data/static/int8', writeValue, VariantTypes.int8);
    console.log(variant);

    //Stop the DatalayerSystem
    await system.stop();
}

//Writes a flatbuffer value to the datalayer node 'datalayer/subscriptions/settings'
async function writeFlatbuffers() {

    //Build settings to write
    const builder = new flatbuffers.Builder(DatalayerSystem.defaultFlatbuffersInitialSize);
    SubscriptionSettings.startSubscriptionSettings(builder);
    SubscriptionSettings.addMaximumBufferSize(builder, 50);
    SubscriptionSettings.addMinimumErrorInterval(builder, 10000); //Millis
    SubscriptionSettings.addMinimumPublishInterval(builder, 50); //Millis
    SubscriptionSettings.addMinimumSampleInterval(builder, 100000n); //Nanos
    const offset = SubscriptionSettings.endSubscriptionSettings(builder);
    SubscriptionSettings.finishSubscriptionSettingsBuffer(builder, offset);

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    const client = await system.createClient(remote);

    //Write the value
    const result = await client.write('datalayer/subscriptions/settings', builder.asUint8Array(), VariantTypes.flatbuffers);
    console.log(result);

    //Stop the DatalayerSystem
    await system.stop();
}

//Subscribes a value from a node
let client: IClient; //Field to keep alive
async function subscribe() {

    //Create DatalayerSystem and start
    const system = new DatalayerSystem('');
    await system.start(false);

    // Build remote
    const cred = {
        user: 'boschrexroth',
        password: 'boschrexroth',
        ip: '192.168.1.1'
    };
    let remote = Remote.build(cred);

    //Create a Client
    client = await system.createClient(remote);

    //Create a datachanged event callback function
    const dataChanged = (result: IResult, item: INotifyItem) => {

        if (result.isBad) {
            return;
        }

        //Fetch NotifyInfo, which is a flatbuffers holding the timestamp (filetime nanos), notifyType and the source node
        const bb = new flatbuffers.ByteBuffer(item.info);
        const info = NotifyInfo.getRootAsNotifyInfo(bb);

        //Convert the timestamp (filetime nanos since 1601 UTC) to date (milliseconds since 1 January 1970 UTC)
        const timestamp = DatalayerSystem.toDate(info.timestamp());
        console.log(timestamp, result.text, info.notifyType(), info.node(), item.value, item.type);
    };

    //Setup subscription properties
    const builder = new flatbuffers.Builder(DatalayerSystem.defaultFlatbuffersInitialSize);
    const idOffset = builder.createString('mySubscription'); //Required
    SubscriptionProperties.startSubscriptionProperties(builder);
    SubscriptionProperties.addId(builder, idOffset);
    SubscriptionProperties.addKeepaliveInterval(builder, 10000);
    SubscriptionProperties.addPublishInterval(builder, 1000);
    SubscriptionProperties.addErrorInterval(builder, 1000);
    const offset = SubscriptionProperties.endSubscriptionProperties(builder);
    SubscriptionProperties.finishSubscriptionPropertiesBuffer(builder, offset);

    //Create subscription
    const subscription = await client.createSubscription(builder.asUint8Array(), dataChanged);

    //Subscribe a node
    await subscription.subscribe('framework/metrics/system/cpu-utilisation-percent');

    //We stop the subscription after some time
    setTimeout(async () => {
        await system.stop();
    }, 10000);
}

//read
read();
readFlatbuffers();
readMetadata();

//subcribe
subscribe();

//write
write();
writeWithImplicitConversion();
writeFlatbuffers();
