package info.knacki.pass.io;

import android.content.Context;
import android.content.res.Resources;

import java.io.File;
import java.util.AbstractMap;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Logger;

import info.knacki.gitdroid.callback.OnErrorListener;
import info.knacki.pass.R;
import info.knacki.pass.io.pgp.GPGFileInterface;
import info.knacki.pass.settings.SettingsManager;

public class FileInterfaceFactory {
    private static final Logger log = Logger.getLogger(FileInterfaceFactory.class.getName());
    private static final ArrayDeque<EncryptionMethodItem> fEncryptionMethods = new ArrayDeque<>();

    static {
        fEncryptionMethods.add(new EncryptionMethodItem((ctx, passwordGetter, file) -> new RawFileInterface(ctx.getResources(), file), SettingsManager.EncryptionType.TYPE_RAW,".raw", R.string.pref_enc_type_title_none));
        fEncryptionMethods.add(new EncryptionMethodItem(GPGFileInterface::new, SettingsManager.EncryptionType.TYPE_GPG,".gpg", R.string.pref_enc_type_title_gpg));
        fEncryptionMethods.add(new EncryptionMethodItem(PasswordFileInterface::new, SettingsManager.EncryptionType.TYPE_PASSWORD, PasswordFileInterface.PASSWORD_SUFFIX, R.string.pref_enc_type_title_password));
    }

    public interface OnPasswordEnteredListener extends OnErrorListener {
        boolean OnResponse(String password);
    }

    public interface PasswordGetter {
        PasswordGetter GetPassword(OnPasswordEnteredListener onPassword);
        PasswordGetter SetFile(File file);
    }

    public interface ChangePasswordGetter extends PasswordGetter {
        enum eStep {
            eOldPassword,
            eNewPassword,
            eRetypePassword
        }

        ChangePasswordGetter SetStep(eStep step);
    }

    private static class EncryptionMethodItem {
        final IFileInterface.IFileInterfaceCreator fFactory;
        final String fFileSuffix;
        final int fName;
        final SettingsManager.EncryptionType fEncryptionType;

        EncryptionMethodItem(IFileInterface.IFileInterfaceCreator factory, SettingsManager.EncryptionType type, String fileSuffix, int name) {
            fFactory = factory;
            fFileSuffix = fileSuffix;
            fName = name;
            fEncryptionType = type;
        }
    }

    protected static EncryptionMethodItem GetEncryptionMethod(File f) {
        String fileName = f.getName();
        if (PathUtils.IsHidden(f.getAbsolutePath())) {
            fileName = PathUtils.UnHiddenPath(fileName);
        }
        for (EncryptionMethodItem i: fEncryptionMethods)
            if (fileName.endsWith(i.fFileSuffix))
                return i;
        log.severe("Unknown Interface for file type " +f.getAbsolutePath());
        throw new NoSuchMethodError();
    }

    public static SettingsManager.EncryptionType GetEncryptionType(File f) {
        return GetEncryptionMethod(f).fEncryptionType;
    }

    public static IFileInterface GetFileInterface(Context ctx, PasswordGetter passInput, File f) {
        return GetEncryptionMethod(f).fFactory.Create(ctx, passInput, f);
    }

    public static String GetExtension(SettingsManager.EncryptionType type) {
        for (EncryptionMethodItem i: fEncryptionMethods)
            if (i.fEncryptionType.equals(type))
                return i.fFileSuffix;
        return "";
    }

    public static Collection<Map.Entry<SettingsManager.EncryptionType, String>> GetEncryptionMethodNames(Resources r) {
        ArrayDeque<Map.Entry<SettingsManager.EncryptionType, String>> result = new ArrayDeque<>(fEncryptionMethods.size());
        for (EncryptionMethodItem enc: fEncryptionMethods)
            result.add(new AbstractMap.SimpleEntry<>(enc.fEncryptionType, r.getString(enc.fName)));
        return result;
    }
}
