/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 com.example.android.autobackupsample;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

import com.example.android.autobackupsample.MainActivityFragment;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;

/**
 * The purpose of AddFileActivity activity is to create a data file based on the
 * file name and size parameters specified as an Intent external parameters or with the
 * activity UI.
 * <p/>
 * The optional intent parameters are
 * {@link com.example.android.autobackupsample.AddFileActivity#FILE_NAME} and
 * {@link com.example.android.autobackupsample.AddFileActivity#FILE_SIZE_IN_BYTES}.
 * {@link com.example.android.autobackupsample.AddFileActivity#FILE_STORAGE}.
 * <p/>
 * The activity will return an
 * {@link com.example.android.autobackupsample.MainActivityFragment#ADD_FILE_RESULT_ERROR}
 * if intent parameters are specified incorrectly or it will display Toast messages to the user
 * if those parameters are specified via the activity UI.
 */
public class AddFileActivity extends Activity {

    private static final String TAG = "AutoBackupSample";

    /**
     * The intent parameter that specifies a file name. The file name must be unique for the
     * application internal directory.
     */
    public static final String FILE_NAME = "file_name";

    /**
     * The intent parameter that specifies a file size in bytes. The size must be a number
     * larger or equal to 0.
     */
    public static final String FILE_SIZE_IN_BYTES = "file_size_in_bytes";

    /**
     * The file storage is an optional parameter. It should be one of these:
     * "INTERNAL", "EXTERNAL", "DONOTBACKUP". The default option is "INTERNAL".
     */
    public static final String FILE_STORAGE = "file_storage";

    /**
     * A file size multiplier. It is used to calculate the total number of bytes to be added
     * to the file.
     */
    private int mSizeMultiplier = 1;

    /**
     * Defines File Storage options.
     */
    private static enum FileStorage {
        INTERNAL,
        EXTERNAL,
        DONOTBACKUP;
    }

