package com.xyoye.dandanplay.ui.weight.dialog;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;

import com.blankj.utilcode.util.ToastUtils;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.xyoye.dandanplay.R;
import com.xyoye.dandanplay.app.IApplication;
import com.xyoye.dandanplay.utils.AppConfig;
import com.xyoye.dandanplay.utils.Constants;
import com.xyoye.dandanplay.utils.DanmuUtils;

import org.jsoup.Connection;
import org.jsoup.Jsoup;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by xyoye on 2018/5/17.
 */

public class BiliBiliDownloadDialog extends Dialog {
    private final int DOWNLOAD_ONE = 1;
    private final int DOWNLOAD_LIST = 2;

    @BindView(R.id.log_et)
    EditText logEt;
    @BindView(R.id.file_name_et)
    EditText fileNameEt;
    @BindView(R.id.download_start_bt)
    Button downloadStartBt;
    @BindView(R.id.download_over_bt)
    Button downloadOverBt;
    @BindView(R.id.change_file_ll)
    LinearLayout fileNameLayout;

    private String keyWord;
    private String type;
    private Context context;
    private String fileName;

    private String cid;
    private String videoTitle;

    private List<String> cidList;
    private String animeTitle;

    private int downloadType;

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                //Log消息
                case 100:
                    logEt.append((String)msg.obj);
                    break;
                //获取cid成功
                case 101:
                    if (downloadType == DOWNLOAD_ONE){
                        fileNameEt.setEnabled(true);
                        fileNameEt.setText(videoTitle);
                    }
                    downloadStartBt.setVisibility(View.VISIBLE);
                    downloadStartBt.setEnabled(true);
                    downloadStartBt.setBackground(ContextCompat.getDrawable(context, R.drawable.btn_corner_blue));
                    downloadStartBt.setText("开始下载");
                    break;
                //初始化完成
                case 102:
                    downloadStartBt.setVisibility(View.GONE);
                    downloadOverBt.setVisibility(View.VISIBLE);
                    break;
                //开始下载
                case 103:
                    fileNameLayout.setVisibility(View.GONE);
                    downloadStartBt.setEnabled(false);
                    downloadStartBt.setBackground(null);
                    downloadStartBt.setText("正在下载…");
                    break;
                //下载完成
                case 104:
                    downloadStartBt.setVisibility(View.GONE);
                    downloadOverBt.setVisibility(View.VISIBLE);
                    downloadOverBt.setText("关闭");
                    break;
                //Toast消息
                case 105:
                    String toast = (String) msg.obj;
                    ToastUtils.showShort(toast);
                    break;
            }
            return false;
        }
    });

    public BiliBiliDownloadDialog(@NonNull Context context, int themeResId , String keyWord, String type) {
        super(context, themeResId);
        this.context = context;
        this.keyWord = keyWord;
        this.type = type;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_bilibili_download);
        ButterKnife.bind(this, this);

        logEt.setFocusable(false);
        logEt.setFocusableInTouchMode(false);
        fileNameEt.setEnabled(false);
        downloadStartBt.setText("正在准备…");

        IApplication.getExecutor().execute(() -> {
            try {
                if ("url".equals(type))
                    downloadByUrl(keyWord);
                else
                    downloadByAv(keyWord);
            } catch (Exception e) {
                sendToastMessage("无法获取视频信息,请检查链接或网络状态");
                sendLogMessage("无法获取视频信息,请检查链接或网络状态");
                handler.sendEmptyMessage(104);
                e.printStackTrace();
            }
        });

        initListener();
    }

    /**
     * 初始化接口
     */
    private void initListener(){
        downloadStartBt.setOnClickListener(v ->
           IApplication.getExecutor().execute(() -> {
                handler.sendEmptyMessage(103);
                if (downloadType == DOWNLOAD_ONE){
                    downloadDanmuOne();
                }else if (downloadType == DOWNLOAD_LIST){
                    downloadDanmuList();
                }
            })
        );

        downloadOverBt.setOnClickListener(v -> BiliBiliDownloadDialog.this.cancel());
    }

    /**
     * 根据url获取相关信息
     */
    private void downloadByUrl(String url) throws Exception {
        if (!url.isEmpty()){
            sendLogMessage("开始连接URL...\n");
            String root = Jsoup.connect(url).timeout(10000).get().toString();
            sendLogMessage("连接URL成功\n");

            if(url.contains("www.bilibili.com/video") || url.contains("m.bilibili.com/video")){
                getVideoCid(root);
            }else if (url.contains("www.bilibili.com/bangumi") || url.contains("m.bilibili.com/bangumi")){
                getAnimeCid(root);
            }else {
                sendLogMessage("获取视频链接信息失败\n");
                sendToastMessage("获取视频链接信息失败");
                handler.sendEmptyMessage(104);
            }
        }else {
            sendToastMessage("请输入视频链接");
        }
    }

    /**
     * 根据av号获取相关信息
     */
    private void downloadByAv(String avNumber) throws Exception {
        if (avNumber.isEmpty()){
            sendToastMessage("请输入av号");
        }else {
            sendLogMessage("开始转换URL...\n");
            String url = "https://www.bilibili.com/video/av"+avNumber;
            Connection.Response response = Jsoup.connect(url).timeout(10000).execute();
            //普通视频不会重定向,番剧会进行重定向
            if (!response.url().toString().equals(url)){
                url = response.url().toString();
            }
            sendLogMessage("转换URL成功\n");
            downloadByUrl(url);
        }
    }

    /**
     * 下载视频弹幕
     */
    private void downloadDanmuOne(){
        fileName = fileNameEt.getText().toString();
        String path = AppConfig.getInstance().getDownloadFolder();

        sendLogMessage("开始下载弹幕文件...\n");
        String xmlContent = getXmlString(cid);
        if (xmlContent == null){
            sendLogMessage("弹幕文件下载失败");
            return;
        }
        sendLogMessage("弹幕文件下载成功\n正在写入文件...\n");

        if (fileName.isEmpty())
            fileName = cid;
        DanmuUtils.saveDanmuSourceFormBiliBili(xmlContent, fileName, path);
        sendLogMessage("写入文件成功\n文件路径:\n" + path + "/" + fileName + ".xml");

        handler.sendEmptyMessage(102);
    }

    /**
     * 下载番剧弹幕集合
     */
    private void downloadDanmuList(){
        String path = AppConfig.getInstance().getDownloadFolder();
        path = path + "/"+ animeTitle + Constants.DefaultConfig.danmuFolder;

        sendLogMessage("开始下载弹幕文件...\n");
        for (int i=0; i<cidList.size(); i++){
            int episode = i+1;
            String cid = cidList.get(i);
            sendLogMessage("下载第"+episode+"集弹幕...\n");
            String xmlContent = getXmlString(cid);
            if (xmlContent == null){
                sendLogMessage("第"+episode+"集弹幕文件下载失败");
                continue;
            }

            if (episode < 10)
                fileName = "0"+episode;
            else
                fileName = episode+"";
            DanmuUtils.saveDanmuSourceFormBiliBili(xmlContent, fileName, path);
        }
        sendLogMessage("弹幕下载完成\n文件路径:\n" + path);
        handler.sendEmptyMessage(102);
    }

    /**
     * 发送Log消息
     */
    private void sendLogMessage(String msg){
        Message message = new Message();
        message.what = 100;
        message.obj = msg;
        handler.sendMessage(message);
    }

    /**
     * 发送Toast消息
     */
    private void sendToastMessage(String msg){
        Message message = new Message();
        message.what = 105;
        message.obj = msg;
        handler.sendMessage(message);
    }

    /**
     * 获取视频Cid
     */
    private void getVideoCid(String root){
        sendLogMessage("开始获取cid...\n");

        try {
            int start = root.indexOf("INITIAL_STATE__=")+16;
            int end = root.indexOf(";(function()");
            String jsonText = root.substring(start,end);
            //获取标题
            JsonObject jsonObject = new JsonParser().parse(jsonText).getAsJsonObject();
            JsonObject videoInfo = jsonObject.get("videoData").getAsJsonObject();
            videoTitle = videoInfo.get("title").getAsString();
            //获取cid
            JsonArray cidInfo = videoInfo.get("pages").getAsJsonArray();
            JsonObject cidObject = cidInfo.get(0).getAsJsonObject();
            cid = cidObject.get("cid").getAsString();

            sendLogMessage("获取cid成功\n");
            downloadType = DOWNLOAD_ONE;
            handler.sendEmptyMessage(101);
        }catch (Exception e){
            sendLogMessage("cid解析错误");
        }
    }

    /**
     * 获取番剧Cid
     */
    private void getAnimeCid(String root){
        try {
            sendLogMessage("开始获取番剧cid列表...\n");
            int start = root.indexOf("INITIAL_STATE__=")+16;
            int end = root.indexOf(";(function()");
            String jsonText = root.substring(start,end);
            //获取标题
            JsonObject animeInfo = new JsonParser().parse(jsonText).getAsJsonObject();
            JsonObject animeTitleInfo = animeInfo.get("mediaInfo").getAsJsonObject();
            animeTitle = animeTitleInfo.get("title").getAsString();
            //获取cid集合
            JsonArray cidListArray = animeInfo.get("epList").getAsJsonArray();
            cidList = new ArrayList<>();
            for (int i=0; i<cidListArray.size(); i++){
                JsonObject cidInfo = cidListArray.get(i).getAsJsonObject();
                String cid = cidInfo.get("cid").getAsString();
                cidList.add(cid);
            }
            sendLogMessage("获取番剧【"+animeTitle+"】cid列表成功\n");
            downloadType = DOWNLOAD_LIST;
            handler.sendEmptyMessage(101);
        }catch (Exception e){
            sendLogMessage("cid解析错误");
        }
    }

    /**
     * 下载xml
     */
    private String getXmlString(String cid){
        InputStream in = null;
        InputStream flin = null;
        Scanner sc = null;
        try {
            String xmlUrl = "http://comment.bilibili.com/" + cid + ".xml";
            URL url = new URL(xmlUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
            conn.setConnectTimeout(10000);
            conn.connect();

            in = conn.getInputStream();
            flin = new InflaterInputStream(in, new Inflater(true));

            sc = new Scanner(flin, "utf-8");

            StringBuilder stringBuffer = new StringBuilder();
            while(sc.hasNext())
                stringBuffer.append(sc.nextLine());
            return stringBuffer.toString();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if (sc != null)
                    sc.close();
                if (flin != null)
                    flin.close();
                if (in != null)
                    in.close();
            }catch (IOException e){
                e.printStackTrace();
            }

        }
        return null;
    }
}