const express = require('express');
const readline = require('readline');
var os = require('os');
const e = require('express');

const app = express();
const mgmtApp = express();
const port = 999;
const mgmtPort = 998;
var pointsdb = []; 
var lastUserAgent = '';
var chunked = false;
var delay = 0;
var permanentError = 0;
const prefix = '';
var server = undefined;

mgmtApp.use (function(req, res, next) {
    var data='';
    req.setEncoding('utf8');
    req.on('data', function(chunk) { 
       data += chunk;
    });

    req.on('end', function() {
        req.body = data;
        next();
    });
});

mgmtApp.get('/start', (req,res) => {
    if(server === undefined) {
        console.log('Starting server');
        server = app.listen(port);
        server.on('close',function() {
            pointsdb = [];
            server = undefined;
            console.log('Server closed');
        });
        server.on('clientError', (err, socket) => {
            console.log("client error", err);
            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
        });
        res.status(201).send(`Listening on http://${server.address().address}:${server.address().port}`);
    } else {
        server.
        res.status(204).end();
    };
});

mgmtApp.get('/stop', (req,res) => {
    if(server === undefined) {
        res.status(404).end();
    } else {
        console.log('Shutting down server');
        server.close();
        res.status(200).end();
    };
});
mgmtApp.get('/status', (req,res) => {
    if(server === undefined) {
        res.status(404).send("stopped");
    } else {
        res.status(200).send("running");
    }
});

mgmtApp.post('/log', (req,res) => {
    console.log('===' + req.body + '=====');
    res.status(204).end();
})


app.use (function(req, res, next) {
    var data='';
    req.setEncoding('utf8');
    req.on('data', function(chunk) { 
       data += chunk;
    });

    req.on('end', function() {
        req.body = data;
        next();
    });
});
app.get(prefix + '/test/user-agent', (req,res) => {
    res.status(200).send(lastUserAgent);
})
app.get(prefix + '/ready', (req,res) => {
    lastUserAgent = req.get('User-Agent');
    res.status(200).send("<html><body><h1>OK</h1></body></html>");
})
app.get(prefix + '/health', (req,res) => {
    lastUserAgent = req.get('User-Agent');
    res.status(200).send("<html><body><h1>OK</h1></body></html>");
})

app.get(prefix + '/ping', (req,res) => {
    lastUserAgent = req.get('User-Agent');
    if(req.query['verbose'] == 'true') {
        res.status(200).send("<html><body><h1>OK</h1></body></html>");
    } else {
        res.status(204).end();
    }
})


app.post(prefix + '/api/v2/write', (req,res) => {
    chunked = false;
    if(checkWriteParams(req, res) && handleAuthentication(req, res)) {
        //console.log('Write');
        //console.log(req.body);
        var points = parsePoints(req.body);
        if(permanentError > 0) {
            console.log('Pernament error ' + permanentError);
            res.status(permanentError).send("Internal server error"); 
        }
        if(Array.isArray(points) && points.length > 0) {
            var point = points[0];
            if(point.tags.hasOwnProperty('direction')) {
                if(permanentError > 0) {
                    if(point.tags.direction == 'permanent-unset' ) {
                        permanentError = 0;
                    }
                } else {
                    switch(point.tags.direction) {
                        case '429-1':
                            console.log('Limit exceeded');
                            res.set("Retry-After","10");
                            console.log('Retry-After 10');
                            res.status(429).send("Limit exceeded"); 
                            break;
                        case '429-2':
                            console.log('Limit exceeded');
                            console.log('Retry-After default');
                            res.status(429).send("Limit exceeded"); 
                            break;
                        case '503-1':
                            res.set("Retry-After","10");
                            console.log('Server overloaded');
                            console.log('Retry-After 10');
                            res.status(503).send("Server overloaded"); 
                            break;
                        case '503-2':
                            console.log('Server overloaded');
                            console.log('Retry-After default');
                            res.status(503).send("Server overloaded"); 
                            break;
                        case 'delete-all':
                            pointsdb = [];
                            res.status(204).end(); 
                            break; 
                        case 'status':
                            points = [];
                            const code = parseInt(point.tags['x-code']);
                            console.log("Set code: " + code);
                            res.status(code).send("bad request");
                            break;
                        case 'chunked':
                            chunked = true;
                            console.log("Set chunked = true");
                            break;
                        case 'timeout':
                            delay = parseInt(point.tags.timeout)*1000;
                            console.log("Set delay: " + delay);
                            break;
                        case 'permanent-set':
                            permanentError = parseInt(point.tags['x-code']);
                            console.log("Set permanentError: " + permanentError);
                            res.status(permanentError).send("bad request");
                            break;
                    }
                    
                }
                points.shift();
            }
            console.log("write " + points.length + ' points');
            points.forEach((item, index) => {
                pointsdb.push(item);
            })
            if(res.statusCode < 299) {
                res.status(204).end();  
            }
        } else {
            res.status(204).end();
        }
    }
    if(res.statusCode != 204) {
        console.log('Responded with ' + res.statusCode);
    }
})

