package com.cognaxon.wsqlibrary;
import com.cognaxon.WSQlib;
import com.cognaxon.ImageRetainingFragment;



import android.Manifest;
import android.app.Activity;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.Toast;
import android.util.Log;


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.fragment.app.FragmentManager;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Locale;








public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    WSQlib WSQlibrary;

// "WSQ image library" supported media formats:
    String wsq_image_format = "wsq";
    String jp2_image_format = "jp2";
    String jpc_image_format = "jpc";
    String pbm_image_format = "pbm";
    String pgm_image_format = "pgm";
    String ppm_image_format = "ppm";
    String type8_signature_image_format = "bin";

// https://developer.android.com/guide/topics/media/media-formats
// Android supported media formats:
    String bmp_image_format = "bmp";
    String gif_image_format = "gif";
    String jpg_image_format = "jpg";
    String png_image_format = "png";
    String webp_image_format = "webp";
    String heic_image_format = "heic";
    String heif_image_format = "heif";


    private static final String TAG = MainActivity.class.getSimpleName();

    private Spinner saveImageFormat;
    private Integer save_image_format_int;

    private ScaleGestureDetector scaleGestureDetector;
    private float mScaleFactor = 1.0f;


    ImageView img;
    Bitmap OutputBitmap;
    EditText imageWidthEditText;
    EditText imageHeightEditText;
    TextView filenameText;

    String outputtext;
    String width_string, height_string;
    int width, height, ppi, number_of_pixels, number_of_pixels_x_2;
    int row, col;
    Integer pixel_integer_value;
    Integer type8_compression_type, type8_width, type8_width_padded, type8_width_remainder, type8_height, number_of_pixels_padded;
    String comment_text;
    int[] imageProperties = new int[3];
    double bitrate;
    byte[] input_encoded_data_stream, input_decoded_data_stream;
    byte[] wsq_decoded_data_stream, jp2_decoded_data_stream, jpc_decoded_data_stream, pbm_decoded_data_stream, pgm_decoded_data_stream, ppm_decoded_data_stream, type8_decoded_uncompressed_data_stream, type8_decoded_compressed_data_stream;
    byte[] wsq_encoded_data_stream, jp2_encoded_data_stream, jpc_encoded_data_stream, pbm_encoded_data_stream, pgm_encoded_data_stream, ppm_encoded_data_stream, type8_encoded_uncompressed_data_stream, type8_encoded_compressed_data_stream;


    Uri intentURI;
    String stringUri;
    String input_filename;
    String output_filename;

    ActivityResultLauncher<Intent> loadImageActivityResultLauncher;
    ActivityResultLauncher<Intent> saveImageActivityResultLauncher;



    public void onClick(View v) {

        int view_id = v.getId();

        if(view_id == R.id.OpenButton)
        {
            if (!isStoragePermissionGranted())
            { return;}

            onSelectOpenButton();
        }
        else if(view_id == R.id.SaveButton)
        {
            if (!isStoragePermissionGranted())
            { return;}

            onSelectSaveButton();
        }
        else
        {
            outputtext = "Please select button";
            printInToast(outputtext);
        }
    }




    private void onSelectOpenButton() {

        String mimeType = "*/*";
        String [] mimeTypes = {"application/octet-stream", "image/bmp", "image/gif", "image/heic", "image/heif", "image/jpeg", "image/png", "image/webp"};

        Intent intent_load_image = new Intent(Intent.ACTION_OPEN_DOCUMENT); //Field requires API level 19: `android.content.Intent#ACTION_OPEN_DOCUMENT`
        intent_load_image.addCategory(Intent.CATEGORY_OPENABLE);
        intent_load_image.setType(mimeType);
        //intent_load_image.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);

//        intent_load_image.putExtra("android.content.extra.SHOW_ADVANCED", true); //  not part of the official API, there is a possibility this will stop working in future Android versions.

        if (Build.VERSION.SDK_INT >= 18) { // SDK_INT is always >= 18
            intent_load_image.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
        }

        // Specify initial directory for file picker when it loads.
        if (Build.VERSION.SDK_INT >= 26) {
//            String uriAsString = "";
//            Uri pickerInitialUri = Uri.parse(uriAsString);
//            intent_load_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
//            intent_load_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOCUMENTS);
            intent_load_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);

        }

        loadImageActivityResultLauncher.launch(intent_load_image);
    }


    private void onSelectSaveButton() {

        if (input_filename.contains(".")) {
            output_filename = input_filename.substring(0, input_filename.lastIndexOf('.'));
        }
        else {
            output_filename = input_filename;
        }

        if (save_image_format_int == 0) // WSQ - FBI's Wavelet Scalar Quantization
        {output_filename += ".wsq";}
        else if (save_image_format_int == 1) // JP2 - JPEG-2000 Part-1
        {output_filename += ".jp2";}
        else if (save_image_format_int == 2) // JPC - JPEG-2000 Code Stream
        {output_filename += ".jpc";}
        else if (save_image_format_int == 3) // JPG - Joint Photographic Experts Group
        {output_filename += ".jpg";}
        else if (save_image_format_int == 4) // PNG - Portable Network Graphics
        {output_filename += ".png";}
        else if (save_image_format_int == 5) // WEBP - Web Picture
        {output_filename += ".webp";}
        else if (save_image_format_int == 6) // PPM - Portable Pixmap Format
        {output_filename += ".ppm";}
        else if (save_image_format_int == 7) // PGM - Portable Graymap Format
        {output_filename += ".pgm";}
        else if (save_image_format_int == 8) // PBM - Portable Bitmap Format
        {output_filename += ".pbm";}
        else if (save_image_format_int == 9) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (Uncompressed scanned binary image data)
        {output_filename += ".bin";}
        else if (save_image_format_int == 10) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (ANSI/EIA-538-1988 facsimile compression)
        {output_filename += ".bin";}



        String fileMimeType = "application/octet-stream";

        Intent intent_save_image = new Intent(Intent.ACTION_CREATE_DOCUMENT); //Field requires API level 19 (current min is 18): `android.content.Intent#ACTION_CREATE_DOCUMENT`
        intent_save_image.addCategory(Intent.CATEGORY_OPENABLE);
        intent_save_image.setType(fileMimeType);

        //For use with ACTION_CREATE_DOCUMENT to specify an initial file name.
        intent_save_image.putExtra(Intent.EXTRA_TITLE, output_filename);

//        intent_save_image.putExtra("android.content.extra.SHOW_ADVANCED", true); //  not part of the official API, there is a possibility this will stop working in future Android versions.

        // Specify initial directory for file picker when it loads.
        if (Build.VERSION.SDK_INT >= 26) {
//            String uriAsString = "";
//            Uri pickerInitialUri = Uri.parse(uriAsString);
//            intent_save_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
//            intent_save_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOCUMENTS);
            intent_save_image.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);

        }

        saveImageActivityResultLauncher.launch(intent_save_image);

    }




    //  https://stackoverflow.com/questions/3234090/how-to-determine-the-file-extension-of-a-file-from-a-uri
    private boolean CheckFileExtension(String filename_uri, String file_extension) {

        if (filename_uri.contains(".") && file_extension.equalsIgnoreCase(filename_uri.substring(filename_uri.lastIndexOf(".") + 1)))
        {return true;}
        else
        {return false;}

    }




    @Override
    public void onCreate(Bundle savedInstanceState) {


//============================================================================================

        loadImageActivityResultLauncher =
                registerForActivityResult(
                        new ActivityResultContracts.StartActivityForResult(),
                        result -> {
                            if (result.getResultCode() == Activity.RESULT_OK) {

                                Intent intent_load_image_data = result.getData();


                                findViewById(R.id.OpenButton).setEnabled(false);
                                findViewById(R.id.SaveButton).setEnabled(false);


                                intentURI = intent_load_image_data.getData();

                                 // https://developer.android.com/training/secure-file-sharing/retrieve-info
                                 // Get the file's content URI from the incoming Intent, then query the server app to get the file's display name and size.
                                Cursor returnCursor = getContentResolver().query(intentURI, null, null, null, null);

                                 // Get the column indexes of the data in the Cursor, move to the first row in the Cursor, get the data, and display it.
                                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); // file name
                                //int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); //file size
                                returnCursor.moveToFirst();

                                input_filename = returnCursor.getString(nameIndex);
                                filenameText.setText(input_filename);
                                //filenameText.setText(Long.toString(returnCursor.getLong(sizeIndex))); //file size
                                returnCursor.close();


                                if ((CheckFileExtension(input_filename, bmp_image_format)) | (CheckFileExtension(input_filename, gif_image_format)) | (CheckFileExtension(input_filename, jpg_image_format)) | (CheckFileExtension(input_filename, png_image_format)) | (CheckFileExtension(input_filename, webp_image_format)) | (CheckFileExtension(input_filename, heic_image_format)) | (CheckFileExtension(input_filename, heif_image_format))) //if Android supported media formats
                                {
                                    OutputBitmap = GetBitmapFromUri(intentURI);
                                    img.setImageBitmap(OutputBitmap);
                                } //if Android supported media formats


                                else if ((CheckFileExtension(input_filename, wsq_image_format)) | (CheckFileExtension(input_filename, jp2_image_format)) | (CheckFileExtension(input_filename, jpc_image_format)) | (CheckFileExtension(input_filename, pbm_image_format)) | (CheckFileExtension(input_filename, pgm_image_format)) | (CheckFileExtension(input_filename, ppm_image_format)) | (CheckFileExtension(input_filename, type8_signature_image_format))) {
                                    try {
                                        input_encoded_data_stream = LoadBytesFromUri(intentURI);

                                        if (CheckFileExtension(input_filename, wsq_image_format)) {


                                            wsq_decoded_data_stream = WSQlib.WSQ_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < wsq_decoded_data_stream.length; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) wsq_decoded_data_stream[i] & 0xFF, (int) wsq_decoded_data_stream[i] & 0xFF, (int) wsq_decoded_data_stream[i] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }


                                        } //if (CheckFileExtension(input_filename, wsq_image_format))
                                        else if (CheckFileExtension(input_filename, jp2_image_format)) {

                                            jp2_decoded_data_stream = WSQlib.JP2_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];
                                            number_of_pixels = width * height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < number_of_pixels; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) jp2_decoded_data_stream[i] & 0xFF, (int) jp2_decoded_data_stream[i + number_of_pixels] & 0xFF, (int) jp2_decoded_data_stream[i + number_of_pixels_x_2] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }

                                        } //if (CheckFileExtension(input_filename, jp2_image_format))
                                        else if (CheckFileExtension(input_filename, jpc_image_format)) {

                                            jpc_decoded_data_stream = WSQlib.JPC_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];
                                            number_of_pixels = width * height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < number_of_pixels; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) jpc_decoded_data_stream[i] & 0xFF, (int) jpc_decoded_data_stream[i + number_of_pixels] & 0xFF, (int) jpc_decoded_data_stream[i + number_of_pixels_x_2] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }

                                        } //if (CheckFileExtension(input_filename, jpc_image_format))
                                        else if (CheckFileExtension(input_filename, pbm_image_format)) {

                                            pbm_decoded_data_stream = WSQlib.PBM_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];
                                            number_of_pixels = width * height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < number_of_pixels; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) pbm_decoded_data_stream[i] & 0xFF, (int) pbm_decoded_data_stream[i] & 0xFF, (int) pbm_decoded_data_stream[i] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }

                                        } //if (CheckFileExtension(input_filename, pbm_image_format))
                                        else if (CheckFileExtension(input_filename, pgm_image_format)) {

                                            pgm_decoded_data_stream = WSQlib.PGM_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];
                                            number_of_pixels = width * height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < number_of_pixels; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) pgm_decoded_data_stream[i] & 0xFF, (int) pgm_decoded_data_stream[i] & 0xFF, (int) pgm_decoded_data_stream[i] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }


                                        } //if (CheckFileExtension(input_filename, pgm_image_format))
                                        else if (CheckFileExtension(input_filename, ppm_image_format)) {

                                            ppm_decoded_data_stream = WSQlib.PPM_decode_stream(input_encoded_data_stream, imageProperties);
                                            width = imageProperties[0];
                                            height = imageProperties[1];
                                            ppi = imageProperties[2];
                                            number_of_pixels = width * height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            // 'for' loop replaceable with enhanced 'for'
                                            for (int i = 0; i < number_of_pixels; i++) {
                                                OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) ppm_decoded_data_stream[i] & 0xFF, (int) ppm_decoded_data_stream[i + number_of_pixels] & 0xFF, (int) ppm_decoded_data_stream[i + number_of_pixels_x_2] & 0xFF));

                                                if (col == width) {
                                                    col = 0;
                                                    row++;
                                                }
                                            }


                                        } //if (CheckFileExtension(input_filename, ppm_image_format))
                                        else if (CheckFileExtension(input_filename, type8_signature_image_format)) {

                                            width_string = imageWidthEditText.getText().toString();
                                            height_string = imageHeightEditText.getText().toString();

                                            type8_width = Integer.parseInt(width_string);
                                            type8_height = Integer.parseInt(height_string);



                                            type8_width_remainder = type8_width % 8;

                                            if (type8_width_remainder == 0) {
                                                type8_width_padded = type8_width;
                                            }
                                            else {
                                                type8_width_padded = type8_width + (8 - type8_width_remainder);
                                            }

                                            number_of_pixels_padded = type8_width_padded*type8_height;

                                            if (input_encoded_data_stream.length >= (number_of_pixels_padded / 8)) {
                                                type8_compression_type = 0;
                                            }
                                            else {
                                                type8_compression_type = 1;
                                            }


                                            if(type8_compression_type == 0)
                                            {type8_decoded_uncompressed_data_stream = WSQlib.CreateImageFromNISTType8UncompressedBinaryField(input_encoded_data_stream, type8_width, type8_height);}
                                            else if (type8_compression_type == 1)
                                            {type8_decoded_compressed_data_stream = WSQlib.CreateImageFromNISTType8CompressedBinaryField(input_encoded_data_stream, type8_width, type8_height);}

                                            number_of_pixels = type8_width * type8_height;
                                            number_of_pixels_x_2 = number_of_pixels * 2;


                                            OutputBitmap = Bitmap.createBitmap(type8_width, type8_height, Bitmap.Config.RGB_565);
                                            row = 0; col = 0;

                                            if(type8_compression_type == 0) {
                                                // 'for' loop replaceable with enhanced 'for'
                                                for (int i = 0; i < type8_decoded_uncompressed_data_stream.length; i++) {
                                                    OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) type8_decoded_uncompressed_data_stream[i] & 0xFF, (int) type8_decoded_uncompressed_data_stream[i] & 0xFF, (int) type8_decoded_uncompressed_data_stream[i] & 0xFF));

                                                    if (col == type8_width) {
                                                        col = 0;
                                                        row++;
                                                    }
                                                }
                                            } // if(type8_compression_type == 0)
                                            else if (type8_compression_type == 1) {
                                                // 'for' loop replaceable with enhanced 'for'
                                                for (int i = 0; i < type8_decoded_compressed_data_stream.length; i++) {
                                                    OutputBitmap.setPixel(col++, row, android.graphics.Color.rgb((int) type8_decoded_compressed_data_stream[i] & 0xFF, (int) type8_decoded_compressed_data_stream[i] & 0xFF, (int) type8_decoded_compressed_data_stream[i] & 0xFF));

                                                    if (col == type8_width) {
                                                        col = 0;
                                                        row++;
                                                    }
                                                }
                                            } // if(type8_compression_type == 1)


                                        } //if (CheckFileExtension(stringUri, type8_signature_image_format));


                                        img.setImageBitmap(OutputBitmap);


                                    } catch (Exception e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }

                                }// "WSQ image library" supported media formats


                                outputtext = "Image opened.";
                                printInToast(outputtext);



                                try{

                                   // imageWidthEditText.setText(Integer.toString(OutputBitmap.getWidth()));
                                    imageWidthEditText.setText(String.format(Locale.getDefault(), "%d", OutputBitmap.getWidth()));
                                    //imageHeightEditText.setText(Integer.toString(OutputBitmap.getHeight()));
                                    imageHeightEditText.setText(String.format(Locale.getDefault(), "%d", OutputBitmap.getHeight()));

                                }catch (Exception w){
                                    // the bitmap is not valid, eighter null or empty
                                }


                                findViewById(R.id.OpenButton).setEnabled(true);
                                findViewById(R.id.SaveButton).setEnabled(true);


                            }
                        });


