package com.github.developerpaul123.filepickerlibrary;

import android.Manifest;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.afollestad.materialdialogs.MaterialDialog;
import com.github.developerpaul123.filepickerlibrary.adapter.FileRecyclerViewAdapter;
import com.github.developerpaul123.filepickerlibrary.enums.MimeType;
import com.github.developerpaul123.filepickerlibrary.enums.Request;
import com.github.developerpaul123.filepickerlibrary.enums.Scope;
import com.github.developerpaul123.filepickerlibrary.enums.ThemeType;

import java.io.File;

/**
 * Created by Paul on 10/8/2015.
 */
public class FilePicker extends AppCompatActivity implements NameFileDialogInterface {

    /**
     * Constant value for adding the REQUEST int as an extra to the {@code FilePickerActivity}
     * {@code Intent}
     */
    public static final String REQUEST = "request";

    /**
     * Constant value for adding the SCOPE enum as an extra to the {@code FilePickerActivity}
     * {@code Intent} The default is {@code FileType.ALL} see
     * {@link Scope} for other types.
     */
    public static final String SCOPE = "scope";

    /**
     * Constant label value for sending a color id extra in the calling intent for this
     * {@code FilePickerActivity}
     */
    public static final String INTENT_EXTRA_COLOR_ID = "intentExtraColorId";

    /**
     * Constant label value for sending a drawable image id in the calling intent for this
     * {@code FilePickerActivity}
     */
    public static final String INTENT_EXTRA_DRAWABLE_ID = "intentExtraDrawableId";

    /**
     * Constant label value for sending a color id to be used for the floating action button.
     */
    public static final String INTENT_EXTRA_FAB_COLOR_ID = "intentExtraFabColorId";

    /**
     * Constant for retrieving the return file path in {@link #onActivityResult(int, int, Intent)}
     * If the result code is RESULT_OK then the file path will not be null. This should always be
     * checked though.
     * <p/>
     * Example:
     * <p/>
     * {@code
     * <p/>
     * protected void onActivityResult(int resultCode, int requestCode, Intent data) {
     * <p/>
     * if(resultCode == RESULT_OK && requestCode == FILEPICKER) {
     * String filePath = data.getStringExtra(FilePickerActivity.FILE_EXTRA_DATA_PATH);
     * <p/>
     * if(filePath != null) {
     * //do something with the string.
     * }
     * }
     * }
     * }
     */
    public static final String FILE_EXTRA_DATA_PATH = "fileExtraPath";
    /**
     * Constant used for passing a {@link ThemeType} enum
     * to this activity from the calling activity.
     */
    public static final String THEME_TYPE = "themeType";
    /**
     * Constant used for setting the mime type of the files that the user is supposed to choose.
     */
    public static final String MIME_TYPE = "mimeType";
    /**
     * Request code for app permissions.
     */
    private static final int REQUEST_FOR_READ_EXTERNAL_STORAGE = 101;
    private static final OvershootInterpolator interpolator = new OvershootInterpolator();
    /**
     * Current toolbar
     */
    Toolbar toolbar;

    /**
     * Floating action button.
     */
    FloatingActionButton fab;