app.post(prefix + '/write', (req,res) => {
    if(checkWriteParamsV1(req, res) ) {
        var points = parsePoints(req.body);
        if(Array.isArray(points) && points.length > 0) {
            var point = points[0];
            if(point.tags.hasOwnProperty('direction')) {
                switch(point.tags.direction) {
                    case 'delete-all':
                        pointsdb = [];
                        buckets = [];
                        res.status(204).end(); 
                        break; 
                    case '400':
                        points = [];
                        res.status(400).send("bad request");
                        break;
                    case '500':
                        points = [];
                        res.status(500).send("internal server error");
                        break;
                }
                points.shift();
            }
            console.log("write " + points.length + ' points');
            points.forEach((item, index) => {
                pointsdb.push(item);
            })
            if(res.statusCode < 299) {
                res.status(204).end();  
            }
        } else {
            res.status(204).end();
        }
    }
    if(res.statusCode != 204) {
        console.log('Responded with ' + res.statusCode);
    }
})

app.post(prefix + '/api/v2/delete', (req,res) => {
    console.log('Deleteting points');
    pointsdb = [];
    buckets = [];
    res.status(204).end(); 
});

var queryRes = {
    "singleTable":`#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
#group,false,false,true,true,false,false,true,true,true,true
#default,_result,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,1.4,f,test,1,adsfasdf
,,1,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,6.6,f,test,3,adsfasdf
\r 
`,
    "nil-value": `#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,,f,test,1,adsfasdf
,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,6.6,f,test,,adsfasdf
,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:11:32.225467895Z,1122.45,f,test,3,
\r
`,
"multiTables":`#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,unsignedLong,string,string,string,string
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,_result,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,14,f,test,1,adsfasdf
,_result,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,66,f,test,1,adsfasdf
\r
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,long,string,string,string,string
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,_result1,1,2020-02-16T22:19:49.747562847Z,2020-02-17T22:19:49.747562847Z,2020-02-17T10:34:08.135814545Z,-4,i,test,1,adsfasdf
,_result1,1,2020-02-16T22:19:49.747562847Z,2020-02-17T22:19:49.747562847Z,2020-02-16T22:08:44.850214724Z,-1,i,test,1,adsfasdf
\r
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,boolean,string,string,string,string
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,_result2,2,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,false,b,test,0,brtfgh
,_result2,2,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.969100374Z,true,b,test,0,brtfgh
\r
#datatype,string,long,dateTime:RFC3339Nano,dateTime:RFC3339Nano,dateTime:RFC3339Nano,duration,string,string,string,base64Binary
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,_result3,3,2020-02-10T22:19:49.747562847Z,2020-02-12T22:19:49.747562847Z,2020-02-11T10:34:08.135814545Z,1d2h3m4s,d,test,0,eHh4eHhjY2NjY2NkZGRkZA==
,_result3,3,2020-02-10T22:19:49.747562847Z,2020-02-12T22:19:49.747562847Z,2020-02-12T22:08:44.969100374Z,22h52s,d,test,0,ZGF0YWluYmFzZTY0
\r
`,
"diffNum-data":`#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,long,string,duration,base64Binary,dateTime:RFC3339
,result,table,_start,_stop,_time,deviceId,sensor,elapsed,note,start
,,0,2020-04-28T12:36:50.990018157Z,2020-04-28T12:51:50.990018157Z,2020-04-28T12:38:11.480545389Z,1467463,BME280,1m1s,ZGF0YWluYmFzZTY0,2020-04-27T00:00:00Z,2345234
\r
`,
"diffNum-type-vs-header":`#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,long,string,duration,base64Binary,dateTime:RFC3339
,result,table,_start,_stop,_time,deviceId,sensor,elapsed,note
,,0,2020-04-28T12:36:50.990018157Z,2020-04-28T12:51:50.990018157Z,2020-04-28T12:38:11.480545389Z,1467463,BME280,1m1s,ZGF0YWluYmFzZTY0,2020-04-27T00:00:00Z
\r
`,
"flux-error":`{"code":"invalid","message":"compilation failed: loc 4:17-4:86: expected an operator between two expressions"}`,
"invalid-datatype":`#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,int,string,duration,base64Binary,dateTime:RFC3339
,result,table,_start,_stop,_time,deviceId,sensor,elapsed,note,start
,,0,2020-04-28T12:36:50.990018157Z,2020-04-28T12:51:50.990018157Z,2020-04-28T12:38:11.480545389Z,1467463,BME280,1m1s,ZGF0YWluYmFzZTY0,2020-04-27T00:00:00Z
,,0,2020-04-28T12:36:50.990018157Z,2020-04-28T12:51:50.990018157Z,2020-04-28T12:39:36.330153686Z,1467463,BME280,1h20m30.13245s,eHh4eHhjY2NjY2NkZGRkZA==,2020-04-28T00:00:00Z
\r
`,
"missing-datatype":`,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
,_result3,3,2020-02-10T22:19:49.747562847Z,2020-02-12T22:19:49.747562847Z,2020-02-11T10:34:08.135814545Z,1d2h3m4s,d,test,0,eHh4eHhjY2NjY2NkZGRkZA==
,_result3,3,2020-02-10T22:19:49.747562847Z,2020-02-12T22:19:49.747562847Z,2020-02-12T22:08:44.969100374Z,22h52s,d,test,0,eHh4eHhjY2NjY2NkZGRkZA==
\r
`,
"error-it-row-full":`#datatype,string,string
,error,reference
,failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time,897
\r
`,
"error-it-row-no-reference":`#datatype,string,string
,error,reference
,failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time,
\r
`,
"error-it-row-no-message":`#datatype,string,string
,error,reference
,,
\r
`,
"empty":``
};