//============================================================================================

        saveImageActivityResultLauncher =
                registerForActivityResult(
                        new ActivityResultContracts.StartActivityForResult(),
                        result -> {
                            if (result.getResultCode() == Activity.RESULT_OK) {

                                Intent intent_save_image_data = result.getData();


                                findViewById(R.id.OpenButton).setEnabled(false);
                                findViewById(R.id.SaveButton).setEnabled(false);


                                intentURI = intent_save_image_data.getData();
                                stringUri = intentURI.toString();


                                width = OutputBitmap.getWidth();
                                height = OutputBitmap.getHeight();
                                number_of_pixels = width * height;
                                number_of_pixels_x_2 = number_of_pixels*2;

                                int[] pixels = new int[number_of_pixels];
                                OutputBitmap.getPixels(pixels, 0, width, 0, 0, width, height);


                                if (save_image_format_int == 0) // WSQ - FBI's Wavelet Scalar Quantization
                                {
                                    bitrate = 0.75;
                                    comment_text = "Cognaxon";

                                    input_decoded_data_stream = new byte[number_of_pixels];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = (((pixels[n] >> 16) & 0xFF) + ((pixels[n] >> 8) & 0xFF) + (pixels[n] & 0xFF)) / 3;
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                    }
                                    wsq_encoded_data_stream = WSQlib.WSQ_encode_stream(input_decoded_data_stream, width, height, bitrate, ppi, comment_text);

                                    SaveBytesToUri(wsq_encoded_data_stream, intentURI);

                                } //if (save_image_format_int == 0) // WSQ - FBI's Wavelet Scalar Quantization
                                else if (save_image_format_int == 1) // JP2 - JPEG-2000 Part-1
                                {
                                    input_decoded_data_stream = new byte[number_of_pixels * 3];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = ((pixels[n] >> 16) & 0xFF);
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                        pixel_integer_value = ((pixels[n] >> 8) & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels] = pixel_integer_value.byteValue();
                                        pixel_integer_value = (pixels[n] & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels_x_2] = pixel_integer_value.byteValue();
                                    }

                                    jp2_encoded_data_stream = WSQlib.JP2_encode_stream(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(jp2_encoded_data_stream, intentURI);

                                } //if (save_image_format_int == 1) // JP2 - JPEG-2000 Part-1
                                else if (save_image_format_int == 2) // JPC - JPEG-2000 Code Stream
                                {
                                    input_decoded_data_stream = new byte[number_of_pixels * 3];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = ((pixels[n] >> 16) & 0xFF);
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                        pixel_integer_value = ((pixels[n] >> 8) & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels] = pixel_integer_value.byteValue();
                                        pixel_integer_value = (pixels[n] & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels_x_2] = pixel_integer_value.byteValue();
                                    }

                                    jpc_encoded_data_stream = WSQlib.JPC_encode_stream(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(jpc_encoded_data_stream, intentURI);

                                } //if (save_image_format_int == 2) // JPC - JPEG-2000 Code Stream
                                else if (save_image_format_int == 3) // JPG - Joint Photographic Experts Group
                                {
                                    SaveImageTypeToUri(OutputBitmap, intentURI, 1);

                                } //if (save_image_format_int == 3) // JPG - Joint Photographic Experts Group
                                else if (save_image_format_int == 4) // PNG - Portable Network Graphics
                                {

                                    SaveImageTypeToUri(OutputBitmap, intentURI, 2);

                                } //if (save_image_format_int == 4) // PNG - Portable Network Graphics
                                else if (save_image_format_int == 5) // WEBP - Web Picture
                                {
                                    SaveImageTypeToUri(OutputBitmap, intentURI, 3);

                                } //if (save_image_format_int == 5) // WEBP - Web Picture
                                else if (save_image_format_int == 6) // PPM - Portable Pixmap Format
                                {

                                    input_decoded_data_stream = new byte[number_of_pixels * 3];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = ((pixels[n] >> 16) & 0xFF);
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                        pixel_integer_value = ((pixels[n] >> 8) & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels] = pixel_integer_value.byteValue();
                                        pixel_integer_value = (pixels[n] & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels_x_2] = pixel_integer_value.byteValue();
                                    }

                                    ppm_encoded_data_stream = WSQlib.PPM_encode_stream(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(ppm_encoded_data_stream, intentURI);

                                } //if (save_image_format_int == 6) // PPM - Portable Pixmap Format

                                else if (save_image_format_int == 7) // PGM - Portable Graymap Format
                                {

                                    input_decoded_data_stream = new byte[number_of_pixels * 3];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = ((pixels[n] >> 16) & 0xFF);
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                        pixel_integer_value = ((pixels[n] >> 8) & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels] = pixel_integer_value.byteValue();
                                        pixel_integer_value = (pixels[n] & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels_x_2] = pixel_integer_value.byteValue();
                                    }

                                    pgm_encoded_data_stream = WSQlib.PGM_encode_stream(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(pgm_encoded_data_stream, intentURI);

                                } //if (save_image_format_int == 7) // PPGM - Portable Graymap Format

                                else if (save_image_format_int == 8) // PBM - Portable Bitmap Format
                                {

                                    input_decoded_data_stream = new byte[number_of_pixels * 3];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = ((pixels[n] >> 16) & 0xFF);
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                        pixel_integer_value = ((pixels[n] >> 8) & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels] = pixel_integer_value.byteValue();
                                        pixel_integer_value = (pixels[n] & 0xFF);
                                        input_decoded_data_stream[n + number_of_pixels_x_2] = pixel_integer_value.byteValue();
                                    }

                                    pbm_encoded_data_stream = WSQlib.PBM_encode_stream(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(pbm_encoded_data_stream, intentURI);

                                } //if (save_image_format_int = 8) // PBM - Portable Bitmap Format

                                else if (save_image_format_int == 9) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (Uncompressed scanned binary image data)
                                {
                                    type8_width = OutputBitmap.getWidth();
                                    type8_height = OutputBitmap.getHeight();

                                    input_decoded_data_stream = new byte[number_of_pixels];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = (((pixels[n] >> 16) & 0xFF) + ((pixels[n] >> 8) & 0xFF) + (pixels[n] & 0xFF)) / 3;
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                    }
                                    type8_encoded_uncompressed_data_stream = WSQlib.CreateNISTType8UncompressedBinaryFieldfromImage(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(type8_encoded_uncompressed_data_stream, intentURI);

                                } //if (save_image_format_int == 9) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (Uncompressed scanned binary image data)

                                else if (save_image_format_int == 10) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (ANSI/EIA-538-1988 facsimile compression)
                                {
                                    type8_width = OutputBitmap.getWidth();
                                    type8_height = OutputBitmap.getHeight();

                                    input_decoded_data_stream = new byte[number_of_pixels];

                                    for (int n = 0; n < number_of_pixels; n++) {
                                        pixel_integer_value = (((pixels[n] >> 16) & 0xFF) + ((pixels[n] >> 8) & 0xFF) + (pixels[n] & 0xFF)) / 3;
                                        input_decoded_data_stream[n] = pixel_integer_value.byteValue();
                                    }
                                    type8_encoded_compressed_data_stream = WSQlib.CreateNISTType8CompressedBinaryFieldfromImage(input_decoded_data_stream, width, height);

                                    SaveBytesToUri(type8_encoded_compressed_data_stream, intentURI);

                                } //if (save_image_format_int == 10) // BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (ANSI/EIA-538-1988 facsimile compression)



                                outputtext = "Image saved.";
                                printInToast(outputtext);


                                findViewById(R.id.OpenButton).setEnabled(true);
                                findViewById(R.id.SaveButton).setEnabled(true);



                            }
                        });

