/*
    Copyright 2021-2023 Picovoice Inc.

    You may not use this file except in compliance with the license. A copy of the license is
    located in the "LICENSE" file accompanying this source.

    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 ai.picovoice.porcupine.demo;


import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;

import ai.picovoice.porcupine.Porcupine;
import ai.picovoice.porcupine.PorcupineActivationException;
import ai.picovoice.porcupine.PorcupineActivationLimitException;
import ai.picovoice.porcupine.PorcupineActivationRefusedException;
import ai.picovoice.porcupine.PorcupineActivationThrottledException;
import ai.picovoice.porcupine.PorcupineException;
import ai.picovoice.porcupine.PorcupineInvalidArgumentException;
import ai.picovoice.porcupine.PorcupineManager;
import ai.picovoice.porcupine.PorcupineManagerCallback;


public class MainActivity extends AppCompatActivity {
    private static final String ACCESS_KEY = "${YOUR_ACCESS_KEY_HERE}";
    private PorcupineManager porcupineManager = null;
    private MediaPlayer notificationPlayer;
    private final PorcupineManagerCallback porcupineManagerCallback = new PorcupineManagerCallback() {
        @Override
        public void invoke(int keywordIndex) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (!notificationPlayer.isPlaying()) {
                        notificationPlayer.start();
                    }

                    final int detectedBackgroundColor = ContextCompat.getColor(
                            getApplicationContext(),
                            R.color.colorAccent);
                    final RelativeLayout layout = findViewById(R.id.layout);
                    layout.setBackgroundColor(detectedBackgroundColor);
                    new CountDownTimer(1000, 100) {
                        @Override
                        public void onTick(long millisUntilFinished) {
                            if (!notificationPlayer.isPlaying()) {
                                notificationPlayer.start();
                            }
                        }

                        @Override
                        public void onFinish() {
                            layout.setBackgroundColor(Color.TRANSPARENT);
                        }
                    }.start();
                }
            });
        }
    };

    private void startPorcupine() {
        try {
            final Spinner mySpinner = findViewById(R.id.keyword_spinner);
            final String keywordName = mySpinner.getSelectedItem().toString();

            PorcupineManager.Builder builder = new PorcupineManager.Builder()
                    .setAccessKey(ACCESS_KEY)
                    .setSensitivity(0.7f);
            if (Objects.equals(BuildConfig.FLAVOR, "en")) {
                String keyword = keywordName.toUpperCase().replace(" ", "_");
                builder.setKeyword(Porcupine.BuiltInKeyword.valueOf(keyword));
            } else {
                String keywordFile = keywordName.toLowerCase().replace(" ", "_") + ".ppn";
                builder.setKeywordPath("keywords/" + keywordFile);
                String model = "porcupine_params_" + BuildConfig.FLAVOR + ".pv";
                builder.setModelPath("models/" + model);
            }

            porcupineManager = builder.build(getApplicationContext(), porcupineManagerCallback);
            porcupineManager.start();
        } catch (PorcupineInvalidArgumentException e) {
            onPorcupineInitError(
                    String.format("%s\nEnsure your accessKey '%s' is a valid access key.", e.getMessage(), ACCESS_KEY)
            );
        } catch (PorcupineActivationException e) {
            onPorcupineInitError("AccessKey activation error");
        } catch (PorcupineActivationLimitException e) {
            onPorcupineInitError("AccessKey reached its device limit");
        } catch (PorcupineActivationRefusedException e) {
            onPorcupineInitError("AccessKey refused");
        } catch (PorcupineActivationThrottledException e) {
            onPorcupineInitError("AccessKey has been throttled");
        } catch (PorcupineException e) {
            onPorcupineInitError("Failed to initialize Porcupine " + e.getMessage());
        }
    }

    private void stopPorcupine() {
        if (porcupineManager != null) {
            try {
                porcupineManager.stop();
                porcupineManager.delete();
            } catch (PorcupineException e) {
                displayError("Failed to stop Porcupine.");
            }
        }
    }

    private void onPorcupineInitError(final String errorMessage) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                TextView errorText = findViewById(R.id.errorMessage);
                errorText.setText(errorMessage);
                errorText.setVisibility(View.VISIBLE);

                ToggleButton recordButton = findViewById(R.id.record_button);
                recordButton.setBackground(ContextCompat.getDrawable(
                        getApplicationContext(),
                        R.drawable.button_disabled));
                recordButton.setChecked(false);
                recordButton.setEnabled(false);
            }
        });
    }

    private void displayError(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    private void configureKeywordSpinner() {
        Spinner spinner = findViewById(R.id.keyword_spinner);
        ArrayList<String> spinnerItems = new ArrayList<>();
        if (Objects.equals(BuildConfig.FLAVOR, "en")) {
            for (Porcupine.BuiltInKeyword k : Porcupine.BuiltInKeyword.values()) {
                spinnerItems.add(k.name().toLowerCase().replace("_", " "));
            }
        } else {
            try {
                for (String keyword : this.getAssets().list("keywords")) {
                    spinnerItems.add(keyword.replace("_", " ").replace(".ppn", ""));
                }
            } catch (IOException ex) {
                displayError("Unable to get keyword files for given language");
            }
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<>(
                this,
                R.layout.keyword_spinner_item,
                spinnerItems);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);

        final ToggleButton recordButton = findViewById(R.id.record_button);

        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
                if (recordButton.isChecked()) {
                    stopPorcupine();
                    recordButton.toggle();
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parentView) {
                // Do nothing.
            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        notificationPlayer = MediaPlayer.create(this, R.raw.notification);

        configureKeywordSpinner();
    }

    @Override
    protected void onStop() {
        ToggleButton recordButton = findViewById(R.id.record_button);
        recordButton.setChecked(false);

        super.onStop();
    }

    private boolean hasRecordPermission() {
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
                PackageManager.PERMISSION_GRANTED;
    }

    private void requestRecordPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 0);
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode,
            @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
            onPorcupineInitError("Microphone permission is required for this demo");
        } else {
            startPorcupine();
        }
    }

    public void process(View view) {
        ToggleButton recordButton = findViewById(R.id.record_button);
        if (recordButton.isChecked()) {
            if (hasRecordPermission()) {
                startPorcupine();
            } else {
                requestRecordPermission();
            }
        } else {
            stopPorcupine();
        }
    }
}