function sleep(milliseconds) {
    const date = Date.now();
    let currentDate = null;
    do {
      currentDate = Date.now();
    } while (currentDate - date < milliseconds);
  }

app.post(prefix+'/api/v2/query', (req,res) => {
    //console.log("Query with: " + req.body);
    if(checkQueryParams(req, res) && handleAuthentication(req, res)) {
        var queryObj = JSON.parse(req.body);
        var data = '';
        var status = 200;
        if (queryObj["query"].startsWith('testquery-')) {
            var qi =  queryObj["query"].substring(10) ;
            console.log('query: ' + qi + ' dataset');
            if(qi.endsWith('error')) {
                status = 400;
            }
            data = queryRes[qi];
        } else if(queryObj["query"] === 'echo') {
            status = 444;
            data = JSON.stringify(queryObj);
        } else if(pointsdb.length > 0) {
            console.log('query: ' + pointsdb.length + ' points');
            data = convertToCSV(pointsdb);
        }
        if(data.length > 0) {
            if(delay) {
                sleep(delay);
                delay = 0;
            }
            //console.log(data);

            if(chunked) {
                var i = data.length/3;
                res.set("Transfer-Encoding","chunked");
                res.status(status);
                res.write(data.substring(0, i+1));
                res.write(data.substring(i+1, 2*i+1));
                res.write(data.substring(2*i+1));
                res.end();
                chunked = false;
            } else {
                res.status(status).send(data);
            }
        } else {
            res.status(status).end();
        }
    } else {
        res.status(400).send('invalid query request');
    }
});

