package jp.sourceforge.nicoro;

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

import android.os.Parcel;
import android.os.Parcelable;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

/**
 * 動画ページ(watch)に含まれるJavaScriptのVideo変数
 */
public class WatchVideo implements Parcelable {
    private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

    private HashMap<String, String> mVideo;
    private String mUrl;

    private String v = null;
    private String id = null;
    private String title = null;
    private String description = null;
    private String thumbnail = null;
    private Date postedAt = null;
    private int length = -1;
    private int viewCount = -1;
    private int mylistCount = -1;

    private DateFormat mDateFormat = new SimpleDateFormat(
            "yyyy/MM/dd HH:mm:ss");

    public static class TagList {
        public boolean dic;
        public int id;
        public String tag;
    }
    private ArrayList<TagList> mTagList;

    /**
     * Videoを読み込んだMapから生成
     * @param video
     */
    public WatchVideo(Map<String, String> video) {
        mVideo = new HashMap<String, String>(video);
    }

    /**
     * ThumbInfoから擬似的に生成
     * @param thumbInfo
     */
    public WatchVideo(ThumbInfo thumbInfo) {
        mVideo = new HashMap<String, String>();
        mVideo.put("v", thumbInfo.getVideoNumber());
        mVideo.put("id", thumbInfo.getVideoNumber());
        mVideo.put("title", thumbInfo.getTitle());
        mVideo.put("description", thumbInfo.getDescription());
        mVideo.put("thumbnail", thumbInfo.getThumbnailUrl());
        mVideo.put("postedAt", mDateFormat.format(thumbInfo.getFirstRetrieveAsDate()));
        mVideo.put("length", thumbInfo.getLength());
        mVideo.put("viewCount", Integer.toString(thumbInfo.getViewCounter()));
        mVideo.put("mylistCount", Integer.toString(thumbInfo.getMylistCounter()));
    }

    public String v() {
        if (v == null) {
            v = v(mVideo);
        }
        return v;
    }
    public static String v(Map<String, String> video) {
        return video.get("v");
    }

    public String id() {
        if (id == null) {
            id = id(mVideo);
        }
        return id;
    }
    public static String id(Map<String, String> video) {
        return video.get("id");
    }

    public String title() {
        if (title == null) {
            title = title(mVideo);
        }
        return title;
    }
    public static String title(Map<String, String> video) {
        return video.get("title");
    }

    public String description() {
        if (description == null) {
            description = description(mVideo);
        }
        return description;
    }
    public static String description(Map<String, String> video) {
        return video.get("description");
    }

    public String thumbnail() {
        if (thumbnail == null) {
            thumbnail = thumbnail(mVideo);
        }
        return thumbnail;
    }
    public static String thumbnail(Map<String, String> video) {
        return video.get("thumbnail");
    }

    public Date postedAt() {
        if (postedAt == null) {
            try {
                postedAt = mDateFormat.parse(postedAt(mVideo));
            } catch (ParseException e) {
                Log.w(LOG_TAG, e.toString(), e);
            }
        }
        return postedAt;
    }
    public static String postedAt(Map<String, String> video) {
        return video.get("postedAt");
    }

    public int length() {
        if (length < 0) {
            length = length(mVideo);
        }
        return length;
    }
    public static int length(Map<String, String> video) {
        return Util.parseInt(video.get("length"), -1);
    }

    public int viewCount() {
        if (viewCount < 0) {
            viewCount = viewCount(mVideo);
        }
        return viewCount;
    }
    public static int viewCount(Map<String, String> video) {
        return Util.parseInt(video.get("viewCount"), -1);
    }

    public int mylistCount() {
        if (mylistCount < 0) {
            mylistCount = mylistCount(mVideo);
        }
        return mylistCount;
    }
    public static int mylistCount(Map<String, String> video) {
        return Util.parseInt(video.get("mylistCount"), -1);
    }

    /**
     * vまたはidと一致するか判定
     * @param videoNumber
     * @return
     */
    public boolean hasVideoNumber(String videoNumber) {
        if (videoNumber == null) {
            return false;
        }
        return videoNumber.equals(v()) || videoNumber.equals(id());
    }

    /**
     * 最低限の正しい値を持っているか確認
     * @return
     */
    public boolean isValid() {
        return (v() != null && id() != null);
    }

    @Override
    public String toString() {
        return "WatchVideo: " + mVideo.toString();
    }

    public void setUrl(String url) {
        mUrl = url;
    }
    public String getUrl() {
        return mUrl;
    }

