package jp.sourceforge.nicoro;

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

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.text.TextUtils;
import android.util.Xml;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LivePlayerStatusLoader extends HttpXmlLoader {
    private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;
    private static final boolean DEBUG_LOGD_PARSE = Release.IS_DEBUG && true;

    private static final String PATTERN_CONTENTS = "(rtmp://.+),(.+)";

    private String mLvNumber;
    private String mCookieUserSession;

    private CallbackMessage<Void, String> mCallbackMessage;

    // XML解析データ
    private static class Rtmp {
        public int is_fms;
        public String url;
        public String ticket;
    }
    private Rtmp mRtmp;
    private static class Contents {
        public String id;
        public int disableAudio;
        public int disableVideo;
        public int start_time;
        public String text;

        public String parseUrl;
        public String parsePlaypath;
//        public String akamaiUser;
//        public String akamaiPassword;
    }
    private static class Que {
        public int vpos;
        public String mail;
        public String name;
        public String text;

        public String publishPlaypath;
        public String publishContent;
    }
    private static class Stream {
        public String watch_count;
        public String title;
        public String description;
        public String comment_count;
        public String aspect;
        public long base_time;
        public long open_time;
        public long end_time;
        public long start_time;
        public int archive;
        public ArrayList<Contents> contents_list;
        public ArrayList<Que> quesheet;
    }
    private Stream mStream;
    private static class TicketsStream {
        public String name;
        public String text;
    }
    private ArrayList<TicketsStream> mTickets;
    private static class User {
        public String room_label;
        public String room_seetno;
        public String user_id;
    }
    private User mUser;
    private static class Ms {
        public String addr;
        public String port;
        public String thread;
    }
    private Ms mMs;
    private long mTime;

    public LivePlayerStatusLoader(String lvNumber, String cookieUserSession) {
        mLvNumber = lvNumber;
        mCookieUserSession = cookieUserSession;
    }

    public void registerCallback(CallbackMessage<Void, String> callback) {
        mCallbackMessage = callback;
    }

    public String getRtmpUrl() {
        if (mRtmp == null) {
            return null;
        }
        return mRtmp.url;
    }
    public String getRtmpTicket() {
        if (mRtmp == null) {
            return null;
        }
        return mRtmp.ticket;
    }

    public String getContentsUrl() {
        if (mStream == null) {
            return null;
        }
        ArrayList<Contents> contentsList = mStream.contents_list;
        if (contentsList == null || contentsList.size() == 0) {
            return null;
        }
        // TODO 最初の１個で決め打ち
        return contentsList.get(0).parseUrl;
    }
    public String getContentsPlaypath() {
        if (mStream == null) {
            return null;
        }
        ArrayList<Contents> contentsList = mStream.contents_list;
        if (contentsList == null || contentsList.size() == 0) {
            return null;
        }
        // TODO 最初の１個で決め打ち
        String playpath = contentsList.get(0).parsePlaypath;
        if (mTickets == null) {
            return playpath;
        } else {
            for (TicketsStream stream : mTickets) {
                if (TextUtils.equals(stream.name, playpath)) {
                    return playpath + "?" + stream.text;
                }
            }
        }
        return null;
    }
//    public String getAkamaiUser() {
//        if (mContentsList == null || mContentsList.size() == 0) {
//            return null;
//        }
//        // TODO 最初の１個で決め打ち
//        return mContentsList.get(0).akamaiUser;
//    }
//    public String getAkamaiPassword() {
//        if (mContentsList == null || mContentsList.size() == 0) {
//            return null;
//        }
//        // TODO 最初の１個で決め打ち
//        return mContentsList.get(0).akamaiPassword;
//    }
//    public String getQuePlay() {
//        if (mQuesheet == null || mQuesheet.size() == 0) {
//            return null;
//        }
//        Matcher matcher = Pattern.compile("/play\\s+rtmp:(.+)\\s").matcher("");
//        for (Que que : mQuesheet) {
//            matcher.reset(que.text);
//            if (matcher.find()) {
//                return matcher.group(1);
//            }
//        }
//        return null;
//    }
    public String getQueContent() {
        if (mStream == null) {
            return null;
        }
        ArrayList<Que> quesheet = mStream.quesheet;
        if (quesheet == null || quesheet.size() == 0) {
            return null;
        }

        // TODO 最初の１個で決め打ち
        for (Que que : quesheet) {
            if (que.publishContent != null) {
//                if (que.publishContent.endsWith(".f4v")) {
                    return "mp4:" + que.publishContent;
//                } else {
//                    return que.publishContent;
//                }
            }
        }
        return null;
    }
    public String getQuePlaypath() {
        if (mStream == null) {
            return null;
        }
        ArrayList<Que> quesheet = mStream.quesheet;
        if (quesheet == null || quesheet.size() == 0) {
            return null;
        }

        // TODO 最初の１個で決め打ち
        for (Que que : quesheet) {
            if (que.publishPlaypath != null) {
                return que.publishPlaypath;
            }
        }
        return null;
    }
    public int getQueVpos() {
        if (mStream == null) {
            return 0;
        }
        ArrayList<Que> quesheet = mStream.quesheet;
        if (quesheet == null || quesheet.size() == 0) {
            return 0;
        }

        // TODO 最初の１個で決め打ち
        for (Que que : quesheet) {
            if (que.publishPlaypath != null) {
                return que.vpos;
            }
        }
        return 0;
    }

    public String getMsAddr() {
        if (mMs == null) {
            return null;
        }
        return mMs.addr;
    }
    public String getMsPort() {
        if (mMs == null) {
            return null;
        }
        return mMs.port;
    }
    public String getMsThread() {
        if (mMs == null) {
            return null;
        }
        return mMs.thread;
    }

    public String getRoomLabel() {
        if (mUser == null) {
            return null;
        }
        return mUser.room_label;
    }
    public String getRoomSeetNo() {
        if (mUser == null) {
            return null;
        }
        return mUser.room_seetno;
    }
    public String getUserId() {
        if (mUser == null) {
            return null;
        }
        return mUser.user_id;
    }

    public String getTitle() {
        if (mStream == null) {
            return null;
        }
        return mStream.title;
    }
    public String getDescription() {
        if (mStream == null) {
            return null;
        }
        return mStream.description;
    }
    public String getWatchCount() {
        if (mStream == null) {
            return null;
        }
        return mStream.watch_count;
    }
    public String getCommentCount() {
        if (mStream == null) {
            return null;
        }
        return mStream.comment_count;
    }
    public int getArchive() {
        if (mStream == null) {
            return 0;
        }
        return mStream.archive;
    }

    public long getTime() {
        return mTime;
    }
    public long getStartTime() {
        if (mStream == null) {
            return 0;
        }
        return mStream.start_time;
    }

    @Override
    protected HttpUriRequest createRequest() {
        HttpGet httpRequest = new HttpGet(
                "http://live.nicovideo.jp/api/getplayerstatus?v=" + mLvNumber);
        httpRequest.addHeader("Cookie", mCookieUserSession);
        return httpRequest;
    }

    @Override
    protected boolean createDataFromXml(String xmlBody) {
        XmlPullParser pullParser = Xml.newPullParser();
        try {
            pullParser.setInput(new StringReader(xmlBody));

            int next;
            String name = null;
            boolean rtmp = false;
            boolean contentsList = false;
            Contents contents = null;
            boolean tickets = false;
            TicketsStream ticketsStream = null;
            boolean quesheet = false;
            Que que = null;
            boolean user = false;
            boolean ms = false;
            boolean stream = false;
            while ((next = pullParser.next()) != XmlPullParser.END_DOCUMENT) {
                if (DEBUG_LOGD_PARSE) {
                    Log.d(LOG_TAG, Log.buf().append("next=").append(next).toString());
                }
                switch (next) {
                case XmlPullParser.START_TAG:
                    name = pullParser.getName();
                    if (DEBUG_LOGD_PARSE) {
                        Log.d(LOG_TAG, Log.buf().append("<").append(name)
                                .append(">").toString());
                    }
                    if (stream) {
                        assert mStream != null;
                        if (contentsList) {
                            if ("contents".equals(name)) {
                                if (contents != null) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, "contents duplicate nest");
                                    }
                                    return false;
                                }
                                contents = new Contents();
                                contents.id = pullParser.getAttributeValue(null, "id");
                                try {
                                    contents.disableAudio = Integer.parseInt(
                                            pullParser.getAttributeValue(null, "disableAudio"));
                                } catch (NumberFormatException e) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, e.toString(), e);
                                    }
                                }
                                try {
                                    contents.disableVideo = Integer.parseInt(
                                            pullParser.getAttributeValue(null, "disableVideo"));
                                } catch (NumberFormatException e) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, e.toString(), e);
                                    }
                                }
                                try {
                                    contents.start_time = Integer.parseInt(
                                            pullParser.getAttributeValue(null, "start_time"));
                                } catch (NumberFormatException e) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, e.toString(), e);
                                    }
                                }
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, Log.buf().append("id=").append(contents.id)
                                            .append(" disableAudio=").append(contents.disableAudio)
                                            .append(" disableVideo=").append(contents.disableVideo)
                                            .append(" start_time=").append(contents.start_time)
                                            .toString());
                                }
                            } else {
                                // その他のタグはなさそうなので無視
                            }
                        } else if (quesheet) {
                            if ("que".equals(name)) {
                                if (que != null) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, "quesheet/que duplicate nest");
                                    }
                                    return false;
                                }
                                que = new Que();
                                try {
                                    que.vpos = Integer.parseInt(pullParser.getAttributeValue(null, "vpos"));
                                } catch (NumberFormatException e) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, e.toString(), e);
                                    }
                                }
                                que.mail = pullParser.getAttributeValue(null, "mail");
                                que.name = pullParser.getAttributeValue(null, "name");
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, Log.buf().append("vpos=").append(que.vpos)
                                            .append(" mail=").append(que.mail)
                                            .append(" name=").append(que.name)
                                            .toString());
                                }
                            } else {
                                // その他のタグはなさそうなので無視
                            }
                        } else {
                            if ("contents_list".equals(name)) {
                                if (mStream.contents_list != null) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, "contents_list duplicate");
                                    }
                                    return false;
                                }
                                mStream.contents_list = new ArrayList<Contents>();
                                contentsList = true;
                            } else if ("quesheet".equals(name)) {
                                if (mStream.quesheet != null) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, "quesheet duplicate");
                                    }
                                    return false;
                                }
                                mStream.quesheet = new ArrayList<Que>();
                                quesheet = true;
                            } else {
                                // その他のタグはとりあえず無視
                            }
                        }
                    } else if (tickets) {
                        if ("stream".equals(name)) {
                            if (ticketsStream != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "tickets/stream duplicate nest");
                                }
                                return false;
                            }
                            ticketsStream = new TicketsStream();
                            ticketsStream.name = pullParser.getAttributeValue(null, "name");
                            if (DEBUG_LOGD_PARSE) {
                                Log.d(LOG_TAG, Log.buf().append("name=").append(ticketsStream.name)
                                        .toString());
                            }
                        } else {
                            // その他のタグはなさそうなので無視
                        }
                    } else {
                        if ("getplayerstatus".equals(name)) {
                            try {
                                mTime = Long.parseLong(
                                        pullParser.getAttributeValue(null, "time"));
                            } catch (NumberFormatException e) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, e.toString(), e);
                                }
                            }
                        } else if ("stream".equals(name)) {
                            if (mStream != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "stream duplicate");
                                }
                                return false;
                            }
                            mStream = new Stream();
                            stream = true;
                        } else if ("rtmp".equals(name)) {
                            if (mRtmp != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "rtmp duplicate");
                                }
                                return false;
                            }
                            mRtmp = new Rtmp();
                            String is_fms = pullParser.getAttributeValue(null, "is_fms");
                            if (is_fms != null) {
                                try {
                                    mRtmp.is_fms = Integer.parseInt(is_fms);
                                } catch (NumberFormatException e) {
                                    if (DEBUG_LOGD_PARSE) {
                                        Log.d(LOG_TAG, e.toString(), e);
                                    }
                                }
                            }
                            rtmp = true;
                        } else if ("tickets".equals(name)) {
                            if (mTickets != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "tickets duplicate");
                                }
                                return false;
                            }
                            mTickets = new ArrayList<TicketsStream>();
                            tickets = true;
                        } else if ("user".equals(name)) {
                            if (mUser != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "user duplicate");
                                }
                                return false;
                            }
                            mUser = new User();
                            user = true;
                        } else if ("ms".equals(name)) {
                            if (mMs != null) {
                                if (DEBUG_LOGD_PARSE) {
                                    Log.d(LOG_TAG, "user duplicate");
                                }
                                return false;
                            }
                            mMs = new Ms();
                            ms = true;
                        } else {
                            // その他のタグはとりあえず無視
                        }
                    }
                    break;
                case XmlPullParser.TEXT:
                    if (name != null) {
                        String text = pullParser.getText();
                        if (DEBUG_LOGD_PARSE) {
                            Log.d(LOG_TAG, text);
                        }
                        try {
                            if (stream) {
                                assert mStream != null;
                                if (contentsList) {
                                    assert mStream.contents_list != null;
                                    if ("contents".equals(name)) {
                                        assert contents != null;
                                        contents.text = text;

                                        boolean isAkamai = text.startsWith("akamai:");
                                        Matcher matcher = Pattern.compile(PATTERN_CONTENTS
                                                ).matcher(text);
                                        if (matcher.find()) {
                                            contents.parseUrl = matcher.group(1);
                                            // TODO akamaiのuser名とpassword？
                                            contents.parsePlaypath = matcher.group(2);
    //                                        if (isAkamai) {
    //                                            Matcher matcherAkamai = Pattern.compile("(.+)@(.+)"
    //                                                    ).matcher(contents.parseStream);
    //                                            if (matcherAkamai.find()) {
    //                                                contents.akamaiUser = matcherAkamai.group(1);
    //                                                contents.akamaiPassword = matcherAkamai.group(2);
    //                                            }
    //                                        }
                                        }
                                    } else {
                                        // その他のタグはなさそうなので無視
                                    }
                                } else if (quesheet) {
                                    assert mStream.quesheet != null;
                                    if ("que".equals(name)) {
                                        assert que != null;
                                        que.text = text;

                                        Matcher matcher = Pattern.compile("/publish\\s+(.+)\\s+(.+)").matcher(text);
                                        if (matcher.find()) {
                                            que.publishPlaypath = matcher.group(1);
                                            que.publishContent = matcher.group(2);
                                        }

                                    } else {
                                        // その他のタグはなさそうなので無視
                                    }
                                } else {
                                    if ("watch_count".equals(name)) {
                                        mStream.watch_count = text;
                                    } else if ("title".equals(name)) {
                                        mStream.title = text;
                                    } else if ("description".equals(name)) {
                                        mStream.description = text;
                                    } else if ("comment_count".equals(name)) {
                                        mStream.comment_count = text;
                                    } else if ("aspect".equals(name)) {
                                        mStream.aspect = text;
                                    } else if ("base_time".equals(name)) {
                                        mStream.base_time = Long.parseLong(text);
                                    } else if ("open_time".equals(name)) {
                                        mStream.open_time = Long.parseLong(text);
                                    } else if ("end_time".equals(name)) {
                                        mStream.end_time = Long.parseLong(text);
                                    } else if ("start_time".equals(name)) {
                                        mStream.start_time = Long.parseLong(text);
                                    } else if ("archive".equals(name)) {
                                        mStream.archive = Integer.parseInt(text);
                                    } else {
                                        // その他のタグはとりあえず無視
                                    }
                                }
                            } else if (rtmp) {
                                assert mRtmp != null;
                                if ("url".equals(name)) {
                                    mRtmp.url = text;
                                } else if ("ticket".equals(name)) {
                                    mRtmp.ticket = text;
                                } else {
                                    // その他のタグはとりあえず無視
                                }
                            } else if (tickets) {
                                assert mTickets != null;
                                if ("stream".equals(name)) {
                                    assert ticketsStream != null;
                                    ticketsStream.text = text;
                                } else {
                                    // その他のタグはなさそうなので無視
                                }
                            } else if (user) {
                                assert mUser != null;
                                if ("room_label".equals(name)) {
                                    mUser.room_label = text;
                                } else if ("room_seetno".equals(name)) {
                                    mUser.room_seetno = text;
                                } else if ("user_id".equals(name)) {
                                    mUser.user_id = text;
                                } else {
                                    // その他のタグはとりあえず無視
                                }
                            } else if (ms) {
                                assert mMs != null;
                                if ("addr".equals(name)) {
                                    mMs.addr = text;
                                } else if ("port".equals(name)) {
                                    mMs.port = text;
                                } else if ("thread".equals(name)) {
                                    mMs.thread = text;
                                } else {
                                    // その他のタグはなさそうなので無視
                                }
                            } else {
                                // その他のタグはとりあえず無視
                            }
                        } catch (NumberFormatException e) {
                            Log.d(LOG_TAG, Log.buf().append("parseInt failed:")
                                    .append(text).toString());
                            // できる限り続行
                        }
                    }
                    break;
                case XmlPullParser.END_TAG:
                    name = pullParser.getName();
                    if (DEBUG_LOGD_PARSE) {
                        Log.d(LOG_TAG, Log.buf().append("</").append(name)
                                .append(">").toString());
                    }
                    if (stream) {
                        assert mStream != null;
                        if (contentsList) {
                            assert mStream.contents_list != null;
                            if ("contents".equals(name)) {
                                assert contents != null;
                                mStream.contents_list.add(contents);
                                contents = null;
                            } else if ("contents_list".equals(name)) {
                                contentsList = false;
                            } else {
                                // その他のタグはなさそうなので無視
                            }
                        } else if (quesheet) {
                            assert mStream.quesheet != null;
                            if ("que".equals(name)) {
                                assert que != null;
                                mStream.quesheet.add(que);
                                que = null;
                            } else if ("quesheet".equals(name)) {
                                quesheet = false;
                            } else {
                                // その他のタグはなさそうなので無視
                            }
                        } else {
                            if ("stream".equals(name)) {
                                stream = false;
                            } else {
                                // その他のタグはとりあえず無視
                            }
                        }
                    } else if (tickets) {
                        assert mTickets != null;
                        if ("stream".equals(name)) {
                            assert ticketsStream != null;
                            mTickets.add(ticketsStream);
                            ticketsStream = null;
                        } else if ("tickets".equals(name)) {
                            tickets = false;
                        } else {
                            // その他のタグはなさそうなので無視
                        }
                    } else if (user) {
                        assert mUser != null;
                        if ("user".equals(name)) {
                            user = false;
                        } else {
                            // その他のタグはとりあえず無視
                        }
                    } else if (ms) {
                        assert mMs != null;
                        if ("ms".equals(name)) {
                            ms = false;
                        } else {
                            // その他のタグはなさそうなので無視
                        }
                    } else {
                        if ("rtmp".equals(name)) {
                            assert mRtmp != null;
                            rtmp = false;
                        } else {
                            // その他のタグはとりあえず無視
                        }
                    }
                    name = null;
                    break;
                default:
                    break;
                }
            }
        } catch (XmlPullParserException e) {
            Log.d(LOG_TAG, e.toString(), e);
        } catch (IOException e) {
            Log.d(LOG_TAG, e.toString(), e);
        }

        if (DEBUG_LOGD) {
            StringBuilder buf = Log.buf().append("LivePlayerStatus:");
            if (mRtmp == null) {
                buf.append(" rtmp=(null)");
            } else {
                buf.append(" rtmp url=").append(mRtmp.url)
                    .append(" rtmp ticket=").append(mRtmp.ticket);
            }
            Log.d(LOG_TAG, buf.toString());
        }

        return true;
    }

    @Override
    protected void dispatchOnFinished() {
        mCallbackMessage.sendMessageSuccess(null);
    }

    @Override
    protected void dispatchOnOccurredError(String errorMessage) {
        mCallbackMessage.sendMessageError(errorMessage);
    }

    @Override
    protected boolean readAndCreateData(InputStream inDownload) throws IOException {
        String xmlBody = readEntityAndDecode(inDownload);
        return createDataFromXml(xmlBody);
    }

}