const orgID = `e2e2d84ffb3c4f85`;
const orgName = `my-org`;

var buckets = []

function bucketResp(bucket) {
    return `{
        "id": "${bucket.id}",
        "orgID": "${bucket.orgID}",
        "type": "user",
        "name": "${bucket.name}",
        "retentionRules": [
            {
                    "type": "expire",
                    "everySeconds": ${bucket.expire},
                    "shardGroupDurationSeconds": 604800
            }
        ],
        "createdAt": "2021-08-24T07:53:17.2525733Z",
        "updatedAt": "2021-08-24T07:53:17.2525734Z",
        "links": {
                "labels": "/api/v2/buckets/3eae77843acbebee/labels",
                "members": "/api/v2/buckets/3eae77843acbebee/members",
                "org": "/api/v2/orgs/e2e2d84ffb3c4f85",
                "owners": "/api/v2/buckets/3eae77843acbebee/owners",
                "self": "/api/v2/buckets/3eae77843acbebee",
                "write": "/api/v2/write?org=e2e2d84ffb3c4f85\u0026bucket=3eae77843acbebee"
        },
        "labels": []
    }
   `
}



function bucketsResp(buckets){
   return `{
    "links": {
            "self": "/api/v2/buckets?descending=false\u0026limit=20\u0026offset=0"
    },
    "buckets": [
        ${buckets}
    ]
}
`
}

const invalidIDResp = `{
    "code": "invalid",
    "message": "id must have a length of 16 bytes"
}
`

const notfoundResp = `{
    "code": "not found",
    "message": "bucket not found"
}`

const orgNotFoundReps= `{
    "code": "not found",
    "message": "organization not found"
}`

app.get(prefix+'/api/v2/buckets', (req,res) => {
    if(handleAuthentication(req, res)) {
        var name = req.query['name'];
        var id = req.query['id'];
        res.set("Content-Type","application/json");
        res.status(200);
        if(name) { //filter by name
            console.log("GET buckets name: " + name)
            let b = buckets.find((value)=> {
                return value.name === name
            })
            if(b) {
                res.send(bucketResp(b))
            } else { //invalid name
                res.send(bucketsResp(''))
            }
        } else if(id) { //filter by id
            console.log("GET buckets id: " + id)
            if(id.length != 16) { //invalid name
                res.status(400)
                res.send(invalidIDResp)
                return
            }
            let b = buckets.find((value)=> {
                return value.id === id
            })
            if(b) {
                res.send(bucketResp(b))
            } else {
                res.status(404)
                res.send(notfoundResp)
            }
        } else { //return all buckets
            console.log("GET all buckets")
            bucketsJson = buckets.reduce((total, value, index) => {
                return total + (index > 0?",\n":"") + bucketResp(value)
            },'')
            res.send(bucketsResp(bucketsJson))
        }
    }
});


function invalidJSONResp(err) {
    return `{
    "code": "invalid",
    "message": "${err}"
}`
}

function conflictBucketResp(bucket) {
    return `{
    "code": "conflict",
    "message": "bucket with name ${bucket} already exists"
}`
}

const idBase = '0123456789abcdef'

function genID() {
    let result = '';
    for ( let i = 0; i < 16; i++ ) {
        result += idBase.charAt(Math.floor(Math.random() * 16));
    }
    return result;
}