    public ArrayList<TagList> getTagList() {
        return mTagList;
    }

    public static WatchVideo createFromVideoMap(Map<String, String> video) {
        return new WatchVideo(video);
    }

    public static WatchVideo createFromWatchHtml(String html) {
        try {
            Matcher matcher = Pattern.compile("var\\s+Video\\s*=\\s*(\\{.*\\})",
                    Pattern.DOTALL | Pattern.MULTILINE).matcher(html);
            if (matcher.find()) {
                JSONObject json = new JSONObject(matcher.group(1));
                HashMap<String, String> map = new HashMap<String, String>(json.length());
                Iterator<?> ite = json.keys();
                while (ite.hasNext()) {
                    String key = (String) ite.next();
                    String value = json.optString(key, null);
                    map.put(key, value);
                    if (DEBUG_LOGD) {
                        Log.d(LOG_TAG, Log.buf().append("Video.").append(key)
                                .append('=').append(value).toString());
                    }
                }
                return new WatchVideo(map);
            } else {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, "var Video is not found");
                }
            }
        } catch (JSONException e) {
            Log.w(LOG_TAG, e.toString(), e);
        }
        if (DEBUG_LOGD) {
            Log.d(LOG_TAG, "<<<createFromWatchHtml html start>>>");
            Log.dLines(LOG_TAG, html);
            Log.d(LOG_TAG, "<<<createFromWatchHtml html end>>>");
        }
        return null;
    }

    public static WatchVideo createFromWatchAPIDataContainer(JSONObject json) {
        JSONObject videoDetail = json.optJSONObject("videoDetail");
        if (videoDetail == null) {
            return null;
        }
        // TODO keyの組み合わせはVideoと合っているので、とりあえずmap経由で作成
        HashMap<String, String> map = new HashMap<String, String>();
        ArrayList<TagList> tagList = null;
        Iterator<?> it = videoDetail.keys();
        while (it.hasNext()) {
            String key = (String) it.next();
            if ("tagList".equals(key)) {
                JSONArray tagArray = videoDetail.optJSONArray(key);
                int length = tagArray.length();
                tagList = new ArrayList<TagList>(length);
                for (int i = 0; i < length; ++i) {
                    JSONObject tag = tagArray.optJSONObject(i);
                    if (tag != null) {
                        TagList item = new TagList();
                        item.dic = tag.optBoolean("dic");
                        item.id = tag.optInt("id");
                        item.tag = tag.optString("tag");
                        tagList.add(item);
                    }
                }
            } else {
                String val = videoDetail.optString(key, null);
                if (val != null) {
                    map.put(key, val);
                }
            }
        }
        WatchVideo ret = new WatchVideo(map);
        ret.mTagList = tagList;
        return ret;
    }

    /**
     * 動画番号だけから仮生成
     * @param videoNumber
     * @return
     */
    public static WatchVideo createTemp(String videoNumber) {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("v", videoNumber);
        map.put("id", videoNumber);
        return new WatchVideo(map);
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeSerializable(mVideo);
        dest.writeString(mUrl);
        int tagListSize = (mTagList == null ? 0 : mTagList.size());
        dest.writeInt(tagListSize);
        if (tagListSize != 0) {
            for (int i = 0; i < tagListSize; ++i) {
                TagList tag = mTagList.get(i);
                dest.writeInt(tag.dic ? 1 : 0);
                dest.writeInt(tag.id);
                dest.writeString(tag.tag);
            }
        }
    }

    public static final Parcelable.Creator<WatchVideo> CREATOR = new Creator<WatchVideo>() {
        @Override
        public WatchVideo createFromParcel(Parcel source) {
            return new WatchVideo(source);
        }

        @Override
        public WatchVideo[] newArray(int size) {
            return new WatchVideo[size];
        }
    };

    @SuppressWarnings("unchecked")
    WatchVideo(Parcel p) {
        mVideo = (HashMap<String, String>) p.readSerializable();
        mUrl = p.readString();
        int tagListSize = p.readInt();
        if (tagListSize != 0) {
            ArrayList<TagList> tagList = new ArrayList<TagList>(tagListSize);
            for (int i = 0; i < tagListSize; ++i) {
                TagList tag = new TagList();
                tag.dic = (p.readInt() != 0);
                tag.id = p.readInt();
                tag.tag = p.readString();
                tagList.add(tag);
            }
            mTagList = tagList;
        }
    }
}
