package jp.gr.java_conf.shiseissi.commonlib;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
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 static android.view.ViewGroup.LayoutParams.FILL_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileFilter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author shisei
 *
 * TODO 全体的にファイルアクセスの非同期化
 */
public class FileSelectFragment extends Fragment
implements EditTextDialogFragment.EventListener {
    private static final String EXTRA_DIR_ONLY = "EXTRA_DIR_ONLY";
    private static final String EXTRA_INIT_DIR = "EXTRA_INIT_DIR";
    private static final String EXTRA_STR_RES = "EXTRA_STR_RES";

    private static final String KEY_ARGUMENTS = "KEY_ARGUMENTS";

    public static interface EventListener {
        void onOk(FileSelectFragment fragment, String filePath);
        void onCancel(FileSelectFragment fragment);
        void onChangeDirectory(FileSelectFragment fragment, String dir);
    }

    public static class StringResourceParams implements Parcelable {
        public int moveButton;
        public int makeDirMenu;
        public int makeDirTitle;
        public int makeDirHint;
        public int makeDirSucceed;
        public int makeDirFail;

        public StringResourceParams() {
        }

        @Override
        public int describeContents() {
            return 0;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(moveButton);
            dest.writeInt(makeDirMenu);
            dest.writeInt(makeDirTitle);
            dest.writeInt(makeDirHint);
            dest.writeInt(makeDirSucceed);
            dest.writeInt(makeDirFail);
        }

        public static final Parcelable.Creator<StringResourceParams> CREATOR =
            new Creator<FileSelectFragment.StringResourceParams>() {
                @Override
                public StringResourceParams[] newArray(int size) {
                    return new StringResourceParams[size];
                }

                @Override
                public StringResourceParams createFromParcel(Parcel source) {
                    return new StringResourceParams(source);
                }
            };

        private StringResourceParams(Parcel in) {
            moveButton = in.readInt();
            makeDirMenu = in.readInt();
            makeDirTitle = in.readInt();
            makeDirHint = in.readInt();
            makeDirSucceed = in.readInt();
            makeDirFail = in.readInt();
        }
    }

    private Context mContext;
    private boolean mDirOnly;
    private ViewPager mDirPager;
    private EditText mFilePathView;
    private StringResourceParams mStrRes;

    private MenuItem mMakeDirMenu;

    private String mCurrentDir;

    public static FileSelectFragment newInstance(boolean dirOnly,
            String initDir, StringResourceParams strRes) {
        FileSelectFragment f = new FileSelectFragment();
        Bundle args = new Bundle(3);
        args.putBoolean(EXTRA_DIR_ONLY, dirOnly);
        args.putString(EXTRA_INIT_DIR, initDir);
        args.putParcelable(EXTRA_STR_RES, strRes);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mContext = getActivity().getApplicationContext();

        Bundle args = getArguments();
        if (args == null && savedInstanceState != null) {
            args = savedInstanceState.getBundle(KEY_ARGUMENTS);
            setArguments(args);
        }
        if (args != null) {
            mDirOnly = args.getBoolean(EXTRA_DIR_ONLY, false);
            mStrRes = args.getParcelable(EXTRA_STR_RES);
        }

        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle args = getArguments();
        String initDir = null;
        if (args != null) {
            initDir = args.getString(EXTRA_INIT_DIR);
        }
        if (initDir == null) {
            initDir = Environment.getExternalStorageDirectory().getAbsolutePath();
        }

        FragmentActivity activity = getActivity();
        LinearLayout root = new LinearLayout(activity);
        root.setLayoutParams(new ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT));
        root.setOrientation(LinearLayout.VERTICAL);

        LinearLayout filePathContainer = new LinearLayout(activity);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                FILL_PARENT, WRAP_CONTENT, 0.0f);
        root.addView(filePathContainer, params);

        filePathContainer.setOrientation(LinearLayout.HORIZONTAL);

        EditText filePath = new EditText(activity);
        mFilePathView = filePath;
        params = new LinearLayout.LayoutParams(
                0, WRAP_CONTENT, 1.0f);
        filePathContainer.addView(filePath, params);

