/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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 android.support.v4.hardware.fingerprint;

import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;

import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;

import java.security.Signature;

import javax.crypto.Cipher;
import javax.crypto.Mac;

/**
 * Actual FingerprintManagerCompat implementation for API level 23 and later.
 * @hide
 */
@RequiresApi(23)
@TargetApi(23)
@RestrictTo(LIBRARY_GROUP)
public final class FingerprintManagerCompatApi23 {

    private static FingerprintManager getFingerprintManager(Context ctx) {
        return ctx.getSystemService(FingerprintManager.class);
    }

    public static boolean hasEnrolledFingerprints(Context context) {
        return getFingerprintManager(context).hasEnrolledFingerprints();
    }

    public static boolean isHardwareDetected(Context context) {
        return getFingerprintManager(context).isHardwareDetected();
    }

    public static void authenticate(Context context, CryptoObject crypto, int flags, Object cancel,
            AuthenticationCallback callback, Handler handler) {
        getFingerprintManager(context).authenticate(wrapCryptoObject(crypto),
                (android.os.CancellationSignal) cancel, flags,
                wrapCallback(callback), handler);
    }

    private static FingerprintManager.CryptoObject wrapCryptoObject(CryptoObject cryptoObject) {
        if (cryptoObject == null) {
            return null;
        } else if (cryptoObject.getCipher() != null) {
            return new FingerprintManager.CryptoObject(cryptoObject.getCipher());
        } else if (cryptoObject.getSignature() != null) {
            return new FingerprintManager.CryptoObject(cryptoObject.getSignature());
        } else if (cryptoObject.getMac() != null) {
            return new FingerprintManager.CryptoObject(cryptoObject.getMac());
        } else {
            return null;
        }
    }

    private static CryptoObject unwrapCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
        if (cryptoObject == null) {
            return null;
        } else if (cryptoObject.getCipher() != null) {
            return new CryptoObject(cryptoObject.getCipher());
        } else if (cryptoObject.getSignature() != null) {
            return new CryptoObject(cryptoObject.getSignature());
        } else if (cryptoObject.getMac() != null) {
            return new CryptoObject(cryptoObject.getMac());
        } else {
            return null;
        }
    }

    private static FingerprintManager.AuthenticationCallback wrapCallback(
            final AuthenticationCallback callback) {
        return new FingerprintManager.AuthenticationCallback() {
            @Override
            public void onAuthenticationError(int errMsgId, CharSequence errString) {
                callback.onAuthenticationError(errMsgId, errString);
            }

            @Override
            public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
                callback.onAuthenticationHelp(helpMsgId, helpString);
            }

            @Override
            public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
                callback.onAuthenticationSucceeded(new AuthenticationResultInternal(
                        unwrapCryptoObject(result.getCryptoObject())));
            }

            @Override
            public void onAuthenticationFailed() {
                callback.onAuthenticationFailed();
            }
        };
    }

    public static class CryptoObject {

        private final Signature mSignature;
        private final Cipher mCipher;
        private final Mac mMac;

        public CryptoObject(Signature signature) {
            mSignature = signature;
            mCipher = null;
            mMac = null;
        }

        public CryptoObject(Cipher cipher) {
            mCipher = cipher;
            mSignature = null;
            mMac = null;
        }

        public CryptoObject(Mac mac) {
            mMac = mac;
            mCipher = null;
            mSignature = null;
        }

        public Signature getSignature() { return mSignature; }
        public Cipher getCipher() { return mCipher; }
        public Mac getMac() { return mMac; }
    }

    public static final class AuthenticationResultInternal {
        private CryptoObject mCryptoObject;

        public AuthenticationResultInternal(CryptoObject crypto) {
            mCryptoObject = crypto;
        }

        public CryptoObject getCryptoObject() { return mCryptoObject; }
    }

    public static abstract class AuthenticationCallback {

        public void onAuthenticationError(int errMsgId, CharSequence errString) { }
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
        public void onAuthenticationSucceeded(AuthenticationResultInternal result) { }
        public void onAuthenticationFailed() { }
    }
}

@@ -1277,38 +1277,37 @@
 i23 %7B%0A%0A    p
-rivate
+ublic
  static 
 FingerprintM
@@ -1298,30 +1298,27 @@
 tic 
-FingerprintManager get
+boolean hasEnrolled
 Fing
@@ -1324,23 +1324,17 @@
 gerprint
-Manager
+s
 (Context
@@ -1335,18 +1335,22 @@
 ontext c
+ontex
 t
-x
 ) %7B%0A    
@@ -1357,18 +1357,45 @@
     
-return ctx
+final FingerprintManager fp = context
 .get
@@ -1443,269 +1443,259 @@
     
-%7D%0A%0A
     
-public static boolean hasEnrolledFingerprints(Context context) %7B%0A        return getFingerprintManager(context).hasEnrolledFingerprints();%0A    %7D%0A%0A    public static boolean isHardwareDetected(Context context) %7B%0A        return getFingerprintManager(context)
+return (fp != null) && fp.hasEnrolledFingerprints();%0A    %7D%0A%0A    public static boolean isHardwareDetected(Context context) %7B%0A        final FingerprintManager fp = context.getSystemService(FingerprintManager.class);%0A        return (fp != null) && fp
 .isH
@@ -1892,19 +1892,22 @@
         
-get
+final 
 Fingerpr
@@ -1920,17 +1920,106 @@
 ager
-(
+ fp = 
 context
-)
+.getSystemService(FingerprintManager.class);%0A        if (fp != null) %7B%0A            fp
 .aut
@@ -2054,16 +2054,20 @@
 rypto),%0A
+    
         
@@ -2137,16 +2137,20 @@
         
+  
+  
 wrapCall
@@ -2175,16 +2175,26 @@
 ndler);%0A
+        %7D%0A
     %7D%0A%0A 

Handle missing FingerprintManager.