package jp.sourceforge.nicoro;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import jp.sourceforge.nicoro.StaticRes.integer;

public class Playlist implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 3439582683821783964L;

    /** 動画遷移に成功 */
    public static final int MOVE_TRACK_SUCCEEDED = 0;
    /** 動画遷移に失敗 */
    public static final int MOVE_TRACK_FAILED = 1;
    /** 動画遷移先が削除済みだったので飛ばして次の動画に遷移 */
    public static final int MOVE_TRACK_SKIP_DELETED = 2;

    public static class Item implements Serializable {
        /**
         *
         */
        private static final long serialVersionUID = -8865677571593748687L;

        public int mylistCounter;
        public int viewCounter;
        public long itemUpdateTime;
        public long firstRetrieve;
        public int lengthSeconds;
        public int deleted;
        public String thumbnailUrl;
        public String title;
        public String description;
        public long createTime;
        public String watchId;
        public String videoId;
        public String mylistComment;
        public int numRes;
        public String threadId;

        public boolean isDeleted() {
            // deleted の値のとりあえず内訳：0: 正常、1: アップロード者削除、3: 権利者削除、8: 非表示
            return deleted != 0;
        }
    }

    private String mPlaylistName;
    private ArrayList<Item> mItems;
    private int mSortOrder;
    private boolean mIsShuffle;

    private int mCurrent = 0;

    private Random mRandom = new Random();

    private static ArrayList<Comparator<Playlist.Item>> mMylistComparators;

    /**
     * Serializable向けのデフォルトコンストラクタ
     */
    Playlist() {
    }

    public Playlist(String stringJson) throws JSONException {
        this(new JSONObject(stringJson));
    }

    public Playlist(JSONObject json) {
        // 必要なものだけ読み出す
        JSONObject playlistJson = json.optJSONObject("playlist");
        if (playlistJson != null) {
            mPlaylistName = playlistJson.optString("name");
        }

        JSONArray itemsJson = json.optJSONArray("items");
        if (itemsJson == null) {
            mItems = new ArrayList<Playlist.Item>();
        } else {
            int len = itemsJson.length();
            mItems = new ArrayList<Playlist.Item>(len);
            for (int i = 0; i < len; ++i) {
                JSONObject itemJson = itemsJson.optJSONObject(i);
                if (itemJson != null) {
                    Item item = new Item();
                    mItems.add(item);
                    item.mylistCounter = itemJson.optInt("mylist_counter", -1);
                    item.viewCounter = itemJson.optInt("view_counter", -1);
                    item.itemUpdateTime = itemJson.optLong("item_update_time", 0);
                    item.firstRetrieve = itemJson.optLong("first_retrieve", 0);
                    item.lengthSeconds = itemJson.optInt("length_seconds", 0);
                    item.deleted = itemJson.optInt("deleted", 0);
                    item.thumbnailUrl = itemJson.optString("thumbnail_url");
                    item.title = itemJson.optString("title");
                    item.description = itemJson.optString("description");
                    item.createTime = itemJson.optLong("create_time", 0);
                    item.watchId = itemJson.optString("watch_id");
                    item.videoId = itemJson.optString("video_id");
                    item.mylistComment = itemJson.optString("mylist_comment");
                    item.numRes = itemJson.optInt("num_res", -1);
                    item.threadId = itemJson.optString("thread_id");
                }
            }
        }
    }

    public Playlist(Playlist src) {
        mPlaylistName = src.mPlaylistName;
        mItems = new ArrayList<Playlist.Item>(src.mItems);
        // コピー元でソート済みと見なす
        mSortOrder = src.mSortOrder;
        mIsShuffle = src.mIsShuffle;
        mCurrent = src.mCurrent;
    }

    private void initComparators() {
        if (mMylistComparators == null) {
            setMylistComparators();
        }
    }

    /**
     * ソート順を設定
     * @param sortOrder
     */
    public void setSortOrder(int sortOrder) {
        mSortOrder = sortOrder;

        if (sortOrder == integer.mylist_sort_dummy) {
            // 何もしない
        } else {
            initComparators();
            mIsShuffle = false;
            if (sortOrder >= 0 && sortOrder < mMylistComparators.size()) {
                Item currentItem = getCurrentItem();
                if (currentItem != null) {
                    Collections.sort(mItems, mMylistComparators.get(sortOrder));
                    mCurrent = mItems.indexOf(currentItem);
                }
            } else {
                // TODO エラー表示
            }
        }
    }
    /**
     * ソート順の種別を取得
     * @return
     */
    public int getSortOrder() {
        return mSortOrder;
    }

    public void shuffle() {
        mIsShuffle = true;

        Item currentItem = getCurrentItem();
        if (currentItem != null) {
            Collections.shuffle(mItems, mRandom);
            mCurrent = mItems.indexOf(currentItem);
        }
    }

    public boolean isShuffle() {
        return mIsShuffle;
    }

    public Item getCurrentItem() {
        int current = mCurrent;
        if (current >= 0 && current < mItems.size()) {
            return mItems.get(current);
        } else {
            return null;
        }
    }

    public int getItemsSize() {
        return mItems.size();
    }

    public int setCurrentTrack(int current) {
        int size = mItems.size();
        if (current >= 0 && current < size) {
            Item item = mItems.get(current);
            if (!item.isDeleted()) {
                mCurrent = current;
                return MOVE_TRACK_SUCCEEDED;
            } else {
                // 削除済みの場合は次の動画を探す
                ++current;
                while (current < size) {
                    item = mItems.get(current);
                    if (!item.isDeleted()) {
                        mCurrent = current;
                        return MOVE_TRACK_SKIP_DELETED;
                    }
                    ++current;
                }
                return MOVE_TRACK_FAILED;
            }
        } else {
            return MOVE_TRACK_FAILED;
        }
    }
    public int getCurrentTrack() {
        return mCurrent;
    }

    /**
     * 次の動画に移動する
     * 削除済み動画は飛ばす
     * @return {@link #MOVE_TRACK_SUCCEEDED}, {@link #MOVE_TRACK_FAILED},
     * {@link #MOVE_TRACK_SKIP_DELETED}
     */
    public int moveNextTrack() {
        int current = mCurrent;
        ++current;
        int size = mItems.size();
        if (current < size) {
            Item item = mItems.get(current);
            if (!item.isDeleted()) {
                mCurrent = current;
                return MOVE_TRACK_SUCCEEDED;
            } else {
                // 削除済みの場合は次の動画を探す
                ++current;
                while (current < size) {
                    item = mItems.get(current);
                    if (!item.isDeleted()) {
                        mCurrent = current;
                        return MOVE_TRACK_SKIP_DELETED;
                    }
                    ++current;
                }
                return MOVE_TRACK_FAILED;
            }
        } else {
            return MOVE_TRACK_FAILED;
        }
    }

    /**
     * 前の動画に移動する
     * 削除済み動画は飛ばす
     * @return {@link #MOVE_TRACK_SUCCEEDED}, {@link #MOVE_TRACK_FAILED},
     * {@link #MOVE_TRACK_SKIP_DELETED}
     */
    public int movePreviousTrack() {
        int current = mCurrent;
        --current;
        if (current >= 0 && current < mItems.size()) {
            Item item = mItems.get(current);
            if (!item.isDeleted()) {
                mCurrent = current;
                return MOVE_TRACK_SUCCEEDED;
            } else {
                // 削除済みの場合は前の動画を探す
                --current;
                while (current >= 0) {
                    item = mItems.get(current);
                    if (!item.isDeleted()) {
                        mCurrent = current;
                        return MOVE_TRACK_SKIP_DELETED;
                    }
                    --current;
                }
                return MOVE_TRACK_FAILED;
            }
        } else {
            return MOVE_TRACK_FAILED;
        }
    }

    public String getName() {
        return mPlaylistName;
    }

    public Item getItem(int position) {
        if (position >= 0 && position < mItems.size()) {
            return mItems.get(position);
        } else {
            return null;
        }
    }

    private void setMylistComparators() {
        int sortSize = integer.mylist_sort_short_play_time + 1;
        mMylistComparators = new ArrayList<Comparator<Playlist.Item>>(sortSize);
        for (int i = 0; i < sortSize; ++i) {
            mMylistComparators.add(null);
        }
        mMylistComparators.set(integer.mylist_sort_new_create,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareOldCreate(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_old_create,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareOldCreate(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_title_ascending,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareTitleAscending(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_title_descending,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareTitleAscending(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_mylist_comment_ascending,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareMylistCommentAscending(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_mylist_comment_descending,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareMylistCommentAscending(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_new_upload,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareOldUpload(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_old_upload,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareOldUpload(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_many_views,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareFewViews(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_few_views,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareFewViews(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_new_comment,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareOldComment(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_old_comment,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareOldComment(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_many_comments,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareFewComments(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_few_comments,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareFewComments(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_many_my_lists,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareFewMylists(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_few_my_lists,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareFewMylists(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_long_play_time,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return -compareShortPlayTime(object1, object2);
            }
        });
        mMylistComparators.set(integer.mylist_sort_short_play_time,
                new Comparator<Playlist.Item>() {
            @Override
            public int compare(Playlist.Item object1, Playlist.Item object2) {
                return compareShortPlayTime(object1, object2);
            }
        });
    }

    static int compareOldCreate(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        long val1 = object1.createTime;
        long val2 = object2.createTime;
        if (val1 < val2) {
            return -1;
        } else if (val1 == val2) {
            return 0;
        } else {
            return 1;
        }
    }
    static int compareTitleAscending(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        String val1 = object1.title;
        String val2 = object2.title;
        return val1.compareTo(val2);
    }
    static int compareMylistCommentAscending(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        String val1 = object1.description;
        String val2 = object2.description;
        return val1.compareTo(val2);
    }
    static int compareOldUpload(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        long val1 = object1.firstRetrieve;
        long val2 = object2.firstRetrieve;
        if (val1 < val2) {
            return -1;
        } else if (val1 == val2) {
            return 0;
        } else {
            return 1;
        }
    }
    static int compareFewViews(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        int val1 = object1.viewCounter;
        int val2 = object2.viewCounter;
        return val1 - val2;
    }
    static int compareOldComment(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        // TODO コメントの古い・新しいがこのキーなのか自信ない
        // というかそもそもJSONデータの中にあるかどうかも不明
        long val1 = object1.itemUpdateTime;
        long val2 = object2.itemUpdateTime;
        if (val1 < val2) {
            return -1;
        } else if (val1 == val2) {
            return 0;
        } else {
            return 1;
        }
    }
    static int compareFewComments(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        int val1 = object1.numRes;
        int val2 = object2.numRes;
        return val1 - val2;
    }
    static int compareFewMylists(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        int val1 = object1.mylistCounter;
        int val2 = object2.mylistCounter;
        return val1 - val2;
    }
    static int compareShortPlayTime(Playlist.Item object1, Playlist.Item object2) {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            } else {
                return 1;
            }
        } else if (object2 == null) {
            return -1;
        }
        int val1 = object1.lengthSeconds;
        int val2 = object2.lengthSeconds;
        return val1 - val2;
    }
}
