/*!
 * Copyright 2017 Vpn.Email network security technology Canada Inc. All Rights Reserved.
 *
 * Vpn.Email network technolog Canada Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as crypto from 'crypto'

export interface packetBuffer {
	command: number;
	uuid: string;
	buffer: Buffer;
	serial: number
}

export interface pairConnect {
	serverListen: string;
	clientListen: string;
}

export const encrypt = ( text: Buffer, masterkey: string, CallBack ) => {

		// random initialization vector
		const iv = crypto.randomBytes ( 12 );

		// random salt
		const salt = crypto.randomBytes ( 64 );

		// derive key: 32 byte key length - in assumption the masterkey is a cryptographic and NOT a password there is no need for
		// a large number of iterations. It may can replaced by HKDF
		try {
			crypto.pbkdf2 ( masterkey, salt, 2145, 32, 'sha512', ( err, derivedKey ) => {

				if ( err )
					return CallBack ( err )

				// AES 256 GCM Mode
				const cipher = crypto.createCipheriv ( 'aes-256-gcm', derivedKey, iv );

				// encrypt the given text
				const encrypted = Buffer.concat ([ cipher.update ( text ), cipher.final ()]);
				// generate output

				const ret = Buffer.concat ([ salt, iv, cipher.getAuthTag (), encrypted ])

				
				return CallBack ( null, ret)
			})
		} catch ( ex ) {
			console.log ('encrypt catch error!')
			return CallBack ( ex )
		}
		

		

}
/**
 * Decrypts text by given key
 * @param String base64 encoded input data
 * @param Buffer masterkey
 * @returns String decrypted (original) text
 */
export const decrypt =  ( data: Buffer, masterkey, CallBack ) => {
	if ( !data || !data.length )
		return CallBack ( new Error('null'))
	try {
		// base64 decoding

		// convert data to buffers

		const salt = data.slice ( 0, 64 );
		const iv = data.slice ( 64, 76 );
		const tag = data.slice ( 76, 92 );
		const text = data.slice ( 92 );

		// derive key using; 32 byte key length
		crypto.pbkdf2 ( masterkey, salt , 2145, 32, 'sha512', ( err, derivedKey ) => {

			if ( err )
				return CallBack ( err )
			// AES 256 GCM Mode
			try {
				const decipher = crypto.createDecipheriv ( 'aes-256-gcm', derivedKey, iv );
				decipher.setAuthTag ( tag );

				const decrypted = decipher.update ( text ) + decipher.final ( 'utf8' )
				return CallBack ( null, decrypted )
			} catch ( ex ) {
				
			}
			

			
		})

	} catch ( e ) {
		return CallBack ( e )
	}

}

export const packetBuffer = ( bit0: number, _serial: number, id: string, buffer: Buffer ) => {
	
	const _buffer = new Buffer ( 6 )
	_buffer.fill ( 0 )
	_buffer.writeUInt8 ( bit0, 0 )
	_buffer.writeUInt32BE ( _serial, 1 )

	const uuid = new Buffer ( id, 'utf8' )
	_buffer.writeUInt8 ( id.length, 5 )
	if ( buffer && buffer.length )
		return Buffer.concat ([ _buffer, uuid, buffer ])
	return Buffer.concat ([ _buffer, uuid ])
}

export const openPacket = ( buffer: Buffer ) => {
	const idLength = buffer.readUInt8 ( 5 )
	return  {
		command: buffer.readUInt8 ( 0 ),
		serial: buffer.readUInt32BE ( 1 ),
		uuid: buffer.toString ( 'utf8', 6, 6 + idLength ),
		buffer: buffer.slice ( 6 + idLength )
	}
}
