// 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 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.UnicodeString;
import com.googlecode.objectify.annotation.Subclass;

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

@Subclass
public class FidoU2fAttestationStatement extends AttestationStatement {
  public byte[] sig;
  public byte[] attestnCert;
  public List<byte[]> caCert;

  /**
   * @param sig
   * @param attestnCert
   * @param caCert
   */
  public FidoU2fAttestationStatement(byte[] sig, byte[] attestnCert, List<byte[]> caCert) {
    super();
    this.sig = sig;
    this.attestnCert = attestnCert;
    this.caCert = caCert;
  }

  public FidoU2fAttestationStatement() {

  }

  /**
   * @param attStmt
   * @return Decoded FidoU2fAttestationStatement
   */
  public static FidoU2fAttestationStatement decode(DataItem attStmt) {
    FidoU2fAttestationStatement result = new FidoU2fAttestationStatement();
    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) {
        if (((UnicodeString) data).getString().equals("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());
          }
        } else if (((UnicodeString) data).getString().equals("sig")) {
          result.sig = ((ByteString) (given.get(data))).getBytes();
        }
      }
    }
    return result;
  }

  @Override
  DataItem encode() throws CborException {
    Map result = new Map();
    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);
    result.put(new UnicodeString("sig"), new ByteString(sig));

    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof FidoU2fAttestationStatement)) {
      return false;
    }
    FidoU2fAttestationStatement other = (FidoU2fAttestationStatement) obj;
    if (!Arrays.equals(attestnCert, other.attestnCert)) {
      return false;
    }
    if (!Arrays.equals(sig, other.sig)) {
      return false;
    }
    if (caCert.size() == other.caCert.size()) {
      return false;
    }
    for (int i = 0; i < caCert.size(); i++) {
      if (!Arrays.equals(caCert.get(i), other.caCert.get(i))) {
        return false;
      }
    }
    return true;
  }

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

  @Override
  public String getName() {
    return "FIDO U2F Authenticator";
  }
}