package im.status.keycard;

import im.status.keycard.applet.ApplicationStatus;
import im.status.keycard.applet.KeycardCommandSet;
import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
import org.web3j.crypto.ECKeyPair;

import java.io.IOException;
import java.security.PrivateKey;
import java.security.interfaces.ECPrivateKey;


public class TestKeycardCommandSet extends KeycardCommandSet {
  public TestKeycardCommandSet(CardChannel apduChannel) {
    super(apduChannel);
  }

  public void setSecureChannel(TestSecureChannelSession secureChannel) {
    super.setSecureChannel(secureChannel);
  }

  /**
   * Sends a LOAD KEY APDU. The key is sent in TLV format, includes the public key and no chain code, meaning that
   * the card will not be able to do further key derivation. This is needed when the argument is an EC keypair from
   * the web3j package instead of the regular Java ones. Used by the test which actually submits the transaction to
   * the network.
   *
   * @param ecKeyPair a key pair
   * @return the raw card response
   * @throws IOException communication error
   */
  public APDUResponse loadKey(ECKeyPair ecKeyPair) throws IOException {
    byte[] publicKey = ecKeyPair.getPublicKey().toByteArray();
    byte[] privateKey = ecKeyPair.getPrivateKey().toByteArray();

    int pubLen = publicKey.length;
    int pubOff = 0;

    if(publicKey[0] == 0x00) {
      pubOff++;
      pubLen--;
    }

    byte[] ansiPublic = new byte[pubLen + 1];
    ansiPublic[0] = 0x04;
    System.arraycopy(publicKey, pubOff, ansiPublic, 1, pubLen);

    return loadKey(ansiPublic, privateKey, null);
  }

  /**
   * Sends a LOAD KEY APDU. The given private key and chain code are formatted as a raw binary seed and the P1 of
   * the command is set to LOAD_KEY_P1_SEED (0x03). This works on cards which support public key derivation.
   * The loaded keyset is extended and support further key derivation.
   *
   * @param aPrivate a private key
   * @param chainCode the chain code
   * @return the raw card response
   * @throws IOException communication error
   */
  public APDUResponse loadKey(PrivateKey aPrivate, byte[] chainCode) throws IOException {
    byte[] privateKey = ((ECPrivateKey) aPrivate).getS().toByteArray();

    int privLen = privateKey.length;
    int privOff = 0;

    if(privateKey[0] == 0x00) {
      privOff++;
      privLen--;
    }

    byte[] data = new byte[chainCode.length + privLen];
    System.arraycopy(privateKey, privOff, data, 0, privLen);
    System.arraycopy(chainCode, 0, data, privLen, chainCode.length);

    return loadKey(data, LOAD_KEY_P1_SEED);
  }

  /**
   * Sends a GET STATUS APDU to retrieve the APPLICATION STATUS template and reads the byte indicating key initialization
   * status
   *
   * @return whether the master key is present or not
   * @throws IOException communication error
   */
  public boolean getKeyInitializationStatus() throws IOException {
    APDUResponse resp = getStatus(GET_STATUS_P1_APPLICATION);
    return new ApplicationStatus(resp.getData()).hasMasterKey();
  }
}