package com.yutou.bilibili.BiliBili.Tools;

import com.alibaba.fastjson.JSONObject;
import com.yutou.bilibili.BiliBili.Datas.AppData;
import com.yutou.bilibili.BiliBili.Live;
import com.yutou.bilibili.BiliBili.LiveUtils;
import com.yutou.bilibili.QQBot.QQBotManager;
import com.yutou.bilibili.Tools.*;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class SaveLive {
    private static SaveLive live;
    private final List<String> saveList = new ArrayList<>();
    private final Map<Integer, DownloadThread> downloads = new HashMap<>();
    private final Map<Integer, Timer> heartbeats = new HashMap<>();

    public static void main(String[] args) {
        SaveLive.getInstance().addLive(3715397);
    }

    private SaveLive() {
    }

    public static SaveLive getInstance() {
        if (live == null) {
            live = new SaveLive();
        }
        return live;
    }

    public void addLive(Live live) {
        addLive(live.geData().getRoomid());
    }

    public void startLive(int roomId) {
        start(roomId);
        saveList.add(roomId + "");
    }

    public void addLive(int roomId) {
        if (saveList.contains(roomId + "")) {
            return;
        }
        if (!LiveUtils.isLivePlayer(roomId)) {
            return;
        }
        start(roomId);
    }

    public boolean checkLive(int roomId) {
        return saveList.contains(roomId + "");
    }

    public File getLiveFile(int roomId) {
        return downloads.get(roomId).liveFile;
    }

    private long timer = 0;

    public void stop(int roomId) {
        com.yutou.bilibili.Tools.Log.i("t停止录播:" + roomId + " time=" + (System.currentTimeMillis() - timer));
        saveList.remove(roomId + "");

        if (downloads.containsKey(roomId)) {
            downloads.get(roomId).isSave = false;
            downloads.remove(roomId);
        }

        if (heartbeats.containsKey(roomId)) {
            heartbeats.get(roomId).cancel();
            heartbeats.remove(roomId);
        }

    }

    private void start(int roomId) {
        if (checkLive(roomId)) {
            return;
        }
        timer = System.currentTimeMillis();
        DownloadThread thread = new DownloadThread(roomId);
        downloads.put(roomId, thread);
        saveList.add(roomId + "");
    }

    public List<String> getLiveList() {
        return saveList;
    }

    private class DownloadThread extends Thread {
        int roomId = 0;
        boolean isSave = true;
        Timer heartbeat;
        File liveFile;

        public DownloadThread(int roomId) {
            this.roomId = roomId;
            isSave = true;

            start();

        }

        private String getLiveUrl(int roomId) {
            JSONObject json = LiveUtils.http_get(String.format("https://api.live.bilibili.com/xlive/web-room/v1/playUrl/playUrl?cid=%d&platform=web", roomId));
            if (json != null && json.getInteger("code") == 0) {
                return json.getJSONObject("data").getJSONArray("durl").getJSONObject(0).getString("url");
            } else {
                return null;
            }
        }

        @Override
        public void run() {
            super.run();
            String url = getLiveUrl(roomId);
            try {
                if(AppData.LIVE_SAVE_FFMPEG){
                    ffmpegDownload(url);
                }else {
                    httpDownload(url);
                }
            } catch (Exception e) {
                com.yutou.bilibili.Tools.Log.e(e);
                if (!(e instanceof FileNotFoundException)) {
                    Log.i("录制发生意外:" + e.getMessage());
                    QQBotManager.getInstance().sendMessage("录制发生意外:" + e.getLocalizedMessage());
                }
            }
            SaveLive.this.stop(roomId);

        }

        private void ffmpegDownload(String url) throws Exception {
            liveFile = new File(String.format("%slive%s%s%s[%s]%d.mp4",
                    ConfigTools.load(ConfigTools.CONFIG,"liveSavePath"),
                    File.separator,
                    AppTools.getToDayTime(),
                    File.separator,
                    AppTools.getToDayNowTimeToString().replace(":", ""),
                    roomId));
            if (!liveFile.exists()) {
                liveFile.mkdirs();
                liveFile.delete();
            }
            String exec = String.format("%s    -user_agent  \"%s\"   -cookies \"%s\"   -headers \"%s\"  -i \"%s\"  -threads 8 -c:v copy  -y  \"%s\"  %s ",
                    AppData.FFMPEG,
                    AppData.BILIBILI_HEADERS,
                    LiveUtils.getCookie(),
                    "Referer:https://live.bilibili.com",
                    url,
                    liveFile.getAbsolutePath(),
                    ""
            );
            System.out.println(exec);
            boolean isPrint=false;
            Process process=AppTools.exec(exec);
            InputStream inputStream = process.getErrorStream();
            byte[] bytes = new byte[2048];
            while (inputStream.read(bytes) > -1&&isSave) {
                //System.out.println(new String(bytes,StandardCharsets.UTF_8));
                if(!isPrint){
                    com.yutou.bilibili.Tools.Log.i("开始录制:" + roomId);
                    QQBotManager.getInstance().sendMessage(roomId + " 已启动录制");
                    isPrint=true;
                }
            }
            process.destroy();
            System.out.println("----------------stop ffmpeg");
            inputStream.close();
            com.yutou.bilibili.Tools.Log.i("录制完成:" + roomId);
            QQBotManager.getInstance().sendMessage("录制完成:" + roomId);
        }

        private void httpDownload(String url) throws Exception {
            HttpURLConnection connection = LiveUtils.getBiliHttpGet(url, LiveUtils.getCookie());
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            heartbeat = new Timer();
            //Heartbeat beat = new Heartbeat();
            heartbeat.schedule(new Heartbeat(), 0, 30000);
            heartbeats.put(roomId, heartbeat);
            //heartbeats.add(beat);
            InputStream inputStream = connection.getInputStream();
            liveFile = new File(String.format("%slive%s%s%s[%s]%d.mp4",
                    ConfigTools.load(ConfigTools.CONFIG,"liveSavePath"),
                    File.separator,
                    AppTools.getToDayTime(),
                    File.separator,
                    AppTools.getToDayNowTimeToString().replace(":", ""),
                    roomId));
            if (!liveFile.exists()) {
                liveFile.mkdirs();
                liveFile.delete();
            }
            boolean isPrint=false;
            FileOutputStream outputStream = new FileOutputStream(liveFile);
            int len;
            byte[] bytes = new byte[1024];
            while ((len = inputStream.read(bytes)) != -1 && isSave) {
                outputStream.write(bytes, 0, len);
                outputStream.flush();
                if(!isPrint){
                    com.yutou.bilibili.Tools.Log.i("开始录制:" + roomId);
                    QQBotManager.getInstance().sendMessage(roomId + " 已启动录制");
                    isPrint=true;
                }
            }
            outputStream.close();
            inputStream.close();
            com.yutou.bilibili.Tools.Log.i("录制完成:" + roomId + " save = " + isSave + " len = " + len);
            QQBotManager.getInstance().sendMessage("录制完成:" + roomId + " save = " + isSave + " len = " + len);
        }

        private class Heartbeat extends TimerTask {
            int nextInterval = 1;

            @Override
            public void run() {
                try {
                    JSONObject hearBeat;
                    JSONObject userHear = new JSONObject();
                    JSONObject cookie = JSONObject.parseObject(LiveUtils.getFile("cookies.json"));
                    hearBeat = LiveUtils.http_get("https://api.live.bilibili.com/relation/v1/Feed/heartBeat");
                    com.yutou.bilibili.Tools.Log.i(hearBeat + " \t");
                    hearBeat = LiveUtils.http_get(String.format("https://live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=%s&pf=web", URLEncoder.encode(new String(Base64.getEncoder().encode(String.format("%d|%d|1|0", nextInterval, roomId).getBytes(StandardCharsets.UTF_8))), "UTF-8")));
                    com.yutou.bilibili.Tools.Log.i(hearBeat + "\t");
                    nextInterval = hearBeat.getJSONObject("data").getInteger("next_interval");
                    com.yutou.bilibili.Tools.Log.i("next = " + nextInterval);
                    userHear.put("csrf_token", cookie.getString("bili_jct"));
                    userHear.put("csrf", cookie.getString("bili_jct"));
                    userHear.put("visit_id", "");
                    hearBeat = LiveUtils.http_post("https://api.live.bilibili.com/User/userOnlineHeart", HttpTools.toUrlParams(userHear));
                    com.yutou.bilibili.Tools.Log.i("[" + AppTools.getToDayNowTimeToString() + "]" + hearBeat);
                } catch (Exception e) {
                    com.yutou.bilibili.Tools.Log.e(e);
                }
            }
        }

    }
}