    /**
     * Contains a selected by a user file storage option.
     */
    private FileStorage mFileStorage = FileStorage.INTERNAL;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.add_file);
        initFileSizeSpinner();
        initFileStorageSpinner();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // If an intent has extra parameters, create the file and finish the activity.
        if (getIntent().hasExtra(FILE_NAME) && getIntent().hasExtra(FILE_SIZE_IN_BYTES)) {
            String fileName = getIntent().getStringExtra(FILE_NAME);
            String sizeInBytesParamValue = getIntent().getStringExtra(FILE_SIZE_IN_BYTES);
            String fileStorageParamValue = FileStorage.INTERNAL.toString();

            if (getIntent().hasExtra(FILE_STORAGE)) {
                fileStorageParamValue = getIntent().getStringExtra(FILE_STORAGE);
            }

            if (TextUtils.isEmpty(fileName) ||
                    isFileExists(fileName) ||
                    !isSizeValid(sizeInBytesParamValue) ||
                    !isFileStorageParamValid(fileStorageParamValue)) {
                setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
                finish();
                return;
            }

            mFileStorage = FileStorage.valueOf(fileStorageParamValue);

            if (mFileStorage == FileStorage.EXTERNAL && !Utils.isExternalStorageAvailable()) {
                setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
                finish();
                return;
            }

            createFileWithRandomDataAndFinishActivity(fileName, mFileStorage,
                    sizeInBytesParamValue);
        }
    }

    /**
     * A handler function for a Create File button click event.
     *
     * @param view a reference to the Create File button view.
     */
    public void onCreateFileButtonClick(View view) {
        EditText fileNameEditText = (EditText) findViewById(R.id.file_name);
        EditText fileSizeEditText = (EditText) findViewById(R.id.file_size);
        String fileName = fileNameEditText.getText().toString();
        String fileSizeEditTextValue = fileSizeEditText.getText().toString();

        if (TextUtils.isEmpty(fileName) || isFileExists(fileName)) {
            Toast toast = Toast.makeText(this, getText(R.string.file_exists), Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
            toast.show();
            return;
        }

        if (!isSizeValid(fileSizeEditTextValue)) {
            Toast toast = Toast.makeText(this, getText(R.string.file_size_is_invalid),
                    Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
            toast.show();
            return;
        }

        long fileSize = Integer.parseInt(fileSizeEditTextValue) * mSizeMultiplier;

        if (mFileStorage == FileStorage.EXTERNAL && !Utils.isExternalStorageAvailable()) {
            Toast toast = Toast.makeText(this,
                    getText(R.string.external_storage_unavailable), Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
            toast.show();
            return;
        }

        createFileWithRandomDataAndFinishActivity(fileName, mFileStorage, String.valueOf(fileSize));
    }

    private void initFileSizeSpinner() {
        Spinner spinner = (Spinner) findViewById(R.id.file_size_spinner);
        final ArrayAdapter<CharSequence> adapter =
                ArrayAdapter.createFromResource(this, R.array.file_size_array,
                        android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                String sizeMeasure = adapter.getItem(position).toString();
                mSizeMultiplier = (int) Math.pow(1024, position);
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, String.format("Selected: %s, %d", sizeMeasure,
                            mSizeMultiplier));
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
    }

    private void initFileStorageSpinner() {
        Spinner spinner = (Spinner) findViewById(R.id.storage_spinner);
        final ArrayAdapter<CharSequence> adapter =
                ArrayAdapter.createFromResource(this, R.array.file_storage_array,
                        android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                mFileStorage = FileStorage.values()[position];
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
    }

    private void createFileWithRandomDataAndFinishActivity(String fileName, FileStorage storage,
                                                           String sizeInBytes) {
        long size = Long.valueOf(sizeInBytes);
        File file = null;
        FileOutputStream out = null;
        BufferedOutputStream bufOut = null;
        try {
            switch (storage) {
                case INTERNAL:
                    file = getInternalFile(fileName);
                    out = openFileOutput(file.getName(), Context.MODE_PRIVATE);
                    break;
                case EXTERNAL:
                    assert Utils.isExternalStorageAvailable() :
                            "The external storage is not available";
                    File externalAppDir = getExternalFilesDir(null);
                    file = new File(externalAppDir, fileName);
                    out = new FileOutputStream(file);
                    break;
                case DONOTBACKUP:
                    file = new File(getNoBackupFilesDir(), fileName);
                    out = new FileOutputStream(file);
                    break;
            }

            if (file == null || out == null) {
                Log.d(TAG, "Unable to create file output stream");
                // Returning back to the caller activity.
                setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
                finish();
                return;
            }

            bufOut = new BufferedOutputStream(out);
            for (int i = 0; i < size; i++) {
                byte b = (byte) (255 * Math.random());
                bufOut.write(b);
            }

            String message = String.format("File created: %s, size: %s bytes",
                    file.getAbsolutePath(), sizeInBytes);

            Toast toast = Toast.makeText(this, message, Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
            toast.show();
            Log.d(TAG, message);

            // Returning back to the caller activity.
            setResult(MainActivityFragment.ADD_FILE_RESULT_SUCCESS);
            finish();
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
            // Returning back to the caller activity.
            setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
            finish();
        } finally {
            if (bufOut != null) {
                try {
                    bufOut.close();
                } catch (Exception e) {
                    // Ignore.
                }
            }
        }
    }

    private boolean isFileExists(String fileName) {
        File file = getInternalFile(fileName);
        if (file.exists()) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "This file exists: " + file.getName());
            }
            return true;
        }
        return false;
    }

    private boolean isSizeValid(String sizeInBytesParamValue) {
        long sizeInBytes = 0;
        try {
            sizeInBytes = Long.valueOf(sizeInBytesParamValue);
        } catch (NumberFormatException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Invalid file size: " + sizeInBytesParamValue);
            }
            return false;
        }

        // Validate file size value. It should be 0 or a positive number.
        if (sizeInBytes < 0) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Invalid file size: " + sizeInBytes);
            }
            return false;
        }
        return true;
    }

    private boolean isFileStorageParamValid(String fileStorage) {
        try {
            mFileStorage = FileStorage.valueOf(fileStorage);
        } catch (IllegalArgumentException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Invalid file storage: " + fileStorage);
            }
            return false;
        }
        return true;
    }

    private File getInternalFile(String fileName) {
        File internalAppDir = getFilesDir();
        return new File(internalAppDir, fileName);
    }

}