//        filePath.setInputType(InputType.TYPE_CLASS_TEXT);

        Button move = new Button(activity);
        params = new LinearLayout.LayoutParams(
                WRAP_CONTENT, WRAP_CONTENT, 0.0f);
        filePathContainer.addView(move, params);

        if (mStrRes != null) {
            move.setText(mStrRes.moveButton);
        }
        move.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeDirectoryByFilePathView();
            }
        });

        ViewPager dirPager = new ViewPager(activity);
        mDirPager = dirPager;
        params = new LinearLayout.LayoutParams(
                FILL_PARENT, 0, 1.0f);
        root.addView(dirPager, params);

        DirPagerAdapter adapter = new DirPagerAdapter(this, dirPager,
                mDirOnly, initDir);
        dirPager.setAdapter(adapter);
        int count = adapter.getCount();
        if (count > 0) {
            dirPager.setCurrentItem(count - 1, false);
        }
        dirPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                DirPagerAdapter adapter = (DirPagerAdapter) mDirPager.getAdapter();
                if (adapter.mDirPath != null) {
                    String path = convertToPathString(adapter.mDirPath, position);
                    onChangeDirectory(path);

                    ListView list = (ListView) mDirPager.findViewWithTag(path);
                    DirListAdapter listAdapter = (DirListAdapter) list.getAdapter();
                    listAdapter.updateFiles();
                }
            }
        });

        LinearLayout buttons = new LinearLayout(activity);
        params = new LinearLayout.LayoutParams(
                FILL_PARENT, WRAP_CONTENT, 0.0f);
        root.addView(buttons, params);

        buttons.setOrientation(LinearLayout.HORIZONTAL);
        Button cancel = new Button(activity);
        params = new LinearLayout.LayoutParams(
                0, WRAP_CONTENT, 1.0f);
        buttons.addView(cancel, params);

        cancel.setText(android.R.string.cancel);
        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Activity a = getActivity();
                if (a instanceof EventListener) {
                    EventListener listener = (EventListener) a;
                    listener.onCancel(FileSelectFragment.this);
                }
            }
        });

        Button ok = new Button(activity);
        params = new LinearLayout.LayoutParams(
                0, WRAP_CONTENT, 1.0f);
        buttons.addView(ok, params);

        ok.setText(android.R.string.ok);
        ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Activity a = getActivity();
                if (a instanceof EventListener) {
                    EventListener listener = (EventListener) a;
                    String path = mFilePathView.getText().toString();
                    listener.onOk(FileSelectFragment.this, path);
                }
            }
        });

        return root;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBundle(KEY_ARGUMENTS, getArguments());
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);

        int strRes;
        if (mStrRes == null) {
            strRes = 0;
        } else {
            strRes = mStrRes.makeDirMenu;
        }
        mMakeDirMenu = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, strRes);
        MenuItemCompat.setShowAsAction(mMakeDirMenu,
                MenuItemCompat.SHOW_AS_ACTION_IF_ROOM | MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
        if (mCurrentDir != null) {
            File f = new File(mCurrentDir);
            mMakeDirMenu.setEnabled(f.canWrite());
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item == mMakeDirMenu) {
            int title;
            int hint;
            if (mStrRes == null) {
                title = 0;
                hint = 0;
            } else {
                title = mStrRes.makeDirTitle;
                hint = mStrRes.makeDirHint;
            }
            Resources res = getResources();
            EditTextDialogFragment fragment = EditTextDialogFragment.newInstance(
                    res.getString(title), res.getString(hint), null, true,
                    0, 0, getId(), null);
            fragment.show(getFragmentManager(), "dialog");
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onEditTextDialogFragmentResult(EditTextDialogFragment fragment, String text) {
        // 余分なスペース削除
        String name = text.replaceAll("^[\\s　]*", "").replaceAll("[\\s　]*$", "");

        File newDir = new File(mCurrentDir, name);
        if (mStrRes != null) {
            if (newDir.mkdir()) {
                Toast.makeText(mContext, mStrRes.makeDirSucceed, Toast.LENGTH_SHORT).show();
                DirPagerAdapter adapter = (DirPagerAdapter) mDirPager.getAdapter();
                adapter.updatePath(newDir.getAbsolutePath());
            } else {
                Toast.makeText(mContext, mStrRes.makeDirFail, Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onEditTextDialogFragmentCancel(EditTextDialogFragment fragment) {
    }

    void onChangeDirectory(String dir) {
        mCurrentDir = dir;

        mFilePathView.setText(dir);

        if (mMakeDirMenu != null) {
            File f = new File(dir);
            mMakeDirMenu.setEnabled(f.canWrite());
        }

        Activity a = getActivity();
        if (a instanceof EventListener) {
            ((EventListener) a).onChangeDirectory(FileSelectFragment.this, dir);
        }
    }

    void changeDirectoryByFilePathView() {
        DirPagerAdapter adapter = (DirPagerAdapter) mDirPager.getAdapter();
        String filePath = mFilePathView.getText().toString();
        File f = new File(filePath);
        if (!f.isDirectory()) {
            f = f.getParentFile();
        }
        if (f != null) {
            if (f.canRead()) {
                adapter.updatePath(f.getAbsolutePath());
            } else {
                // XXX
                Toast.makeText(mContext, "Permission denied", Toast.LENGTH_LONG).show();
            }
        }
    }

    public void setDirectory(String dir) {
        mFilePathView.setText(dir);
        changeDirectoryByFilePathView();
    }

    static String convertToPathString(ArrayList<String> path, int level) {
        int size = path.size();
        if (size == 0) {
            return "/";
        }
        StringBuilder builder = new StringBuilder(path.get(0));
        for (int i = 1; i <= level; ++i) {
            builder.append('/').append(path.get(i));
        }
        String ret = builder.toString();
        if (TextUtils.isEmpty(ret)) {
            ret = "/";
        }
        return ret;
    }

    static ArrayList<String> convertToListString(String path) {
        ArrayList<String> list = new ArrayList<String>(
                Arrays.asList(path.split(File.separator)));
        Iterator<String> ite = list.iterator();
        if (ite.hasNext()) {
            // rootディレクトリは空文字のまま飛ばす
            ite.next();
        }
        while (ite.hasNext()) {
            String s = ite.next();
            if (TextUtils.isEmpty(s)) {
                ite.remove();
            }
        }
        return list;
    }

    private static class DirPagerAdapter extends PagerAdapter {
        private WeakReference<FileSelectFragment> mFragment;
        private WeakReference<ViewPager> mViewPager;
        private ArrayList<String> mDirPath;
        private boolean mDirOnly;

        public DirPagerAdapter(FileSelectFragment fragment, ViewPager pager,
                boolean dirOnly, String path) {
            mFragment = new WeakReference<FileSelectFragment>(fragment);
            mViewPager = new WeakReference<ViewPager>(pager);
            mDirOnly = dirOnly;
            updatePath(path);
        }

        public void updatePath(String path) {
            ArrayList<String> list = convertToListString(path);
            if (!new File(path).isDirectory()) {
                list.remove(list.size() - 1);
            }
            boolean updateList = false;
            if (mDirPath == null) {
                updateList = true;
            } else {
                int size = list.size();
                if (size <= mDirPath.size()) {
                    for (int i = 0; i < size; ++i) {
                        if (!list.get(i).equals(mDirPath.get(i))) {
                            updateList = true;
                            break;
                        }
                    }
                } else {
                    updateList = true;
                }
            }
            if (updateList) {
                mDirPath = list;
            }
            notifyDataSetChanged();
            ViewPager pager = mViewPager.get();
            if (pager != null) {
                pager.setCurrentItem(list.size() - 1, true);
            }

            FileSelectFragment fragment = mFragment.get();
            if (fragment != null) {
                fragment.onChangeDirectory(path);
            }
        }

        @Override
        public int getCount() {
            if (mDirPath == null) {
                return 0;
            } else {
                return mDirPath.size();
            }
        }

        @Override
        public void startUpdate(View container) {
            // TODO
        }

        @Override
        public Object instantiateItem(View container, int position) {
            ListView list = new ListView(container.getContext());
            String path = convertToPathString(mDirPath, position);
            DirListAdapter adapter = new DirListAdapter(
                    new File(path), mDirOnly);
            list.setAdapter(adapter);
            list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    DirListAdapter adapter = (DirListAdapter) parent.getAdapter();
                    File file = (File) adapter.getItem(position);
                    if (file.isDirectory() && !file.canRead()) {
                        // XXX
                        Toast.makeText(parent.getContext(), "Permission denied", Toast.LENGTH_LONG).show();
                    } else {
                        updatePath(file.getAbsolutePath());
                    }
                }
            });

            if (container instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) container;
                ViewGroup.LayoutParams params =
                    new ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT);
                vg.addView(list, 0, params);
            }

            list.setTag(path);
            return path;
        }

        @Override
        public void destroyItem(View container, int position, Object object) {
            View v = container.findViewWithTag(object);
            if (v != null) {
                if (container instanceof ViewGroup) {
                    ViewGroup vg = (ViewGroup) container;
                    vg.removeView(v);;
                }
            }
            // TODO 自動生成されたメソッド・スタブ

        }

        @Override
        public void finishUpdate(View container) {
            // TODO 自動生成されたメソッド・スタブ

        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view.findViewWithTag(object) != null;
        }

        @Override
        public Parcelable saveState() {
            // TODO 自動生成されたメソッド・スタブ
            return null;
        }

        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
            // TODO 自動生成されたメソッド・スタブ

        }

        @Override
        public int getItemPosition(Object object) {
            String path = (String) object;
            ArrayList<String> list = convertToListString(path);
            int size = list.size();
            if (mDirPath == null) {
                return POSITION_NONE;
            } else {
                if (size <= mDirPath.size()) {
                    for (int i = 0; i < size; ++i) {
                        if (!list.get(i).equals(mDirPath.get(i))) {
                            return POSITION_NONE;
                        }
                    }
                    return POSITION_UNCHANGED;
                } else {
                    return size - 1;
                }
            }

        }
    }

    private static class DirListAdapter extends BaseAdapter {
        private static final String CURRENT_DIR = ".";
        private static final String PARENT_DIR = "..";

        private File mCurrent;
        private File mParent;
        private ArrayList<File> mFiles;

        private boolean mDirOnly;

        public DirListAdapter(File path, boolean dirOnly) {
            mCurrent = path;
            mParent = path.getParentFile();
            mDirOnly = dirOnly;
            updateFiles();
        }

        @Override
        public int getCount() {
            return mFiles.size();
        }

        @Override
        public Object getItem(int position) {
            return mFiles.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view;
            if (convertView == null) {
                LayoutInflater inflater = LayoutInflater.from(parent.getContext());
                view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            } else {
                view = convertView;
            }
            String name;
            File file = mFiles.get(position);
            if (file == mCurrent) {
                name = CURRENT_DIR;
            } else if (mParent != null && file == mParent) {
                name = PARENT_DIR;
            } else {
                name = file.getName();
            }
            TextView tv = (TextView) view;
            tv.setText(name);
            return view;
        }

        public void updateFiles() {
            File[] files;
            if (mDirOnly) {
                files = mCurrent.listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        return pathname.isDirectory();
                    }
                });
            } else {
                files = mCurrent.listFiles();
            }
            mFiles = new ArrayList<File>(1);
            mFiles.add(mCurrent);
            if (mParent != null) {
                mFiles.add(mParent);
            }
            if (files != null) {
                List<File> list = Arrays.asList(files);
                Collections.sort(list, new Comparator<File>() {
                    @Override
                    public int compare(File lhs, File rhs) {
                        return lhs.getName().compareTo(rhs.getName());
                    }
                });
                mFiles.addAll(list);
            }

            notifyDataSetChanged();
        }

    }
}
