package com.hesiii.i_hear.SoundClassification;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Toast;

import com.github.squti.androidwaverecorder.WaveRecorder;
import com.hesiii.i_hear.API.ApiServices;
import com.hesiii.i_hear.API.RetroServer;
import com.hesiii.i_hear.ClassificationAPI.ClassificationApiServices;
import com.hesiii.i_hear.ClassificationAPI.ClassificationRetroServer;
import com.hesiii.i_hear.Models.Data;
import com.hesiii.i_hear.R;
import com.hesiii.i_hear.ResponseModels.ClassificationResponse;
import com.hesiii.i_hear.databinding.ActivityGameBinding;
import com.hesiii.i_hear.databinding.ActivitySoundClassificationBinding;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.Random;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class SoundClassificationActivity extends AppCompatActivity {

    private ActivitySoundClassificationBinding binding;
    private String filePath;
    private static final int FILE_PICKER_REQUEST = 1;
    private static final int PERMISSION_CODE = 0;

    private WaveRecorder waveRecorder;

    private boolean isRecording;

    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivitySoundClassificationBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        if (getSupportActionBar() != null) {

            getSupportActionBar().hide();

        }

        filePath = getExternalCacheDir().getAbsolutePath() + "/Audio_" + System.currentTimeMillis()+".wav";

        binding.recordButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

               checkPermissions();

            }
        });

        binding.recognizeSoundsButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                recognizeSoundsButtonProcess();

            }
        });

        binding.fileButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