//============================================================================================

        scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());


        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        isStoragePermissionGranted();


        WSQlibrary = new WSQlib();


        findViewById(R.id.OpenButton).setOnClickListener(this);
        findViewById(R.id.SaveButton).setOnClickListener(this);

        findViewById(R.id.SaveButton).setEnabled(false);

        imageWidthEditText = findViewById(R.id.imageWidthEditTextNumber);
        imageHeightEditText = findViewById(R.id.imageHeightEditTextNumber);

        imageWidthEditText = findViewById(R.id.imageWidthEditTextNumber);
        imageHeightEditText = findViewById(R.id.imageHeightEditTextNumber);

        filenameText = findViewById(R.id.filenameTextView);



        img = findViewById(R.id.imageView);
        img.setAdjustViewBounds(false);

        //img.setScaleType(ScaleType.MATRIX);//original size
        img.setScaleType(ScaleType.FIT_CENTER);


        saveImageFormat = findViewById(R.id.ChooseSaveImageFormatSpinner);
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("WSQ - FBI's Wavelet Scalar Quantization"); //0
        arrayList.add("JP2 - JPEG-2000 Part-1"); // 1
        arrayList.add("JPC - JPEG-2000 Code Stream"); // 2
        arrayList.add("JPG - Joint Photographic Experts Group"); // 3
        arrayList.add("PNG - Portable Network Graphics"); //4
        arrayList.add("WEBP - Web Picture"); // 5
        arrayList.add("PPM - Portable Pixmap Format"); //6
        arrayList.add("PGM - Portable Graymap Format"); //7
        arrayList.add("PBM - Portable Bitmap Format"); //8
        arrayList.add("BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (Uncompressed scanned binary image data)"); //9
        arrayList.add("BIN - ANSI/NIST-ITL 1-2000 Type-8 signature (ANSI/EIA-538-1988 facsimile compression)"); //10


