/*
*******************************************************************************    
*   Java Card Bitcoin Hardware Wallet
*   (c) 2015 Ledger
*   
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU Affero General Public License as
*   published by the Free Software Foundation, either version 3 of the
*   License, or (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU Affero General Public License for more details.
*
*   You should have received a copy of the GNU Affero General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************   
*/    

package com.ledger.wallet.test;

import java.math.BigInteger;
import java.util.Arrays;
import junit.framework.TestCase;
import com.licel.jcardsim.base.Simulator;
import com.licel.jcardsim.utils.AIDUtil;
import com.licel.jcardsim.utils.ByteUtil;
import com.licel.jcardsim.bouncycastle.asn1.ASN1Sequence;
import com.licel.jcardsim.bouncycastle.asn1.ASN1EncodableVector;
import com.licel.jcardsim.bouncycastle.asn1.ASN1Primitive;
import com.licel.jcardsim.bouncycastle.asn1.DERInteger;
import com.licel.jcardsim.bouncycastle.asn1.DERSequence;
import javacard.framework.AID;
import javacard.framework.ISO7816;
import com.ledger.wallet.LedgerWalletApplet;
import com.btchip.BTChipDongle;
import com.btchip.BTChipConstants;
import com.btchip.BTChipException;


public abstract class AbstractTest extends TestCase {

	private static final BigInteger HALF_ORDER = new BigInteger(ByteUtil.byteArray("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0"));
	private static final BigInteger ORDER = new BigInteger(1, ByteUtil.byteArray("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"));

	public static final AID LOAD_FILE_AID = AIDUtil.create("FF4C4547522E57414C543031");	
	public static final byte[] INSTANCE_AID_DATA = ByteUtil.byteArray("FF4C4547522E57414C5430312E493031");
	public static final AID INSTANCE_AID = AIDUtil.create(INSTANCE_AID_DATA);

	public static final int TESTNET_VERSION = 111;
	public static final int TESTNET_P2SH_VERSION = 196;
	public static final byte[] DEFAULT_PIN = "1234".getBytes();
	// release afford clump fury license speak hungry remain crouch exile basic choose bar client own clip like armor forum fossil energy eight seven sausage
	public static final byte[] DEFAULT_SEED = ByteUtil.byteArray("d3c9b5146da60ebb8216ced62ecfc3a7dd3c7dc98f41a35e841cd5a659f0991bb7562be0d1138b2a5df2512004c8374162a2970d2a1277001f6614172e44f033");
	public static final byte DEFAULT_KEYCARD_ADDRESS_SIZE = (byte)4;	
	public static final byte[] DEFAULT_KEYCARD = ByteUtil.byteArray("f27c395759a14d3aec2135188d670d8e");

	protected Simulator simulator;

	protected Simulator prepareSimulator() {
		byte[] parameters = new byte[INSTANCE_AID_DATA.length + 3];
		parameters[0] = (byte)INSTANCE_AID_DATA.length;
		System.arraycopy(INSTANCE_AID_DATA, 0, parameters, 1, INSTANCE_AID_DATA.length);
		Simulator tmpSimulator = new Simulator();
		tmpSimulator.installApplet(LOAD_FILE_AID, LedgerWalletApplet.class, parameters, (short)0, (byte)parameters.length);
		return tmpSimulator;
	}

	protected BTChipDongle getDongle(boolean debug) throws BTChipException {
		this.simulator = prepareSimulator();
		assertTrue(simulator.selectApplet(INSTANCE_AID));		
		JCardSIMTransport transport = new JCardSIMTransport(simulator, debug);
		BTChipDongle dongle = new BTChipDongle(transport);
		dongle.setKeycardSeed(DEFAULT_KEYCARD_ADDRESS_SIZE, DEFAULT_KEYCARD);		
		return dongle;
	}

	protected BTChipDongle getDongle() throws BTChipException {
		return getDongle(false);
	}	

	protected BTChipDongle prepareDongleRestoreTestnet(boolean debug) throws BTChipException {
		BTChipDongle dongle = getDongle(debug);
		dongle.setup(
			new BTChipDongle.OperationMode[] { BTChipDongle.OperationMode.WALLET },
			new BTChipDongle.Feature[] { BTChipDongle.Feature.RFC6979, BTChipDongle.Feature.NO_2FA_P2SH},
			TESTNET_VERSION,
			TESTNET_P2SH_VERSION,
			DEFAULT_PIN,
			null,
			BTChipConstants.QWERTY_KEYMAP,
			DEFAULT_SEED,
			null);
		return dongle;
	}

	protected void reset() throws BTChipException {
		simulator.reset();
		assertTrue(simulator.selectApplet(INSTANCE_AID));		
	}

	protected byte[] canonicalizeSignature(byte[] signature) throws BTChipException {
		try {
			ASN1Sequence seq = (ASN1Sequence)ASN1Primitive.fromByteArray(signature);
			BigInteger r = ((DERInteger)seq.getObjectAt(0)).getValue();
			BigInteger s = ((DERInteger)seq.getObjectAt(1)).getValue();
			if (s.compareTo(HALF_ORDER) > 0) {
				s = ORDER.subtract(s);
			}
			else {
				return signature;
			}
 			ASN1EncodableVector v = new ASN1EncodableVector();
  			v.add(new DERInteger(r));
  			v.add(new DERInteger(s)); 
  			return new DERSequence(v).getEncoded("DER");			
		}
		catch(Exception e) {
			throw new BTChipException("Error canonicalizing signature", e);
		}
	}
}