    boolean isFabShowing;
    /**
     * Array of files
     */
    File[] files;
    /**
     * Recycler view for list of files.
     */
    private RecyclerView recyclerView;
    /**
     * Button that allows user to selet the file or directory.
     */
    private Button selectButton;
    /**
     * Allows user to enter a directory tree.
     */
    private Button openButton;
    /**
     * Container that encloses the two buttons above.
     */
    private LinearLayout buttonContainer;
    /**
     * Relative layout that holds the header.
     */
    private RelativeLayout header;
    /**
     * {@code Animation} for showing the buttonContainer
     */
    private Animation slideUp;
    /**
     * {@code Animation} for hiding the buttonContainer
     */
    private Animation slideDown;
    private Animation scaleIn;
    private Animation scaleOut;
    /**
     * {@code File} current directory
     */
    private File curDirectory;
    /**
     * {@code File} the directory one level up from the current one
     */
    private File lastDirectory;
    /**
     * {@code FileListAdapter} object
     */
    private FileRecyclerViewAdapter adapter;
    /**
     * The currently selected file
     */
    private File currentFile;
    private boolean areButtonsShowing;
    private final FileRecyclerViewAdapter.Callback callback = new FileRecyclerViewAdapter.Callback() {
        @Override
        public void onItemClicked(View item, int position) {

            if (position > 0 && position <= files.length - 1) {
                currentFile = files[position];
            }

            if (adapter.getSelectedPosition() == position) {
                hideButtons();
                adapter.setSelectedPosition(-1);
            } else {
                adapter.setSelectedPosition(position);
                showButtons();
            }
        }
    };
    /**
     * {@link Scope} enum
     */
    private Scope scopeType;
    /**
     * {@link ThemeType} enum for the type of them for this
     * activity.
     */
    private ThemeType themeType;
    /**
     * Actual mime type to be used for file browsing
     */
    private String mimeType;
    /**
     * Request code for this activity
     */
    private Request requestCode;
    /**
     * {@code Intent} used to send back the data to the calling activity
     */
    private Intent data;
    /**
     * {@code int} used to store the color resource id sent as an extra to this activity.
     */
    private int colorId;
    /**
     * {@code int} used to store the color for the floating action button.
     */
    private int fabColorId;
    /**
     * {@code int} used to store the drawable resource id sent as an extra to this activity.
     */
    private int drawableId;
    /**
     * (@code int) saves the previous first visible item when scrolling, used to make the buttons
     * disappear
     */
    private int mLastFirstVisibleItem;
    /**
     * (@code Context) saves the context of activity so that you can use it in onClick calls, etc.
     */
    private Context mContext;
    /**
     * Layout manager for the Recycler View.
     */
    private LinearLayoutManager mLinearLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        setContentView(R.layout.material_file_picker_activity_layout);
        recyclerView = (RecyclerView) findViewById(R.id.file_picker_recycler_view);
        toolbar = (Toolbar) findViewById(R.id.file_picker_base_toolbar);
        fab = (FloatingActionButton) findViewById(R.id.file_picker_floating_action_button);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                NameFileDialog nfd = NameFileDialog.newInstance();
                nfd.show(getFragmentManager(), "NameDialog");
            }
        });
        isFabShowing = true;
