package info.knacki.pass.io.pgp;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.util.Base64;

import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import info.knacki.pass.io.FileUtils;
import info.knacki.pass.settings.SettingsManager;

public class SharedPreferenceGPGStorage implements GPGStorageEngine.IGPGStorage {
    private final static Logger log = Logger.getLogger(SharedPreferenceGPGStorage.class.getName());
    private static final String GPG_KEY_FILE = "GPGKeyFile";
    private final Context fContext;

    SharedPreferenceGPGStorage(Context context) {
        fContext = context;
    }

    private SharedPreferences GetPrefManager() {
        return fContext.getSharedPreferences(SettingsManager.SHARED_PREF_FILE, Context.MODE_PRIVATE);
    }

    @Override
    public boolean HasGPGKey() {
        return GetPrefManager().contains(GPG_KEY_FILE);
    }

    @Override
    public boolean SetGPGKeyContent(InputStream content) {
        if (content == null) {
            GetPrefManager().edit().remove(GPG_KEY_FILE).apply();
            return false;
        }
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            final int bufLen = 1024;
            byte[] buffer = new byte[bufLen];
            int len;
            do {
                len = content.read(buffer);
                bytes.write(buffer, 0, len);
            } while (len == bufLen);
            GetPrefManager().edit().putString(GPG_KEY_FILE, Base64.encodeToString(bytes.toByteArray(), 0)).apply();
        } catch (IOException e) {
            log.log(Level.SEVERE, "Cannot read file", e);
            RemoveGpgKey();
            return false;
        }
        return true;    }

    @Override
    public void ExportGPGKeyContent(OutputStream out) throws IOException {
        InputStream keyContent = GetGPGKeyContent();
        if (keyContent != null)
            FileUtils.pipe(keyContent, out);
    }

    @NonNull @Override public PGPSecretKeyRing GetGPGKeyRing() throws IOException {
        try {
            InputStream keyContent = GetGPGKeyContent();
            if (keyContent == null) {
                log.log(Level.WARNING, "Cannot read key stream input");
                throw new FileNotFoundException();
            }
            return new PGPSecretKeyRing(PGPUtil.getDecoderStream(keyContent), new JcaKeyFingerprintCalculator());
        } catch (PGPException e) {
            log.log(Level.WARNING, "Cannot create Keyring: " +e.getMessage(), e);
            throw new FileNotFoundException();
        }
    }

    @NonNull @Override public PGPSecretKey GetGPGSecretKey() throws IOException {
        return GetGPGKeyRing().getSecretKey();
    }

    @NonNull @Override public PGPSecretKey GetGPGSecretKey(long keyId) throws IOException {
        PGPSecretKey key = GetGPGKeyRing().getSecretKey(keyId);
        if (key == null)
            throw new FileNotFoundException(String.format("key %X", keyId));
        return key;
    }

    @Override
    public void RemoveGpgKey() {
        GetPrefManager().edit().remove(GPG_KEY_FILE).apply();
    }

    private InputStream GetGPGKeyContent() {
        String content = GetPrefManager().getString(GPG_KEY_FILE, null);
        if (content != null)
            return new ByteArrayInputStream(Base64.decode(content, 0));
        return null;
    }
}
