/*
 * Copyright (C) 2017 Schürmann & Breitmoser GbR
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sufficientlysecure.keychain.remote.ui;


import java.io.Serializable;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;

import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgorithm;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureEncryptionAlgorithm;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.NotSecureCurve;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.UnidentifiedKeyProblem;
import org.sufficientlysecure.keychain.daos.OverriddenWarningsDao;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;


class SecurityProblemPresenter {
    private static final int OVERRIDE_REQUIRED_COUNT = 3;


    private final Context context;
    private final PackageManager packageManager;
    private final OverriddenWarningsDao overriddenWarningsDao;


    private RemoteSecurityProblemView view;
    private Long viewKeyMasterKeyId;
    private int overrideCounter;
    private String securityProblemIdentifier;

    private String packageName;
    private Serializable securityProblem;
    private boolean supportOverride;


    SecurityProblemPresenter(Context context) {
        this.context = context;
        packageManager = context.getPackageManager();
        overriddenWarningsDao = OverriddenWarningsDao.create(context);
    }

    public void setView(RemoteSecurityProblemView view) {
        this.view = view;
    }

    void setupFromIntentData(String packageName, Serializable securityProblem, boolean supportOverride) {
        this.packageName = packageName;
        this.securityProblem = securityProblem;
        this.supportOverride = supportOverride;

        refreshSecurityProblemDisplay();
        refreshPackageInfo();
    }

    private void refreshSecurityProblemDisplay() {
        if (securityProblem instanceof DecryptVerifySecurityProblem) {
            setupFromDecryptVerifySecurityProblem((DecryptVerifySecurityProblem) securityProblem);
        } else {
            throw new IllegalArgumentException("Unhandled security problem type!");
        }
    }

    private void setupFromDecryptVerifySecurityProblem(DecryptVerifySecurityProblem securityProblem) {
        if (securityProblem.encryptionKeySecurityProblem != null) {
            setupFromEncryptionKeySecurityProblem(securityProblem.encryptionKeySecurityProblem);
        } else if (securityProblem.signingKeySecurityProblem != null) {
            setupFromSigningKeySecurityProblem(securityProblem.signingKeySecurityProblem);
        } else if (securityProblem.symmetricSecurityProblem != null) {
            setupFromEncryptionAlgorithmSecurityProblem(securityProblem.symmetricSecurityProblem);
        } else if (securityProblem.signatureSecurityProblem != null) {
            setupFromSignatureSecurityProblem(securityProblem.signatureSecurityProblem);
        }
    }

    private void setupFromEncryptionKeySecurityProblem(KeySecurityProblem keySecurityProblem) {
        viewKeyMasterKeyId = keySecurityProblem.masterKeyId;
        view.showViewKeyButton();

        if (keySecurityProblem instanceof InsecureBitStrength) {
            InsecureBitStrength problem = (InsecureBitStrength) keySecurityProblem;
            view.showLayoutEncryptInsecureBitsize(problem.algorithm, problem.bitStrength);
        } else if (keySecurityProblem instanceof NotSecureCurve) {
            NotSecureCurve problem = (NotSecureCurve) keySecurityProblem;
            view.showLayoutEncryptNotSecureCurve(problem.curveOid);
        } else if (keySecurityProblem instanceof UnidentifiedKeyProblem) {
            view.showLayoutEncryptUnidentifiedKeyProblem();
        } else {
            throw new IllegalArgumentException("Unhandled key security problem type!");
        }

        if (keySecurityProblem.isIdentifiable()) {
            securityProblemIdentifier = keySecurityProblem.getIdentifier();
            refreshOverrideStatusView();
        }
    }

    private void setupFromSigningKeySecurityProblem(KeySecurityProblem keySecurityProblem) {
        viewKeyMasterKeyId = keySecurityProblem.masterKeyId;
        view.showViewKeyButton();

        if (keySecurityProblem instanceof InsecureBitStrength) {
            InsecureBitStrength problem = (InsecureBitStrength) keySecurityProblem;
            view.showLayoutSignInsecureBitsize(problem.algorithm, problem.bitStrength);
        } else if (keySecurityProblem instanceof NotSecureCurve) {
            NotSecureCurve problem = (NotSecureCurve) keySecurityProblem;
            view.showLayoutSignNotSecureCurve(problem.curveOid);
        } else if (keySecurityProblem instanceof UnidentifiedKeyProblem) {
            view.showLayoutSignUnidentifiedKeyProblem();
        } else {
            throw new IllegalArgumentException("Unhandled key security problem type!");
        }

        if (keySecurityProblem.isIdentifiable()) {
            securityProblemIdentifier = keySecurityProblem.getIdentifier();
            refreshOverrideStatusView();
        }
    }

    private void setupFromEncryptionAlgorithmSecurityProblem(EncryptionAlgorithmProblem securityProblem) {
        if (securityProblem instanceof MissingMdc) {
            view.showLayoutMissingMdc();
        } else if (securityProblem instanceof InsecureEncryptionAlgorithm) {
            InsecureEncryptionAlgorithm insecureSymmetricAlgorithm = (InsecureEncryptionAlgorithm) securityProblem;
            view.showLayoutInsecureSymmetric(insecureSymmetricAlgorithm.symmetricAlgorithm);
        } else {
            throw new IllegalArgumentException("Unhandled symmetric algorithm problem type!");
        }

        if (securityProblem.isIdentifiable()) {
            securityProblemIdentifier = securityProblem.getIdentifier();
            refreshOverrideStatusView();
        }
    }

    private void refreshOverrideStatusView() {
        if (supportOverride) {
            if (overriddenWarningsDao.isWarningOverridden(securityProblemIdentifier)) {
                view.showOverrideUndoButton();
            } else {
                view.showOverrideButton();
            }
        }
    }

    private void setupFromSignatureSecurityProblem(InsecureSigningAlgorithm signatureSecurityProblem) {
        view.showLayoutInsecureHashAlgorithm(signatureSecurityProblem.hashAlgorithm);
    }

    private void refreshPackageInfo() {
        ApplicationInfo applicationInfo;
        try {
            applicationInfo = packageManager.getApplicationInfo(packageName, 0);
        } catch (NameNotFoundException e) {
            throw new IllegalStateException("Could not retrieve package info!");
        }
        Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
        // CharSequence appName = packageManager.getApplicationLabel(applicationInfo);

        view.setTitleClientIcon(appIcon);
    }

    private void incrementOverrideAndDisplayOrTrigger() {
        int overrideCountLeft = OVERRIDE_REQUIRED_COUNT - overrideCounter;
        if (overrideCountLeft > 0) {
            overrideCounter++;
            view.showOverrideMessage(overrideCountLeft);
        } else {
            overriddenWarningsDao.putOverride(securityProblemIdentifier);
            view.finishAsSuppressed();
        }
    }

    private void resetOverrideStatus() {
        overrideCounter = 0;
        overriddenWarningsDao.deleteOverride(securityProblemIdentifier);
    }

    void onClickGotIt() {
        view.finishAsCancelled();
    }

    void onClickViewKey() {
        Intent intent = new Intent(context, ViewKeyActivity.class);
        intent.putExtra(ViewKeyActivity.EXTRA_MASTER_KEY_ID, viewKeyMasterKeyId);
        context.startActivity(intent);
    }

    void onClickOverride() {
        incrementOverrideAndDisplayOrTrigger();
    }

    void onClickOverrideUndo() {
        resetOverrideStatus();
        refreshSecurityProblemDisplay();
    }

    void onClickOverrideBack() {
        resetOverrideStatus();
        refreshSecurityProblemDisplay();
    }

    void onClickOverrideConfirm() {
        incrementOverrideAndDisplayOrTrigger();
    }

    void onCancel() {
        view.finishAsCancelled();
    }

    interface RemoteSecurityProblemView {
        void finishAsCancelled();
        void finishAsSuppressed();
        void setTitleClientIcon(Drawable drawable);

        void showLayoutEncryptInsecureBitsize(int algorithmId, int bitStrength);
        void showLayoutEncryptNotSecureCurve(String curveOid);
        void showLayoutEncryptUnidentifiedKeyProblem();
        void showLayoutSignInsecureBitsize(int algorithmId, int bitStrength);
        void showLayoutSignNotSecureCurve(String curveOid);
        void showLayoutSignUnidentifiedKeyProblem();

        void showLayoutMissingMdc();
        void showLayoutInsecureSymmetric(int symmetricAlgorithm);

        void showLayoutInsecureHashAlgorithm(int hashAlgorithm);

        void showOverrideMessage(int countdown);

        void showViewKeyButton();
        void showOverrideButton();
        void showOverrideUndoButton();
    }
}