//                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//                intent.setType("audio/*"); // Set the MIME type to audio files
//                startActivityForResult(intent, FILE_PICKER_REQUEST);

                if (ActivityCompat.checkSelfPermission(SoundClassificationActivity.this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED
                        && ActivityCompat.checkSelfPermission(SoundClassificationActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(SoundClassificationActivity.this, Manifest.permission.RECORD_AUDIO)
                        != PackageManager.PERMISSION_GRANTED) {

                    ActivityCompat.requestPermissions(SoundClassificationActivity.this,
                            new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
                            100);

                } else {

                    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.setType("audio/*"); // This specifies that you want to allow all file types
                    someActivityResultLauncher.launch(intent);

                }

            }
        });

        waveRecorder = new WaveRecorder(filePath);
        // Configuration
        // You can change the recording parameters using the waveConfig property
        waveRecorder.getWaveConfig().setSampleRate(44100);
        waveRecorder.getWaveConfig().setChannels(AudioFormat.CHANNEL_IN_STEREO);
        waveRecorder.getWaveConfig().setAudioEncoding(AudioFormat.ENCODING_PCM_16BIT);

        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("Please Wait...");
        progressDialog.setCancelable(false);

    }

    private void checkPermissions() {

        if ((ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO)
                != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED)) {

            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, PERMISSION_CODE);

        } else {

            recordingProcess();

        }

    }

    private void recordingProcess() {

        try {

            if (isRecording) {

                waveRecorder.stopRecording();
                binding.recordButton.setImageResource(R.drawable.ic_record);
                binding.recognizeSoundsButton.setVisibility(View.VISIBLE);
                binding.timeChronometer.setBase(SystemClock.elapsedRealtime());
                binding.timeChronometer.stop();
                binding.recordingStatusText.setText("");
                isRecording = false;

            } else {

                waveRecorder.startRecording();
                binding.recognizeSoundsButton.setVisibility(View.GONE);
                binding.timeChronometer.setBase(SystemClock.elapsedRealtime());
                binding.timeChronometer.start();
                binding.recordingStatusText.setText(getResources().getString(R.string.recording_start_status));
                binding.recordButton.setImageResource(R.drawable.ic_stop);
                isRecording = true;

            }

        } catch (Exception e) {

            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();

        }

    }



    // You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
    ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        // There are no request codes
                        Uri uri = result.getData().getData();
                        String filePath = getFilePathFromUri(uri);
                        System.out.println(filePath);
                        uploadToServer(new File(filePath));
                    }
                }
            });

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == FILE_PICKER_REQUEST && resultCode == RESULT_OK) {
            if (data != null) {

                Uri uri = data.getData();
                String filePath = getFilePathFromUri(uri);
                System.out.println(filePath);
                uploadToServer(new File(filePath));
            }
        }
    }

    private String getFilePathFromUri(Uri uri) {
        String filePath = null;
        String[] projection = {MediaStore.Audio.Media.DATA};
        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(uri, projection, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
            filePath = cursor.getString(columnIndex);
            cursor.close();
        }
        return filePath;
    }

    private void uploadToServer(File audioFile) {

        progressDialog.show();

        RequestBody request = RequestBody.create(MediaType.parse("audio/wav"), audioFile);
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", audioFile.getName(), request);
        ClassificationApiServices apiService = ClassificationRetroServer.getRetrofitInstance().create(ClassificationApiServices.class);

        // Call the uploadAudio method with the RequestBody
        Call<ClassificationResponse> call = apiService.uploadAudio(filePart);

        call.enqueue(new Callback<ClassificationResponse>() {
            @Override
            public void onResponse(@NonNull Call<ClassificationResponse> call, @NonNull Response<ClassificationResponse> response) {

                if (response.isSuccessful()) {

                    if (response.body() != null) {

                        if (!response.body().getPrediction().isEmpty()) {

                            System.out.println("Prediction : " + response.body().getPrediction());
                            final String predictionText = response.body().getPrediction();
                            binding.predictionText.setText(predictionText);

                        } else {

                            Toast.makeText(SoundClassificationActivity.this, "Something Went Wrong!", Toast.LENGTH_SHORT).show();

                        }

                    } else {

                        Toast.makeText(SoundClassificationActivity.this, "Something Went Wrong!", Toast.LENGTH_SHORT).show();

                    }

                } else if (response.code() == 500){

                    final String predictionText = "We're unable to classify this audio. Please ensure it's clear and free from background noise for accurate results!";
                    binding.predictionText.setText(predictionText);
                    //Toast.makeText(SoundClassificationActivity.this, "We're unable to classify this audio. Please ensure it's clear and free from background noise for accurate results!", Toast.LENGTH_LONG).show();

                } else {

                    //System.out.println("Error : " + response.errorBody().toString());
                    Toast.makeText(SoundClassificationActivity.this, "Something Went Wrong - " + response.code(), Toast.LENGTH_SHORT).show();

                }

                progressDialog.dismiss();

            }

            @Override
            public void onFailure(@NonNull Call<ClassificationResponse> call, @NonNull Throwable t) {

                progressDialog.dismiss();
                Toast.makeText(SoundClassificationActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
                System.out.println(t.getLocalizedMessage());

            }
        });

    }

    private void recognizeSoundsButtonProcess() {

        File audioFile = new File(filePath);

        if (audioFile.exists()) {

            System.out.println("File Name : " + audioFile.getName());
            //File file = new File(convertAmrToWav(fileName));
            //System.out.println("File Name : "+file.getName());
            //System.out.println("File Path : "+file.getAbsolutePath());
            uploadToServer(audioFile);

        } else {

            System.out.println("Error");
            Toast.makeText(this, "The file cannot be found", Toast.LENGTH_SHORT).show();

        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == PERMISSION_CODE) {
            // If request is cancelled, the grantResults arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED) {
                // Permissions granted, proceed with file deletion
                recordingProcess();

            } else {
                // Permissions not granted, show a toast or other UI message
                Toast.makeText(this, "Permissions not granted", Toast.LENGTH_SHORT).show();
            }
        }

    }

    private String getRandomWord() {

        // Define the character pool
        String characters = "abcdefghijklmnopqrstuvwxyz";

        // Create a random object
        Random random = new Random();

        // Initialize an empty string to store the random characters
        StringBuilder randomWord = new StringBuilder();

        // Generate 4 random characters and add them to the StringBuilder
        for (int i = 0; i < 4; i++) {
            int randomIndex = random.nextInt(characters.length());
            char randomChar = characters.charAt(randomIndex);
            randomWord.append(randomChar);
        }

        // Convert the StringBuilder to a string
        String meaninglessWord = randomWord.toString();

        // Print the meaningless word
        System.out.println("Meaningless Word: " + meaninglessWord);
        return meaninglessWord;

    }

}