/*
 * Copyright (C) 2013 Simon Marquis (http://www.simon-marquis.fr)
 * 
 * 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 fr.simon.marquis.preferencesmanager.ui;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;
import java.util.Map.Entry;

import fr.simon.marquis.preferencesmanager.R;
import fr.simon.marquis.preferencesmanager.model.PreferenceFile;
import fr.simon.marquis.preferencesmanager.model.PreferenceSortType;
import fr.simon.marquis.preferencesmanager.model.PreferenceType;
import fr.simon.marquis.preferencesmanager.util.Ui;
import fr.simon.marquis.preferencesmanager.util.Utils;

public class PreferencesFragment extends Fragment {
    private static final int CODE_EDIT_FILE = 666;

    public static final String ARG_ICON_URI = "ICON_URI";
    public static final String ARG_FILE = "FILE";
    public static final String ARG_PACKAGE_NAME = "PACKAGE_NAME";

    private Uri mIconUri;
    private String mFile;
    private String mPackageName;

    private SearchView mSearchView;

    public PreferenceFile preferenceFile;

    private OnPreferenceFragmentInteractionListener mListener;

    private GridView gridView;
    private View loadingView, emptyView;
    private TextView emptyViewText;

    public static PreferencesFragment newInstance(String paramFile, String paramPackageName, Uri paramIconUri) {
        PreferencesFragment fragment = new PreferencesFragment();
        Bundle args = new Bundle();
        args.putParcelable(ARG_ICON_URI, paramIconUri);
        args.putString(ARG_FILE, paramFile);
        args.putString(ARG_PACKAGE_NAME, paramPackageName);
        fragment.setArguments(args);
        return fragment;
    }

    public PreferencesFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mIconUri = getArguments().getParcelable(ARG_ICON_URI);
            mFile = getArguments().getString(ARG_FILE);
            mPackageName = getArguments().getString(ARG_PACKAGE_NAME);
        }

        setRetainInstance(true);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_preferences, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        loadingView = view.findViewById(R.id.loadingView);
        emptyView = view.findViewById(R.id.emptyView);
        emptyViewText = (TextView) view.findViewById(R.id.emptyViewText);
        gridView = (GridView) view.findViewById(R.id.gridView);
        gridView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL);

        updateFilter(null);

        if (preferenceFile == null) {
            launchTask();
        } else {
            updateListView(preferenceFile, false);
        }
    }

    private void launchTask() {
        ParsingTask task = new ParsingTask(mFile);
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.preferences_fragment, menu);

        MenuItem searchItem = menu.findItem(R.id.menu_search);
        mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        mSearchView.setQueryHint(getString(R.string.action_search_preference));
        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String s) {
                Ui.hideSoftKeyboard(getActivity(), mSearchView);
                mSearchView.clearFocus();
                return true;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                return mSearchView.hasFocus() && updateFilter(s);
            }
        });

        MenuItemCompat.setOnActionExpandListener(searchItem, new OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionExpand(MenuItem arg0) {
                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem arg0) {
                return updateFilter(null);
            }
        });
        super.onCreateOptionsMenu(menu, inflater);
    }

    @SuppressWarnings("ConstantConditions")
    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        menu.findItem(R.id.action_add).setEnabled(preferenceFile != null && preferenceFile.isValidPreferenceFile());
        menu.findItem(R.id.action_add).setIcon(preferenceFile != null && preferenceFile.isValidPreferenceFile() ? R.drawable.ic_action_add : R.drawable.ic_action_add_disabled);
        MenuItem sortAlpha = menu.findItem(R.id.action_sort_alpha);
        MenuItem sortType = menu.findItem(R.id.action_sort_type);
        sortAlpha.setChecked(false);
        sortType.setChecked(false);
        if (PreferencesActivity.preferenceSortType == PreferenceSortType.ALPHANUMERIC) {
            sortAlpha.setChecked(true);
        } else if (PreferencesActivity.preferenceSortType == PreferenceSortType.TYPE_AND_ALPHANUMERIC) {
            sortType.setChecked(true);
        }
        menu.findItem(R.id.action_restore_file).setVisible(mListener != null && mListener.canRestoreFile(mFile));
        super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_add_int:
                showPrefDialog(PreferenceType.INT);
                return true;
            case R.id.action_add_boolean:
                showPrefDialog(PreferenceType.BOOLEAN);
                return true;
            case R.id.action_add_string:
                showPrefDialog(PreferenceType.STRING);
                return true;
            case R.id.action_add_float:
                showPrefDialog(PreferenceType.FLOAT);
                return true;
            case R.id.action_add_long:
                showPrefDialog(PreferenceType.LONG);
                return true;
            case R.id.action_add_stringset:
                showPrefDialog(PreferenceType.STRINGSET);
                return true;
            case R.id.action_edit_file:
                if (preferenceFile == null) {
                    if (getActivity() != null) {
                        getActivity().finish();
                    }
                }
                Intent intent = new Intent(getActivity(), FileEditorActivity.class);
                intent.putExtra(ARG_ICON_URI, mIconUri);
                intent.putExtra(ARG_FILE, mFile);
                intent.putExtra(ARG_PACKAGE_NAME, mPackageName);
                startActivityForResult(intent, CODE_EDIT_FILE);
                return true;
            case R.id.action_sort_alpha:
                setSortType(PreferenceSortType.ALPHANUMERIC);
                return true;
            case R.id.action_sort_type:
                setSortType(PreferenceSortType.TYPE_AND_ALPHANUMERIC);
                return true;
            case R.id.action_backup_file:
                if (mListener != null) {
                    mListener.onBackupFile(mFile);
                }
                return true;
            case R.id.action_restore_file:
                restoreBackup();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void restoreBackup() {
        if (mListener != null) {
            RestoreDialogFragment.show(this, getFragmentManager(), mFile, mListener.getBackups(mFile));
        }
    }

    private boolean updateFilter(String s) {
        String filter = !TextUtils.isEmpty(s) ? s.trim() : null;
        PreferenceAdapter adapter = ((PreferenceAdapter) gridView.getAdapter());
        if (adapter == null) {
            return false;
        }
        adapter.setFilter(filter);
        adapter.getFilter().filter(filter);
        return true;
    }

    private void setSortType(PreferenceSortType type) {
        if (PreferencesActivity.preferenceSortType != type) {
            PreferencesActivity.preferenceSortType = type;
            if (getActivity() != null) {
                getActivity().invalidateOptionsMenu();
                PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putInt(PreferencesActivity.KEY_SORT_TYPE, type.ordinal()).commit();
            }

            if (gridView.getAdapter() != null && preferenceFile != null) {
                preferenceFile.updateSort();
                ((PreferenceAdapter) gridView.getAdapter()).notifyDataSetChanged();
            }
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CODE_EDIT_FILE && resultCode == ActionBarActivity.RESULT_OK) {
            loadingView.setVisibility(View.VISIBLE);
            gridView.setVisibility(View.GONE);

            if (getActivity() != null) {
                Animation fadeInAnim = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in);
                if (fadeInAnim != null) {
                    loadingView.startAnimation(fadeInAnim);
                }
                Animation fadeOutAnim = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
                if (fadeOutAnim != null) {
                    gridView.startAnimation(fadeOutAnim);
                }
            }
            launchTask();
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void showPrefDialog(PreferenceType type) {
        showPrefDialog(type, false, null, null);
    }

    private void showPrefDialog(PreferenceType type, boolean editMode, String key, Object obj) {
        PreferenceDialog newFragment = PreferenceDialog.newInstance(type, editMode, key, obj);
        newFragment.setTargetFragment(this, ("Fragment:" + mFile).hashCode());
        FragmentManager fm = getFragmentManager();
        if (fm != null) {
            newFragment.show(fm, mFile + "#" + key);
        }
    }

    public void addPrefKeyValue(String previousKey, String newKey, Object value, boolean editMode) {
        if (preferenceFile == null) {
            return;
        }
        preferenceFile.add(previousKey, newKey, value, editMode);
        Utils.savePreferences(preferenceFile, mFile, mPackageName, getActivity());
        ((PreferenceAdapter) gridView.getAdapter()).notifyDataSetChanged();
    }

    public void deletePref(String key) {
        if (preferenceFile == null) {
            return;
        }
        preferenceFile.removeValue(key);
        Utils.savePreferences(preferenceFile, mFile, mPackageName, getActivity());
        ((PreferenceAdapter) gridView.getAdapter()).notifyDataSetChanged();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnPreferenceFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnPreferenceFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    void updateListView(PreferenceFile p, boolean animate) {
        if (getActivity() == null || getActivity().isFinishing()) {
            return;
        }
        if (p == null) {
            getActivity().finish();
            return;
        }
        preferenceFile = p;
        emptyViewText.setText(preferenceFile.isValidPreferenceFile() ? R.string.empty_preference_file_valid : R.string.empty_preference_file_invalid);
        loadingView.setVisibility(View.GONE);
        gridView.setVisibility(View.VISIBLE);

        if (animate) {
            if (getActivity() != null) {
                Animation fadeOut = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
                if (fadeOut != null) {
                    loadingView.startAnimation(fadeOut);
                }
                Animation fadeIn = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in);
                if (fadeIn != null) {
                    gridView.startAnimation(fadeIn);
                }
            }
        }

        gridView.setAdapter(new PreferenceAdapter(getActivity(), this));
        gridView.setEmptyView(emptyView);
        gridView.setOnItemClickListener(new OnItemClickListener() {
            @SuppressWarnings("unchecked")
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {

                Entry<String, Object> item = (Entry<String, Object>) gridView.getAdapter().getItem(arg2);
                PreferenceType type = PreferenceType.fromObject(item.getValue());
                if (type == PreferenceType.UNSUPPORTED) {
                    Toast.makeText(getActivity(), R.string.preference_unsupported, Toast.LENGTH_SHORT).show();
                } else {
                    showPrefDialog(type, true, item.getKey(), item.getValue());
                }
            }
        });
        gridView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

            @Override
            public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
                ((PreferenceAdapter) gridView.getAdapter()).itemCheckedStateChanged(position, checked);
                mode.setTitle(Html.fromHtml("<b>" + gridView.getCheckedItemCount() + "</b>"));
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.action_delete:
                        ((PreferenceAdapter) gridView.getAdapter()).deleteSelection();
                        Utils.savePreferences(preferenceFile, mFile, mPackageName, getActivity());
                        ((PreferenceAdapter) gridView.getAdapter()).notifyDataSetChanged();
                        mode.finish();
                        return true;
                    case R.id.action_select_all:
                        boolean check = gridView.getCheckedItemCount() != gridView.getCount();
                        for (int i = 0; i < gridView.getCount(); i++) {
                            gridView.setItemChecked(i, check);
                        }
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                MenuInflater inflater = mode.getMenuInflater();
                if (inflater != null) {
                    inflater.inflate(R.menu.cab, menu);
                }
                return true;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                ((PreferenceAdapter) gridView.getAdapter()).resetSelection();
                getActivity().invalidateOptionsMenu();
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

        });
        getActivity().invalidateOptionsMenu();
    }

    public interface OnPreferenceFragmentInteractionListener {

        public void onBackupFile(String fullPath);

        public boolean canRestoreFile(String fullPath);

        public List<String> getBackups(String fullPath);
    }

    public class ParsingTask extends AsyncTask<Void, Void, PreferenceFile> {
        private final String mFile;

        public ParsingTask(String file) {
            super();
            this.mFile = file;
        }

        @Override
        protected PreferenceFile doInBackground(Void... params) {
            long start = System.currentTimeMillis();
            Log.d(Utils.TAG, "Start reading " + mFile);
            String content = Utils.readFile(mFile);
            Log.d(Utils.TAG, "End reading " + mFile + " --> " + (System.currentTimeMillis() - start) + " ms");
            return PreferenceFile.fromXml(content);
        }

        @Override
        protected void onPostExecute(PreferenceFile result) {
            super.onPostExecute(result);
            updateListView(result, true);
        }

    }

}