app.post(prefix+'/api/v2/buckets', (req,res) => {
    console.log('Post buckets')
    if(handleAuthentication(req, res)) {
        let newBucket = undefined
        try {
            newBucket = JSON.parse(req.body)
        } catch(err) {
            res.status(400).send(invalidJSONResp(err))
            return
        } 
        if(newBucket.orgID !== orgID ) {
            res.status(404).send(orgNotFoundReps)
            return
        }
        //console.log('Finding name ' + newBucket.name)
        let b = buckets.find((value)=> {
            //console.log('  testing ', value)
            return value.name?value.name === newBucket.name: false;
        })
        if(b) {
            res.status(422).send(conflictBucketResp(newBucket.name ))
            return
        }
        expire = 0
        if(newBucket.retentionRules && newBucket.retentionRules.length > 0) {
            expire = newBucket.retentionRules[0].everySeconds
        }
        let bucket = {
            "name": newBucket.name,
            "orgID": orgID,
            "expire": expire,
            "id": genID()
        }
        buckets.push(bucket)
        res.status(201).send(bucketResp(bucket))
    }
})

app.delete(prefix+'/api/v2/buckets/:id', (req,res) => {
    console.log('Delete buckets')
    if(handleAuthentication(req, res)) {
        let id = req.params['id']
        if(!id) {
            res.sendStatus(405)
            return
        }
        //console.log('Finding id ' + id)
        let i = buckets.findIndex((value)=> {
            //console.log('  testing ', value)
            return value.id?value.id === id:false
        })
        if(i<0) {
            res.status(404).send(notfoundResp)
            return
        }
        buckets.splice(i,1)
        res.sendStatus(204)
    }
})

function orgsResp(orgs) {
    return `{
	"links": {
		"self": "/api/v2/orgs"
	},
	"orgs": [
		${orgs}
	]
}`
}

const orgResp = `{
    "links": {
        "buckets": "/api/v2/buckets?org=my-org",
        "dashboards": "/api/v2/dashboards?org=my-org",
        "labels": "/api/v2/orgs/e2e2d84ffb3c4f85/labels",
        "logs": "/api/v2/orgs/e2e2d84ffb3c4f85/logs",
        "members": "/api/v2/orgs/e2e2d84ffb3c4f85/members",
        "owners": "/api/v2/orgs/e2e2d84ffb3c4f85/owners",
        "secrets": "/api/v2/orgs/e2e2d84ffb3c4f85/secrets",
        "self": "/api/v2/orgs/e2e2d84ffb3c4f85",
        "tasks": "/api/v2/tasks?org=my-org"
    },
    "id": "${orgID}",
    "name": "${orgName}",
    "description": "",
    "createdAt": "2021-08-18T06:24:02.427946Z",
    "updatedAt": "2021-08-18T06:24:02.427946Z"
}`

app.get(prefix+'/api/v2/orgs', (req,res) => {
    if(handleAuthentication(req, res)) {
        var name = req.query['org'];
        if(name){
            if(name === orgName) {
                res.status(200).send(orgsResp(orgResp))
            } else {
                res.status(200).send(orgsResp(""))
            }
        } else {
            res.status(200).send(orgsResp(orgResp))
        }
    }
})

function parsePoints(data) {
    var lines = data.split("\n");
    var points = [];
    lines.forEach((line, index) => {
        var parts = line.split(" ");
        if (parts.length > 1) {
            var measTags = parts[0];
            var fields = parts[1].split(",");
            var point = {};
            var keys = measTags.split(",");
            point.measurement = keys[0];
            point.tags = {};
            point.fields = {};
            if (keys.length > 1) {
                for (var i = 1; i < keys.length; i++) {
                    var keyval = keys[i].split("=");
                    point.tags[keyval[0]] = keyval[1];
                }
            }
            for (var i = 0; i < fields.length; i++) {
                var keyval = fields[i].split("=");
                var value = keyval[1];
                if (typeof value === 'string' && value.endsWith("i")) {
                    value = value.substring(0, value.length - 1);
                }
                point.fields[keyval[0]] = value;
            }
            if(parts.length>2) {
                point.timestamp = parts[2];
            }
            points.push(point);
        }
    });
    if (points.length > 0) {
        return points;
    }
    else {
       return data;
    }
}