//        //get the theme type for this activity
//        themeType = (ThemeType) getIntent().getSerializableExtra(THEME_TYPE);
//        if (themeType == null) {
//            themeType = ThemeType.ACTIVITY;
//        }
//
//        setThemeType(themeType);

        areButtonsShowing = false;

        //set up the mime type for the file.
        Object rawMimeTypeParameter = getIntent().getExtras().get(MIME_TYPE);
        if (rawMimeTypeParameter instanceof String) {
            mimeType = (String) rawMimeTypeParameter;
        } else if (rawMimeTypeParameter instanceof MimeType) {
            mimeType = ((MimeType) rawMimeTypeParameter).getMimeType();
        } else {
            mimeType = null;
        }

        //set up the animations
        setUpAnimations();

        Intent givenIntent = getIntent();

        //get the scope type and request code. Defaults are all files and request of a directory
        //path.
        scopeType = (Scope) givenIntent.getSerializableExtra(SCOPE);
        if (scopeType == null) {
            //set default if it is null
            scopeType = Scope.ALL;
        }
        requestCode = (Request) givenIntent.getSerializableExtra(REQUEST);

        colorId = givenIntent.getIntExtra(INTENT_EXTRA_COLOR_ID, android.R.color.holo_blue_light);
        drawableId = givenIntent.getIntExtra(INTENT_EXTRA_DRAWABLE_ID, -1);
        fabColorId = givenIntent.getIntExtra(INTENT_EXTRA_FAB_COLOR_ID, -1);

        mLinearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setLayoutManager(mLinearLayoutManager);
        recyclerView.setHasFixedSize(true);
        adapter = new FileRecyclerViewAdapter(this, new File[0], scopeType, callback);
        recyclerView.setAdapter(adapter);

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                int firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
                if (Math.abs(dy) >= 5) {
                    if (dy > 0) {
                        toggleButton(false);
                    } else if (dy < 0) {
                        toggleButton(true);
                    }
                    if (areButtonsShowing) {
                        hideButtons();
                        adapter.setSelectedPosition(-1);
                        mLastFirstVisibleItem = firstVisibleItem;
                    } else if (firstVisibleItem > adapter.getSelectedPosition()) {
                        hideButtons();
                        adapter.setSelectedPosition(-1);
                    }
                } else {
                    mLastFirstVisibleItem = firstVisibleItem;
                }
                super.onScrolled(recyclerView, dx, dy);

            }
        });

        initializeViews();

        //drawable has not been set so set the color.
        setHeaderBackground(colorId, drawableId);

        //check for proper permissions.
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
            int permissionCheck = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.READ_EXTERNAL_STORAGE);
            if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                    //Show permission rationale.
                    new MaterialDialog.Builder(this)
                            .title(R.string.file_picker_permission_rationale_dialog_title)
                            .content(R.string.file_picker_permission_rationale_dialog_content)
                            .positiveText(R.string.file_picker_ok)
                            .negativeText(R.string.file_picker_cancel)
                            .callback(new MaterialDialog.ButtonCallback() {
                                @Override
                                public void onPositive(MaterialDialog dialog) {
                                    ActivityCompat.requestPermissions(FilePicker.this,
                                            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
                                                    Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                            REQUEST_FOR_READ_EXTERNAL_STORAGE);
                                }

                                @Override
                                public void onNegative(MaterialDialog dialog) {
                                    setResult(RESULT_CANCELED);
                                    finish();
                                }
                            })
                            .show();
                } else {
                    ActivityCompat.requestPermissions(this,
                            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
                                    Manifest.permission.WRITE_EXTERNAL_STORAGE},
                            REQUEST_FOR_READ_EXTERNAL_STORAGE);
                }
            }
        } else {
            init();
        }
    }

    /**
     * Toggles the material floating action button.
     *
     * @param visible
     */
    public void toggleButton(final boolean visible) {
        if (isFabShowing != visible) {
            isFabShowing = visible;
            int height = fab.getHeight();
            if (height == 0) {
                ViewTreeObserver vto = fab.getViewTreeObserver();
                if (vto.isAlive()) {
                    vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            ViewTreeObserver currentVto = fab.getViewTreeObserver();
                            if (currentVto.isAlive()) {
                                currentVto.removeOnPreDrawListener(this);
                            }
                            toggleButton(visible);
                            return true;
                        }
                    });
                    return;
                }
            }
            int translationY = visible ? 0 : height;
            fab.animate().setInterpolator(interpolator)
                    .setDuration(350)
                    .translationY(translationY);

            // On pre-Honeycomb a translated view is still clickable, so we need to disable clicks manually
            fab.setClickable(visible);
        }
    }

    @Override
    public void onBackPressed() {
        if (lastDirectory != null && !curDirectory.getPath()
                .equals(Environment.getExternalStorageDirectory().getPath())) {
            new UpdateFilesTask(this).execute(lastDirectory);
        } else {
            setResult(RESULT_CANCELED);
            finish();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            //see if we got the permission.
            case REQUEST_FOR_READ_EXTERNAL_STORAGE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
                        && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                    init();
                } else {
                    setResult(RESULT_CANCELED);
                    finish();
                }
                return;
        }
    }

    /**
     * Initialize the current directory.
     */
    private void init() {
        curDirectory = new File(Environment.getExternalStorageDirectory().getPath());
        currentFile = new File(curDirectory.getPath());
        lastDirectory = curDirectory.getParentFile();

        if (curDirectory.isDirectory()) {
            new UpdateFilesTask(this).execute(curDirectory);
        } else {
            try {
                throw new Exception(getString(R.string.file_picker_directory_error));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Initializes all the views in the layout of the activity.
     */
    private void initializeViews() {
        buttonContainer = (LinearLayout) findViewById(R.id.button_container);

        selectButton = (Button) findViewById(R.id.select_button);
        selectButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (requestCode == Request.DIRECTORY) {
                    if (currentFile.isDirectory()) {
                        curDirectory = currentFile;
                        data = new Intent();
                        data.putExtra(FILE_EXTRA_DATA_PATH, currentFile.getAbsolutePath());
                        setResult(RESULT_OK, data);
                        finish();
                    } else {
                        Snackbar.make(getWindow().getDecorView(), R.string.file_picker_snackbar_select_directory_message, Snackbar.LENGTH_SHORT).show();
                    }
                } else { //request code is for a file
                    if (currentFile.isDirectory()) {
                        curDirectory = currentFile;
                        new UpdateFilesTask(FilePicker.this).execute(curDirectory);
                    } else {
                        if (!TextUtils.isEmpty(mimeType)) {
                            MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
                            String requiredExtension = "." + mimeTypeMap.getExtensionFromMimeType(mimeType);
                            if (requiredExtension.equalsIgnoreCase(fileExt(currentFile.toString()))) {
                                data = new Intent();
                                data.putExtra(FILE_EXTRA_DATA_PATH, currentFile.getAbsolutePath());
                                setResult(RESULT_OK, data);
                                finish();
                            } else {
                                Snackbar.make(getWindow().getDecorView(), String.format(getString(R.string.file_picker_snackbar_select_file_ext_message), requiredExtension), Snackbar.LENGTH_SHORT).show();
                            }
                        } else {
                            data = new Intent();
                            data.putExtra(FILE_EXTRA_DATA_PATH, currentFile.getAbsolutePath());
                            setResult(RESULT_OK, data);
                            finish();
                        }
                    }
                }
            }
        });

        openButton = (Button) findViewById(R.id.open_button);
        openButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (currentFile.isDirectory()) {
                    curDirectory = currentFile;
                    toolbar.setTitle(curDirectory.getName());
                    new UpdateFilesTask(FilePicker.this).execute(curDirectory);
                } else {
                    Intent newIntent = new Intent(Intent.ACTION_VIEW);
                    String file = currentFile.toString();
                    if (file != null) {
                        newIntent.setDataAndType(Uri.fromFile(currentFile), mimeType);
                        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        try {
                            startActivity(newIntent);
                        } catch (ActivityNotFoundException e) {
                            Snackbar.make(getWindow().getDecorView(), R.string.file_picker_snackbar_no_file_type_handler, Snackbar.LENGTH_SHORT).show();
                        }
                    } else {
                        Snackbar.make(getWindow().getDecorView(), R.string.file_picker_snackbar_no_read_type, Snackbar.LENGTH_SHORT).show();
                    }

                }
            }
        });

        buttonContainer.setVisibility(View.INVISIBLE);
    }

    /**
     * Returns the file extension of a file.
     *
     * @param url the file path
     * @return
     */
    private String fileExt(String url) {
        if (url.indexOf("?") > -1) {
            url = url.substring(0, url.indexOf("?"));
        }
        if (url.lastIndexOf(".") == -1) {
            return null;
        } else {
            String ext = url.substring(url.lastIndexOf("."));
            if (ext.indexOf("%") > -1) {
                ext = ext.substring(0, ext.indexOf("%"));
            }
            if (ext.indexOf("/") > -1) {
                ext = ext.substring(0, ext.indexOf("/"));
            }
            return ext.toLowerCase();

        }
    }

    /**
     * Initializes the animations used in this activity.
     */
    private void setUpAnimations() {
        slideUp = AnimationUtils.loadAnimation(this,
                R.anim.slide_up);
        slideDown = AnimationUtils.loadAnimation(this,
                R.anim.slide_down);
        scaleIn = AnimationUtils.loadAnimation(this,
                R.anim.scale_in);
        scaleOut = AnimationUtils.loadAnimation(this,
                R.anim.scale_out);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            setResult(RESULT_CANCELED);
            finish();
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Method that shows the sliding panel
     */
    private void showButtons() {
        if (!areButtonsShowing) {
            buttonContainer.clearAnimation();
            buttonContainer.startAnimation(slideUp);
            buttonContainer.setVisibility(View.VISIBLE);
            areButtonsShowing = true;
        }
    }

    /**
     * Method that hides the sliding panel
     */
    private void hideButtons() {
        if (areButtonsShowing) {
            buttonContainer.clearAnimation();
            buttonContainer.startAnimation(slideDown);
            buttonContainer.setVisibility(View.INVISIBLE);
            areButtonsShowing = false;
        }
    }

    @Override
    public void onReturnFileName(String fileName) {
        if (fileName.equalsIgnoreCase("") || fileName.isEmpty()) {
            fileName = null;
        }
        if (fileName != null && curDirectory != null) {
            File file = new File(curDirectory.getPath() + "//" + fileName);
            boolean created = false;
            if (!file.exists()) {
                created = file.mkdirs();
            }
            if (created) {
                new UpdateFilesTask(this).execute(curDirectory);
            }
        }
    }

    /**
     * Set the background color of the header
     *
     * @param colorResId    Resource Id of the color
     * @param drawableResId Resource Id of the drawable
     */
    private void setHeaderBackground(int colorResId, int drawableResId) {
        //TODO
    }

    /**
     * Class that updates the list view with a new array of files. Resets the adapter and the
     * directory title.
     */
    private class UpdateFilesTask extends AsyncTask<File, Void, File[]> {

        private final Context mContext;
        private File[] fileArray;
        private ProgressDialog dialog;
        private File directory;

        private UpdateFilesTask(Context context) {
            mContext = context;
        }

        @Override
        protected File[] doInBackground(File... files) {
            directory = files[0];
            fileArray = files[0].listFiles();
            return fileArray;
        }

        @Override
        protected void onPreExecute() {
            dialog = new ProgressDialog(mContext);
            dialog.setMessage(getString(R.string.file_picker_progress_dialog_loading));
            dialog.setCancelable(false);
            dialog.show();
            hideButtons();
            recyclerView.setAdapter(null);
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(File[] localFiles) {
            files = localFiles;
            if (directory.getPath().equalsIgnoreCase(Environment
                    .getExternalStorageDirectory().getPath())) {
                toolbar.setTitle(getString(R.string.file_picker_default_directory_title));

            } else {
                toolbar.setTitle(directory.getName());

            }
            lastDirectory = directory.getParentFile();
            curDirectory = directory;
//            adapter.notifyDataSetChanged();
//            for(int i = 0; i < files.length; i++) {
//                adapter.addFile(files[i]);
//            }
            if (files != null) {
                adapter = new FileRecyclerViewAdapter(FilePicker.this, files, scopeType, callback);
                //TODO: Fix this, figure out how to add and remove the header.
                recyclerView.setAdapter(adapter);
            }
            //make sure the button is showing.
            if (!isFabShowing) {
                toggleButton(true);
            }
            if (dialog.isShowing()) {
                dialog.dismiss();
            }
            super.onPostExecute(files);
        }

        /**
         * Checks if the files contain a directory.
         *
         * @param files the files.
         * @return a boolean, true if there is a file that is a directory.
         */
        public boolean directoryExists(File[] files) {
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    return true;
                }
            }
            return false;
        }
    }


}