/*
Copyright 2017-2018 @ irontiga and vbcs (original developer)
*/
'use strict'
import Base58 from './deps/Base58.js'
import { Sha256, Sha512 } from 'asmcrypto.js'
import nacl from './deps/nacl-fast.js'
import utils from './deps/utils.js'

import { generateSaveWalletData } from './storeWallet.js'

import publicKeyToAddress from './wallet/publicKeyToAddress.js'
import AltcoinHDWallet from "./bitcoin/AltcoinHDWallet"

export default class PhraseWallet {
	constructor(seed, walletVersion) {

		this._walletVersion = walletVersion || 2
		this.seed = seed

		this.savedSeedData = {}
		this.hasBeenSaved = false
	}

	set seed(seed) {
		this._byteSeed = seed
		this._base58Seed = Base58.encode(seed)

		this._addresses = []

		this.genAddress(0)
	}

	getAddress(nonce) {
		return this._addresses[nonce]
	}

	get addresses() {
		return this._addresses
	}

	get addressIDs() {
		return this._addresses.map(addr => {
			return addr.address
		})
	}

	get seed() {
		return this._byteSeed
	}

	addressExists(nonce) {
		return this._addresses[nonce] != undefined
	}

	_genAddressSeed(seed) {
		let newSeed = new Sha512().process(seed).finish().result
		newSeed = new Sha512().process(utils.appendBuffer(newSeed, seed)).finish().result
		return newSeed
	}

	genAddress(nonce) {
		if (nonce >= this._addresses.length) {
			this._addresses.length = nonce + 1
		}

		if (this.addressExists(nonce)) {
			return this.addresses[nonce]
		}

		const nonceBytes = utils.int32ToBytes(nonce)

		let addrSeed = new Uint8Array()
		addrSeed = utils.appendBuffer(addrSeed, nonceBytes)
		addrSeed = utils.appendBuffer(addrSeed, this._byteSeed)
		addrSeed = utils.appendBuffer(addrSeed, nonceBytes)

		if (this._walletVersion == 1) {
			addrSeed = new Sha256().process(
				new Sha256()
					.process(addrSeed)
					.finish()
					.result
			).finish().result

			addrSeed = this._byteSeed
		} else {
			addrSeed = this._genAddressSeed(addrSeed).slice(0, 32)
		}

		const addrKeyPair = nacl.sign.keyPair.fromSeed(new Uint8Array(addrSeed));

		const address = publicKeyToAddress(addrKeyPair.publicKey);
		const qoraAddress = publicKeyToAddress(addrKeyPair.publicKey, true);

		// Create Bitcoin HD Wallet 
		const btcSeed = [...addrSeed];
		const btcWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x0488ADE4,
				public: 0x0488B21E,
				prefix: 0
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: 0x6F
			}
		}).createWallet(new Uint8Array(btcSeed), false);

		// Create Litecoin HD Wallet 
		const ltcSeed = [...addrSeed];
		const ltcWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x0488ADE4,
				public: 0x0488B21E,
				prefix: 0x30
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: 0x6F
			}
		}).createWallet(new Uint8Array(ltcSeed), false, 'LTC');

		// Create Dogecoin HD Wallet 
		const dogeSeed = [...addrSeed];
		const dogeWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x02FAC398,
				public: 0x02FACAFD,
				prefix: 0x1E
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: 0x71
			}
		}).createWallet(new Uint8Array(dogeSeed), false, 'DOGE');

		// Create Digibyte HD Wallet 
		const dgbSeed = [...addrSeed];
		const dgbWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x0488ADE4,
				public: 0x0488B21E,
				prefix: 0x1E
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: 0x7E
			}
		}).createWallet(new Uint8Array(dgbSeed), false, 'DGB');

		// Create Ravencoin HD Wallet 
		const rvnSeed = [...addrSeed];
		const rvnWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x0488ADE4,
				public: 0x0488B21E,
				prefix: 0x3C
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: 0x6F
			}
		}).createWallet(new Uint8Array(rvnSeed), false, 'RVN');

		// Create Pirate Chain HD Wallet 
		const arrrSeed = [...addrSeed];
		const arrrWallet = new AltcoinHDWallet({
			mainnet: {
				private: 0x0488ADE4,
				public: 0x0488B21E,
				prefix: [0x16, 0x9A]
			},
			testnet: {
				private: 0x04358394,
				public: 0x043587CF,
				prefix: [0x14, 0x51]
			}
		}).createWallet(new Uint8Array(arrrSeed), false, 'ARRR');

		this._addresses[nonce] = {
			address,
			btcWallet,
			ltcWallet,
			dogeWallet,
			dgbWallet,
			rvnWallet,
			arrrWallet,
			qoraAddress,
			keyPair: {
				publicKey: addrKeyPair.publicKey,
				privateKey: addrKeyPair.secretKey
			},
			base58PublicKey: Base58.encode(addrKeyPair.publicKey),
			seed: addrSeed,
			nonce: nonce
		}
		return this._addresses[nonce]
	}

	generateSaveWalletData(...args) {
		return generateSaveWalletData(this, ...args)
	}
}