/*
 * 				Twidere - Twitter client for Android
 * 
 *  Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.mariotaku.twidere.fragment;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.res.Resources;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.FixedAsyncTaskLoader;
import androidx.loader.content.Loader;
import androidx.core.content.res.ResourcesCompat;
import androidx.appcompat.app.AlertDialog;
import android.text.TextUtils.TruncateAt;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;

import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.ArrayAdapter;
import org.mariotaku.twidere.extension.DialogExtensionsKt;
import org.mariotaku.twidere.fragment.iface.ISupportDialogFragmentCallback;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereArrayUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import static android.os.Environment.getExternalStorageDirectory;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACTION;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_FILE_EXTENSIONS;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_PATH;
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_PICK_DIRECTORY;

public class FileSelectorDialogFragment extends BaseDialogFragment implements LoaderCallbacks<List<File>>,
        OnClickListener, OnItemClickListener {

    private FilesAdapter mAdapter;

    @Override
    public void onActivityCreated(final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final Bundle args = getArguments();
        getLoaderManager().initLoader(0, args, this);
    }

    @Override
    public void onCancel(final DialogInterface dialog) {
        super.onCancel(dialog);
        final FragmentActivity a = getActivity();
        if (a instanceof Callback) {
            ((Callback) a).onCancelled(this);
        }
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which) {
        switch (which) {
            case DialogInterface.BUTTON_POSITIVE: {
                final FragmentActivity a = getActivity();
                if (isPickDirectory() && a instanceof Callback) {
                    ((Callback) a).onFilePicked(getCurrentDirectory());
                }
//                dismiss();
                break;
            }
            case DialogInterface.BUTTON_NEGATIVE: {
                final FragmentActivity a = getActivity();
                if (a instanceof Callback) {
                    ((Callback) a).onCancelled(this);
                }
                break;
            }
        }
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(final Bundle savedInstanceState) {
        mAdapter = new FilesAdapter(getActivity());
        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setAdapter(mAdapter, this);
        builder.setTitle(R.string.pick_file);
        builder.setNegativeButton(android.R.string.cancel, this);
        if (isPickDirectory()) {
            builder.setPositiveButton(android.R.string.ok, this);
        }
        final AlertDialog dialog = builder.create();
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(final DialogInterface dialog) {
                final AlertDialog alertDialog = (AlertDialog) dialog;
                DialogExtensionsKt.applyTheme(alertDialog);
                final ListView listView = alertDialog.getListView();
                listView.setOnItemClickListener(FileSelectorDialogFragment.this);
            }
        });
        return dialog;
    }

    @Override
    public Loader<List<File>> onCreateLoader(final int id, final Bundle args) {
        final String[] extensions = args.getStringArray(EXTRA_FILE_EXTENSIONS);
        final String path = args.getString(EXTRA_PATH);
        File currentDir = path != null ? new File(path) : getExternalStorageDirectory();
        if (currentDir == null) {
            currentDir = new File("/");
        }
        getArguments().putString(EXTRA_PATH, currentDir.getAbsolutePath());
        return new FilesLoader(getActivity(), currentDir, extensions);
    }

    @Override
    public void onDismiss(final DialogInterface dialog) {
        super.onDismiss(dialog);
        final FragmentActivity a = getActivity();
        if (a instanceof Callback) {
            ((Callback) a).onDismissed(this);
        }
    }

    @Override
    public void onItemClick(final AdapterView<?> view, final View child, final int position, final long id) {
        final File file = mAdapter.getItem(position);
        if (file == null) return;
        if (file.isDirectory()) {
            final Bundle args = getArguments();
            args.putString(EXTRA_PATH, file.getAbsolutePath());
            getLoaderManager().restartLoader(0, args, this);
        } else if (file.isFile() && !isPickDirectory()) {
            final FragmentActivity a = getActivity();
            if (a instanceof Callback) {
                ((Callback) a).onFilePicked(file);
            }
            dismiss();
        }
    }

    @Override
    public void onLoaderReset(final Loader<List<File>> loader) {
        mAdapter.setData(null, null);
    }

    @Override
    public void onLoadFinished(final Loader<List<File>> loader, final List<File> data) {
        final File currentDir = getCurrentDirectory();
        if (currentDir != null) {
            mAdapter.setData(currentDir, data);
            if (currentDir.getParent() == null) {
                setTitle("/");
            } else {
                setTitle(currentDir.getName());
            }
        }
    }

    private File getCurrentDirectory() {
        final Bundle args = getArguments();
        final String path = args.getString(EXTRA_PATH);
        return path != null ? new File(path) : null;
    }

    private boolean isPickDirectory() {
        final Bundle args = getArguments();
        final String action = args != null ? args.getString(EXTRA_ACTION) : null;
        return INTENT_ACTION_PICK_DIRECTORY.equals(action);
    }

    private void setTitle(final CharSequence title) {
        final Dialog dialog = getDialog();
        if (dialog == null) return;
        dialog.setTitle(title);
    }

    public interface Callback extends ISupportDialogFragmentCallback {

        void onFilePicked(File file);
    }

    private static class FilesAdapter extends ArrayAdapter<File> {

        private final int mPadding;
        private final int mActionIconColor;
        private final Resources mResources;

        private File mCurrentPath;

        public FilesAdapter(final Context context) {
            super(context, android.R.layout.simple_list_item_1);
            mResources = context.getResources();
            mActionIconColor = !ThemeUtils.INSTANCE.isLightTheme(context) ? 0xffffffff : 0xc0333333;
            mPadding = (int) (4 * mResources.getDisplayMetrics().density);
        }

        @Override
        public long getItemId(final int position) {
            return getItem(position).hashCode();
        }

        @Override
        public View getView(final int position, @Nullable final View convertView, final ViewGroup parent) {
            final View view = super.getView(position, convertView, parent);
            final TextView text = (TextView) (view instanceof TextView ? view : view.findViewById(android.R.id.text1));
            final File file = getItem(position);
            if (file == null || text == null) return view;
            if (mCurrentPath != null && file.equals(mCurrentPath.getParentFile())) {
                text.setText("..");
            } else {
                text.setText(file.getName());
            }
            text.setSingleLine(true);
            text.setEllipsize(TruncateAt.MARQUEE);
            text.setPadding(mPadding, mPadding, position, mPadding);
            final Drawable icon = ResourcesCompat.getDrawable(mResources,
                    file.isDirectory() ? R.drawable.ic_folder : R.drawable.ic_file, null);
            assert icon != null;
            icon.mutate();
            icon.setColorFilter(mActionIconColor, PorterDuff.Mode.SRC_ATOP);
            text.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
            return view;
        }

        public void setData(final File current, final List<File> data) {
            mCurrentPath = current;
            clear();
            if (data != null) {
                addAll(data);
            }
        }

    }

    private static class FilesLoader extends FixedAsyncTaskLoader<List<File>> {

        private final File path;
        private final String[] extensions;
        private final Pattern extensions_regex;

        private static final Comparator<File> NAME_COMPARATOR = new Comparator<File>() {
            @Override
            public int compare(final File file1, final File file2) {
                final Locale loc = Locale.getDefault();
                return file1.getName().toLowerCase(loc).compareTo(file2.getName().toLowerCase(loc));
            }
        };

        public FilesLoader(final Context context, final File path, final String[] extensions) {
            super(context);
            this.path = path;
            this.extensions = extensions;
            extensions_regex = extensions != null ? Pattern.compile(TwidereArrayUtils.toString(extensions, '|', false),
                    Pattern.CASE_INSENSITIVE) : null;
        }

        @Override
        public List<File> loadInBackground() {
            if (path == null || !path.isDirectory()) return Collections.emptyList();
            final File[] listed_files = path.listFiles();
            if (listed_files == null) return Collections.emptyList();
            final List<File> dirs = new ArrayList<>();
            final List<File> files = new ArrayList<>();
            for (final File file : listed_files) {
                if (!file.canRead() || file.isHidden()) {
                    continue;
                }
                if (file.isDirectory()) {
                    dirs.add(file);
                } else if (file.isFile()) {
                    final String name = file.getName();
                    final int idx = name.lastIndexOf(".");
                    if (extensions == null || extensions.length == 0 || idx == -1 || idx > -1
                            && extensions_regex.matcher(name.substring(idx + 1)).matches()) {
                        files.add(file);
                    }
                }
            }
            Collections.sort(dirs, NAME_COMPARATOR);
            Collections.sort(files, NAME_COMPARATOR);
            final List<File> list = new ArrayList<>();
            final File parent = path.getParentFile();
            if (path.getParentFile() != null) {
                list.add(parent);
            }
            list.addAll(dirs);
            list.addAll(files);
            return list;
        }

        @Override
        protected void onStartLoading() {
            forceLoad();
        }

        @Override
        protected void onStopLoading() {
            cancelLoad();
        }
    }

}
