// Copyright 2015 Joyent, Inc.

module.exports = {

read: read.bind(undefined, false, undefined),
readType: read.bind(undefined, false),
write: write,
/* semi-private api, used by sshpk-agent */
readPartial: read.bind(undefined, true),

/* shared with ssh format */
readInternal: read,
keyTypeToAlg: keyTypeToAlg,
algToKeyType: algToKeyType

};

var assert = require('assert-plus'); var Buffer = require('safer-buffer').Buffer; var algs = require('../algs'); var utils = require('../utils'); var Key = require('../key'); var PrivateKey = require('../private-key'); var SSHBuffer = require('../ssh-buffer');

function algToKeyType(alg) {

assert.string(alg);
if (alg === 'ssh-dss')
        return ('dsa');
else if (alg === 'ssh-rsa')
        return ('rsa');
else if (alg === 'ssh-ed25519')
        return ('ed25519');
else if (alg === 'ssh-curve25519')
        return ('curve25519');
else if (alg.match(/^ecdsa-sha2-/))
        return ('ecdsa');
else
        throw (new Error('Unknown algorithm ' + alg));

}

function keyTypeToAlg(key) {

assert.object(key);
if (key.type === 'dsa')
        return ('ssh-dss');
else if (key.type === 'rsa')
        return ('ssh-rsa');
else if (key.type === 'ed25519')
        return ('ssh-ed25519');
else if (key.type === 'curve25519')
        return ('ssh-curve25519');
else if (key.type === 'ecdsa')
        return ('ecdsa-sha2-' + key.part.curve.data.toString());
else
        throw (new Error('Unknown key type ' + key.type));

}

function read(partial, type, buf, options) {

if (typeof (buf) === 'string')
        buf = Buffer.from(buf);
assert.buffer(buf, 'buf');

var key = {};

var parts = key.parts = [];
var sshbuf = new SSHBuffer({buffer: buf});

var alg = sshbuf.readString();
assert.ok(!sshbuf.atEnd(), 'key must have at least one part');

key.type = algToKeyType(alg);

var partCount = algs.info[key.type].parts.length;
if (type && type === 'private')
        partCount = algs.privInfo[key.type].parts.length;

while (!sshbuf.atEnd() && parts.length < partCount)
        parts.push(sshbuf.readPart());
while (!partial && !sshbuf.atEnd())
        parts.push(sshbuf.readPart());

assert.ok(parts.length >= 1,
    'key must have at least one part');
assert.ok(partial || sshbuf.atEnd(),
    'leftover bytes at end of key');

var Constructor = Key;
var algInfo = algs.info[key.type];
if (type === 'private' || algInfo.parts.length !== parts.length) {
        algInfo = algs.privInfo[key.type];
        Constructor = PrivateKey;
}
assert.strictEqual(algInfo.parts.length, parts.length);

if (key.type === 'ecdsa') {
        var res = /^ecdsa-sha2-(.+)$/.exec(alg);
        assert.ok(res !== null);
        assert.strictEqual(res[1], parts[0].data.toString());
}

var normalized = true;
for (var i = 0; i < algInfo.parts.length; ++i) {
        var p = parts[i];
        p.name = algInfo.parts[i];
        /*
         * OpenSSH stores ed25519 "private" keys as seed + public key
         * concat'd together (k followed by A). We want to keep them
         * separate for other formats that don't do this.
         */
        if (key.type === 'ed25519' && p.name === 'k')
                p.data = p.data.slice(0, 32);

        if (p.name !== 'curve' && algInfo.normalize !== false) {
                var nd;
                if (key.type === 'ed25519') {
                        nd = utils.zeroPadToLength(p.data, 32);
                } else {
                        nd = utils.mpNormalize(p.data);
                }
                if (nd.toString('binary') !==
                    p.data.toString('binary')) {
                        p.data = nd;
                        normalized = false;
                }
        }
}

if (normalized)
        key._rfc4253Cache = sshbuf.toBuffer();

if (partial && typeof (partial) === 'object') {
        partial.remainder = sshbuf.remainder();
        partial.consumed = sshbuf._offset;
}

return (new Constructor(key));

}

function write(key, options) {

assert.object(key);

var alg = keyTypeToAlg(key);
var i;

var algInfo = algs.info[key.type];
if (PrivateKey.isPrivateKey(key))
        algInfo = algs.privInfo[key.type];
var parts = algInfo.parts;

var buf = new SSHBuffer({});

buf.writeString(alg);

for (i = 0; i < parts.length; ++i) {
        var data = key.part[parts[i]].data;
        if (algInfo.normalize !== false) {
                if (key.type === 'ed25519')
                        data = utils.zeroPadToLength(data, 32);
                else
                        data = utils.mpNormalize(data);
        }
        if (key.type === 'ed25519' && parts[i] === 'k')
                data = Buffer.concat([data, key.part.A.data]);
        buf.writeBuffer(data);
}

return (buf.toBuffer());

}