//        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,                         android.R.layout.simple_spinner_item, arrayList);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,                         android.R.layout.simple_spinner_item, arrayList);
        arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        saveImageFormat.setAdapter(arrayAdapter);
        saveImageFormat.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                save_image_format_int = parent.getSelectedItemPosition();
                String saveImageFormatName = parent.getItemAtPosition(position).toString();
                Toast.makeText(parent.getContext(), "Selected: " + saveImageFormatName,          Toast.LENGTH_LONG).show();
            }
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });



//-------------------------
        // http://theworkingdad.it/en/2018/10/09/how-to-retain-image-in-android-when-screen-rotation-occurs/
        // How to retain image in Android when screen rotation occurs
        initializeImageRetainingFragment();
        tryReloadImage(); //reload Bitmap after Device rotation. Every time the screen rotates Android destroys (onDestroy()) and recreates (onCreate()) the Activity
//-------------------------


        if (savedInstanceState == null)
        {
            comment_text = null; // it is very important to set comment_text to null in order to avoid crash of "WSQ image library"
        }


        if (savedInstanceState != null) {

            findViewById(R.id.SaveButton).setEnabled(savedInstanceState.getBoolean("SaveButtonEnabled", false));

//-------------------------
// https://developer.android.com/guide/topics/resources/runtime-changes
// Retaining an object during a configuration change
// If restarting your activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive operations,
// then a full restart due to a configuration change might be a slow user experience. Also, it might not be possible for you to completely restore your
// activity state with the Bundle that the system saves for you with the onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps)
// and the data within it must be serialized then deserialized on the main thread, which can consume a lot of memory and make the configuration change slow.
// In such a situation, you can alleviate the burden of reinitializing part of your activity by using a ViewModel object.
// ViewModels are preserved across configuration changes so they are the perfect place to keep your UI data without having to query them again.
// For more information on using ViewModels in your apps, read the ViewModel guide.

            // code below sometimes crashes because onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps)
            // Bitmap bitmap = savedInstanceState.getParcelable("image");
            // ((ImageView) findViewById(R.id.imageView)).setImageBitmap(bitmap);
//-------------------------
        }


        getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
        );




    } // public void onCreate(Bundle savedInstanceState)






    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putBoolean("SaveButtonEnabled", findViewById(R.id.SaveButton).isEnabled());
