package info.knacki.pass.services;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import info.knacki.pass.R;
import info.knacki.pass.generator.PasswordGenerator;
import info.knacki.pass.generator.ui.PasswordGeneratorWizard;
import info.knacki.pass.io.FileInterfaceFactory;
import info.knacki.pass.io.FileUtils;
import info.knacki.pass.io.IFileInterface;
import info.knacki.gitdroid.callback.OnResponseListener;
import info.knacki.pass.settings.SettingsManager;
import info.knacki.pass.ui.MainActivity;
import info.knacki.pass.ui.alertPrompt.ServiceAlertPromptGenerator;
import info.knacki.pass.ui.alertPrompt.views.SimpleTextEditWithKeyboard;
import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;

public class InputService extends InputMethodService implements AccessibilityView.AccessibilityViewListener {
    protected AccessibilityView fPasswordListView;
    private static final Logger log = Logger.getLogger(InputService.class.getName());
    protected View fInputView;

    @Override
    public void onStartInputView(EditorInfo info, boolean restarting) {
        if (info.packageName.equals(getApplicationContext().getPackageName())) {
            SwitchToPreviousInputMethod();
            return;
        }
        super.onStartInputView(info, restarting);
        if (fPasswordListView != null) {
            fPasswordListView.reset();
        }
    }

    private void PromptInputService() {
        InputMethodManager service = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (null == service) {
            Toast.makeText(InputService.this, "Android error", Toast.LENGTH_LONG).show();
            log.log(Level.SEVERE, "Cannot get Input method service");
            return;
        }
        service.showInputMethodPicker();
    }

    private void SwitchToPreviousInputMethod() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            if (!switchToPreviousInputMethod())
                PromptInputService();
        } else {
            InputMethodManager service = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            if (null == service) {
                Toast.makeText(InputService.this, "Android error", Toast.LENGTH_LONG).show();
                log.log(Level.SEVERE, "Cannot get Input method service");
                return;
            }
            Window win = getWindow().getWindow();
            if (win != null && !service.switchToLastInputMethod(win.getAttributes().token))
                PromptInputService();
        }
    }

    @SuppressLint("InflateParams")
    @Override
    public View onCreateInputView() {
        fInputView = LayoutInflater.from(this).inflate(R.layout.input, null, false);
        fInputView.findViewById(R.id.prevButton).setOnClickListener(view -> SwitchToPreviousInputMethod());
        fInputView.findViewById(R.id.backspaceButton).setOnTouchListener(new View.OnTouchListener() {
            private final static int INIT_DELAY = 650;
            private final static int DELAY = 250;
            private Handler handler;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        if (handler != null)
                            return true;
                        handler = new Handler();
                        handler.postDelayed(fBackspaceHandler, INIT_DELAY);
                        break;
                    case MotionEvent.ACTION_UP:
                        if (handler != null) {
                            handler.removeCallbacks(fBackspaceHandler);
                            handler = null;
                        }
                        fBackspaceHandler.run();
                        v.performClick();
                        break;
                }
                return false;
            }

            private final Runnable fBackspaceHandler = new Runnable() {
                @Override
                public void run() {
                    getCurrentInputConnection().deleteSurroundingText(1, 0);
                    if (handler != null)
                        handler.postDelayed(this, DELAY);
                }
            };
        });
        fInputView.findViewById(R.id.generateButton).setOnClickListener(view -> CreatePassword());
        fInputView.findViewById(R.id.openAppButton).setOnClickListener(view -> startActivity(new Intent(InputService.this, MainActivity.class)));
        fPasswordListView = ((AccessibilityView) fInputView.findViewById(R.id.accessibility)).Init(this, this);
        return fInputView;
    }

    protected void CreatePassword() {
        new ServiceAlertPromptGenerator(fInputView.getWindowToken()).Generate(this)
                .setCancelable(true)
                .setView(new SimpleTextEditWithKeyboard(this))
                .setPositiveButton(R.string.add, (dialogInterface, view) -> {
                    final String filename = ((SimpleTextEditWithKeyboard) view).getStr();
                    if (filename.length() == 0) {
                        Toast.makeText(InputService.this, "Error: Empty file name", Toast.LENGTH_LONG).show();
                        return;
                    }
                    File f = new File(fPasswordListView.GetCurrentDir() +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(InputService.this)));
                    try {
                        FileUtils.Touch(f);
                        fPasswordListView.refresh();
                    }
                    catch (IOException e) {
                        Toast.makeText(InputService.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
                        log.log(Level.WARNING, e.getMessage(), e);
                        return;
                    }
                    GeneratePassword(f);
                })
                .setNegativeButton(R.string.cancel, null)
                .setTitle(R.string.add_title)
                .show();
    }

    protected void GeneratePassword(final File f) {
        final PasswordGeneratorWizard wiz = new PasswordGeneratorWizard(this);

        new ServiceAlertPromptGenerator(fInputView.getWindowToken()).Generate(this)
                .setCancelable(true)
                .setView(wiz)
                .setPositiveButton(R.string.add, (dialogInterface, view) -> {
                    IFileInterface writer = FileInterfaceFactory.GetFileInterface(InputService.this, PasswordPickerFactory.GetPasswordPicker(InputService.this), f);

                    try {
                        final String pwd = PasswordGenerator.generate(wiz);
                        writer.WriteFile(pwd, new OnResponseListener<Void>() {
                            @Override
                            public void OnResponse(Void result) {
                                sendPassword(pwd);
                            }

                            @Override
                            public void OnError(final String msg, final Throwable e) {
                            }
                        });
                    }
                    catch (IFileInterface.InvalidPasswordException e) {
                        FileUtils.DeleteFile(f);
                        log.log(Level.WARNING, e.getMessage(), e);
                        Toast.makeText(InputService.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
                        fPasswordListView.refresh();
                    }
                })
                .setNegativeButton(R.string.cancel, null)
                .setTitle(R.string.generate_title)
                .show();
    }

    public void sendPassword(String passwordContent) {
        getCurrentInputConnection().commitText(passwordContent, passwordContent.length());
        onFinishInput();
        SwitchToPreviousInputMethod();
    }

    @Override
    public void OnPasswordClicked(File f) {
        FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this, fInputView.getWindowToken()), f).ReadFile(new OnResponseListener<String>() {
            @Override
            public void OnResponse(String passwordContent) {
                new android.os.Handler(getMainLooper()).post(() -> sendPassword(passwordContent));
            }

            @Override
            public void OnError(String msg, Throwable e) {
                Toast.makeText(InputService.this, "Error: " + msg, Toast.LENGTH_LONG).show();
                log.log(Level.SEVERE, msg, e);
            }
        });
    }

    @Override
    public void cancel() {
    }
}