const AuthToken = "Token my-token";
function handleAuthentication(req, res) {
    var auth = req.get('Authorization');
    if(auth && auth != AuthToken) {
        res.status(401).send(`{"code":"unauthorized","message":"unauthorized access"}`);
        return false;
    } 
    let u = req.query['u']
    let p = req.query['p']
    if(u && p && (p != "my secret password" || u != "user")) {
        res.status(401).send(`{"code":"unauthorized","message":"invalid user or password"}`);
        return false;
    }
    return true;
}

const AllowedPrecisions = ['ns','us','ms','s'];
function checkWriteParams(req, res) {
    var bucket = req.query['bucket'];
    var precision = req.query['precision'];
    if(!checkOrg(req, res)) {
        return false;
    } else if(bucket != 'my-bucket') {
        res.status(404).send(`{"code":"not found","message":"bucket \"${bucket}\" not found"}`);
        return false;
    } else if(typeof precision !== 'undefined' && AllowedPrecisions.indexOf(precision)==-1) {
        res.status(400).send(`{"code":"bad request ","message":"precision \"${precision}\" is not valid"}`);
        return false;
    } else {
        return true;
    }
}

const AllowedPrecisionsV1 = ['ns','u','ms','s'];
function checkWriteParamsV1(req, res) {
    var db = req.query['db'];
    var user = req.query['u'];
    var pass = req.query['p'];
    var precision = req.query['precision'];
    if(db != 'my-db') {
        res.status(404).send(`{"code":"not found","message":"database \"${db}\" not found"}`);
        return false;
    } else if(typeof precision !== 'undefined' && AllowedPrecisionsV1.indexOf(precision)==-1) {
        res.status(400).send(`precision \"${precision}\" is not valid`);
        return false;
    } else if (user !== 'user' && pass != 'my secret pass') {
        res.status(401).send("unauthorized")
    } else {
        return true;
    }
}

function checkQueryParams(req, res) {
    let org = req.query['org']
    if(org) {
        return checkOrg(req, res);
    } 
    return true
}

function checkOrg(req, res) {
    var org = req.query['org'];
    if(org !== 'my-org') {
        res.status(404).send(`{"code":"not found","message":"organization name \"${org}\" not found"}`);
        return false;
    } else {
        return true;
    }
}

function objectToCSV(obj, type, level) {
    var line = '';
    if(level == 1) line = type==0?'#datatype,':',';
    var i = 0;
    for (var index in obj) {
        if (i>0) line += ',';
        if(typeof obj[index] == 'object') {
            line += objectToCSV(obj[index], type, level+1);
        } else if(type == 0) { //datatype header
            line += 'string';
        } else if(type == 1) {
            line += index;
        } else {
            line += obj[index];
        }
        i++;
    }
    return line;
}

function convertToCSV(objArray) {
    var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
    var str = '';

    if(array.length > 0) {
        str = objectToCSV(array[0], 0, 1) + '\r\n';
        str += objectToCSV(array[0], 1, 1) + '\r\n';
    }

    for (var i = 0; i < array.length; i++) {
        var line = '';
        line = objectToCSV(array[i], 2, 1);
        
        str += line + '\r\n';
    }

    return str;
}

var mgmtServer = mgmtApp.listen(mgmtPort)

var rl = readline.createInterface(process.stdin, process.stdout);

rl.on('line', function(line) {
    rl.close();
}).on('close',function(){
    if(server !== undefined) {
        server.close();
    }
    mgmtServer.close();
    process.exit(0);
});


var ifaces = os.networkInterfaces();

console.log("Available interfaces:")
Object.keys(ifaces).forEach(function (ifname) {
  var alias = 0;

  ifaces[ifname].forEach(function (iface) {
    if ('IPv4' !== iface.family || iface.internal !== false) {
      // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
      return;
    }

    if (alias >= 1) {
      // this single interface has multiple ipv4 addresses
      console.log('  ', ifname + ':' + alias, iface.address);
    } else {
      // this interface has only one ipv4 adress
      console.log('  ', ifname, iface.address);
    }
    ++alias;
  });
});
console.log(`Listening on http://${mgmtServer.address().address}:${mgmtServer.address().port}`)
console.log(`Press Enter to exit`)