//-------------------------
// https://developer.android.com/guide/topics/resources/runtime-changes
// Retaining an object during a configuration change
// If restarting your activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive operations,
// then a full restart due to a configuration change might be a slow user experience. Also, it might not be possible for you to completely restore your
// activity state with the Bundle that the system saves for you with the onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps)
// and the data within it must be serialized then deserialized on the main thread, which can consume a lot of memory and make the configuration change slow.
// In such a situation, you can alleviate the burden of reinitializing part of your activity by using a ViewModel object.
// ViewModels are preserved across configuration changes so they are the perfect place to keep your UI data without having to query them again.

        // code below sometimes crashes because onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps)
        // BitmapDrawable drawable = (BitmapDrawable) ((ImageView) findViewById(R.id.imageView)).getDrawable();
        // if (drawable != null) {
        //   Bitmap bitmap = drawable.getBitmap();
        //   outState.putParcelable("image", bitmap);}
//-------------------------
        super.onSaveInstanceState(outState);
    }




    public  boolean isStoragePermissionGranted() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                Log.v(TAG,"Permission is granted");
                return true;
            } else {

                Log.v(TAG,"Permission is revoked");
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                return false;
            }
        }
        else { //permission is automatically granted on sdk<23 upon installation
            Log.v(TAG,"Permission is granted");
            return true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
            Log.v(TAG,"Permission: "+ permissions[0] + "was " + grantResults[0]);
            //resume tasks needing this permission
        }
    }





    private void printInToast(String outputprinttext) {
        int duration = Toast.LENGTH_LONG; //or Toast.LENGTH_LONG
        Toast.makeText(MainActivity.this, outputprinttext, duration).show();
    }




    public byte[] LoadBytesFromUri(Uri _toTreeUri) {

        byte[] bytes = new byte[1];

        try {

            ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(_toTreeUri, "r");
            assert pfd != null;
            assert pfd.getStatSize() <= Integer.MAX_VALUE;
            FileDescriptor fd = pfd.getFileDescriptor();
            FileInputStream input_file_stream = new FileInputStream(fd);
            bytes = new byte[input_file_stream.available()];
            input_file_stream.read(bytes);
            input_file_stream.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bytes;
    }


    public void SaveBytesToUri(byte[] _bytes, Uri _toTreeUri) {
        OutputStream out = null;
        try {
            out = getContentResolver().openOutputStream(_toTreeUri, "w");
            out.write(_bytes);
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public Bitmap GetBitmapFromUri(Uri _treeUri){

        Bitmap image = null;
        ParcelFileDescriptor parcelFileDescriptor = null;
        try {

            parcelFileDescriptor = getContentResolver().openFileDescriptor(_treeUri, "r");

            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();

            image = BitmapFactory.decodeFileDescriptor(fileDescriptor);

            parcelFileDescriptor.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;

    }


    public void SaveImageTypeToUri(Bitmap _bitmap, Uri _toTreeUri, int _type) {
        OutputStream out;
        try {
            out = getContentResolver().openOutputStream(_toTreeUri, "w");


            if(_type == 1)
            {_bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);}
            else if(_type == 2)
            {	_bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);}
            else if(_type == 3)
            {
                if (android.os.Build.VERSION.SDK_INT >= 30)
                {
                    _bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 90, out);
                    //_bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSY, 90, out);
                }
                else
                {   _bitmap.compress(Bitmap.CompressFormat.WEBP, 90, out);} // Added in API level 14, Deprecated in API level 30
            }
            out.close();
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }





//-------------------------
// http://theworkingdad.it/en/2018/10/09/how-to-retain-image-in-android-when-screen-rotation-occurs/
// How to retain image in Android when screen rotation occurs

    private static final String FRAGMENT_NAME = "imageFragment";
    private static ImageRetainingFragment imageRetainingFragment = null;

    private void initializeImageRetainingFragment() {
        // find the retained fragment on activity restarts
        FragmentManager fragmentManager = getSupportFragmentManager();
        this.imageRetainingFragment = (ImageRetainingFragment) fragmentManager.findFragmentByTag(FRAGMENT_NAME);
        // create the fragment and bitmap the first time
        if (this.imageRetainingFragment == null) {
            this.imageRetainingFragment = new ImageRetainingFragment();
            fragmentManager.beginTransaction()
                    // Add a fragment to the activity state.
                    .add(this.imageRetainingFragment, FRAGMENT_NAME)
                    .commit();
        }
    }


    private void tryReloadImage() {
        if (this.imageRetainingFragment == null) {
            return;
        }
        Bitmap selectedImage = this.imageRetainingFragment.getImage(); // Static member 'com.cognaxon.WSQlib.MainActivity.imageRetainingFragment' accessed via instance reference
        if (selectedImage == null) {
            return;
        }
        ImageView selectedImageView = findViewById(R.id.imageView);
        selectedImageView.setImageBitmap(selectedImage);
    }
//-------------------------


    @Override
    public boolean onTouchEvent(MotionEvent motionEvent) {
        scaleGestureDetector.onTouchEvent(motionEvent);
        return true;
    }


    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
            mScaleFactor *= scaleGestureDetector.getScaleFactor();
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
            img.setScaleX(mScaleFactor);
            img.setScaleY(mScaleFactor);
            return true;
        }
    }





}