// Copyright 2017 Google Inc.
//
// 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.

package com.google.webauthn.gaedemo.objects;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import com.googlecode.objectify.annotation.Subclass;

import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.ByteString;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.Map;
import co.nstant.in.cbor.model.NegativeInteger;
import co.nstant.in.cbor.model.UnicodeString;

@Subclass
public class PackedAttestationStatement extends AttestationStatement {
  public byte[] sig;
  public byte[] attestnCert;
  public List<byte[]> caCert;
  public Algorithm alg;
  public byte[] ecdaaKeyId;

  /**
   * @param sig
   * @param attestnCert
   * @param caCert
   */
  public PackedAttestationStatement(byte[] sig, byte[] attestnCert, List<byte[]> caCert, String alg) {
    super();
    this.sig = sig;
    this.attestnCert = attestnCert;
    this.caCert = caCert;
    this.alg = Algorithm.decode(alg);
    this.ecdaaKeyId = null;
  }

  /**
   * @param sig
   * @param attestnCert
   * @param caCert
   */
  public PackedAttestationStatement(byte[] sig, byte[] ecdaaKeyId, String alg) {
    super();
    this.sig = sig;
    this.ecdaaKeyId = ecdaaKeyId;
    this.alg = Algorithm.decode(alg);
    this.caCert = null;
    this.attestnCert = null;
  }

  public PackedAttestationStatement() {
    this.sig = null;
    this.attestnCert = null;
    this.caCert = null;
    this.alg = null;
    this.ecdaaKeyId = null;
  }

  /**
   * @param attStmt
   * @return Decoded FidoU2fAttestationStatement
   */
  public static PackedAttestationStatement decode(DataItem attStmt) {
    PackedAttestationStatement result = new PackedAttestationStatement();
    Map given = null;

    if (attStmt instanceof ByteString) {
      byte[] temp = ((ByteString) attStmt).getBytes();
      List<DataItem> dataItems = null;
      try {
        dataItems = CborDecoder.decode(temp);
      } catch (Exception e) {
      }
      given = (Map) dataItems.get(0);
    } else {
      given = (Map) attStmt;
    }

    for (DataItem data : given.getKeys()) {
      if (data instanceof UnicodeString) {
        switch (((UnicodeString) data).getString()) {
          case "x5c":
            Array array = (Array) given.get(data);
            List<DataItem> list = array.getDataItems();
            if (list.size() > 0) {
              result.attestnCert = ((ByteString) list.get(0)).getBytes();
            }
            result.caCert = new ArrayList<byte[]>();
            for (int i = 1; i < list.size(); i++) {
              result.caCert.add(((ByteString) list.get(i)).getBytes());
            }
            break;
          case "sig":
            result.sig = ((ByteString) (given.get(data))).getBytes();
            break;
          case "alg":
            int algInt = new BigDecimal(((NegativeInteger) (given.get(data))).getValue()).intValueExact();
            result.alg = Algorithm.decode(algInt);
            break;
          case "ecdaaKeyId":
            result.ecdaaKeyId = ((ByteString) (given.get(data))).getBytes();
            break;
        }
      }
    }
    return result;
  }

  @Override
  DataItem encode() throws CborException {
    Map result = new Map();
    if (attestnCert != null) {
      Array x5c = new Array();
      x5c.add(new ByteString(attestnCert));
      for (byte[] cert : this.caCert) {
        x5c.add(new ByteString(cert));
      }
      result.put(new UnicodeString("x5c"), x5c);
    }
    if (ecdaaKeyId != null) {
      result.put(new UnicodeString("ecdaaKeyId"), new ByteString(ecdaaKeyId));
    }
    result.put(new UnicodeString("sig"), new ByteString(sig));
    result.put(new UnicodeString("alg"), new UnicodeString(alg.toString()));

    return result;
  }

  @Override
  public int hashCode() {
    return Objects.hash(Arrays.hashCode(sig), Arrays.hashCode(attestnCert), caCert, alg, Arrays.hashCode(ecdaaKeyId));
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof PackedAttestationStatement)) {
      return false;
    }
    PackedAttestationStatement other = (PackedAttestationStatement) obj;
    try {
      return encode().equals(other.encode());
    } catch (CborException e) {
    }
    return false;
  }

  @Override
  public String getName() {
    return "Packed Attestation";
  }
}