import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class NiconicoDownload{
    public static void main(String[] args) throws IndexOutOfBoundsException, URISyntaxException,
                                                    MalformedURLException, IOException {
        String niconicoApiLogin1 = "https://account.nicovideo.jp/api/v1/login?show_button_twitter=1";
        String niconicoApiLogin2 = "&site=niconico&show_button_facebook=1&next_url=&mail_or_tel=1";
        String niconicoApiLogin = niconicoApiLogin1 + niconicoApiLogin2;    //  長すぎて分割
        String niconicoApiGetflv = "http://www.nicovideo.jp/api/getflv/";
        String niconicoApiGetthumbinfo = "http://ext.nicovideo.jp/api/getthumbinfo/";
        String videoID = "";		//	動画ID
        String mail_tel = "";		//	メールアドレス
        String password = "";		//	パスワード


        CookieManager cManager = new CookieManager();
        CookieHandler.setDefault(cManager);

        //  ログイン
        URL url = new URL(niconicoApiLogin);
        HttpURLConnection hConnection = (HttpURLConnection)url.openConnection();
        hConnection.setRequestMethod("POST");
        hConnection.setDoOutput(true);
        hConnection.connect();
        BufferedWriter bWriter = new BufferedWriter(new OutputStreamWriter(hConnection.getOutputStream()));
        bWriter.write("mail_tel=" + URLEncoder.encode(mail_tel, "UTF-8"));
        bWriter.write("&password=" + URLEncoder.encode(password, "UTF-8"));
        bWriter.flush();
        bWriter.close();
        int responseCode = hConnection.getResponseCode();   //  HTTPステータス確認
        System.out.println(url + "\nStatus: " + responseCode);

        //  Cookieの取得保存
        CookieStore cStore = cManager.getCookieStore();
        List<HttpCookie> httpCookiesList = cStore.getCookies();
        for(int i = 0; i < httpCookiesList.size(); i++){
            HttpCookie hCookie = httpCookiesList.get(i);
//            System.out.println("Cookie[" + i + "]: " + hCookie);
        }
//        System.out.print("\n");
/*
        //  HTTPヘッダの表示
        Map<String, List<String>> headerFieldMap = hConnection.getHeaderFields();
        Set set = headerFieldMap.keySet();
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            String key = (String)iterator.next();
            System.out.println("Key: " + key + " / " + headerFieldMap.get(key));
        }
        System.out.print("\n");
*/
        hConnection.disconnect();

        //  動画本体のURL取得
        url = new URL(niconicoApiGetflv + videoID);
        hConnection = (HttpURLConnection)url.openConnection();
        hConnection.setRequestProperty("Cookie", httpCookiesList.get(httpCookiesList.size() - 1).toString());
        hConnection.connect();
        responseCode = hConnection.getResponseCode();
        System.out.println(url + "\nStatus: " + responseCode);
        BufferedReader bReader = new BufferedReader(new InputStreamReader(hConnection.getInputStream(), "UTF-8"));
        String[] response = bReader.readLine().split("&");      //  &で分割
        hConnection.disconnect();
        for(int i = 0; i < response.length; i++){
            response[i] = URLDecoder.decode(response[i], "UTF-8");
//            System.out.println(response[i]);
        }
//        System.out.print("\n");

        //  動画のURLをMapに保持
        Map<String, String> flvMap = new HashMap<>();
        for(int i = 0; i < response.length; i++){
            flvMap.put(response[i].substring(0, response[i].indexOf("=")),
                        response[i].substring(response[i].indexOf("=") + 1));
        }
        String videoURL = flvMap.get("url");

        //  動画情報の取得
        url = new URL(niconicoApiGetthumbinfo + videoID);
        hConnection = (HttpURLConnection)url.openConnection();
        responseCode = hConnection.getResponseCode();
        System.out.println(url + "\nStatus: " + responseCode);
        bReader = new BufferedReader(new InputStreamReader(hConnection.getInputStream(), "UTF-8"));
        String str;     //  作業用文字列
        String videoInfoXml = new String();     //  動画情報のXML
        while((str = bReader.readLine()) != null){
                videoInfoXml += (str + "\n");       //  XMLを受け取り、見やすく整形
        }
        bReader.close();
        hConnection.disconnect();
//        System.out.println(videoInfoXml);

        //  タグを除く動画情報をMapに保持
        Map<String, String> infoMap = new HashMap<>();
        str = videoInfoXml.substring(videoInfoXml.indexOf("<thumb>") + 8, videoInfoXml.indexOf("</thumb>"));
        response = str.split("\n");
        for(int i = 0; i < response.length - 1; i++){
                if(response[i].indexOf("<tag") < 0 && response[i].indexOf("</tag") < 0){
                        infoMap.put(response[i].substring(response[i].indexOf("<") + 1, response[i].indexOf(">")),
					response[i].substring(response[i].indexOf(">") + 1, response[i].indexOf("</")));
//                        System.out.println(response[i]);
                }
        }
//        System.out.print("\n");

        String videoTitle = new String(infoMap.get("title"));       //  動画タイトル
        String videoType = new String(infoMap.get("movie_type"));   //  動画形式
        String watchURL = new String(infoMap.get("watch_url"));     //  視聴ページURL

        //  視聴ページを開いて見ているフリ
        url = new URL(watchURL);
        hConnection = (HttpURLConnection)url.openConnection();
        hConnection.setRequestProperty("Cookie", httpCookiesList.get(httpCookiesList.size() - 1).toString());
        hConnection.connect();
        responseCode = hConnection.getResponseCode();
        System.out.println(url + "\nStatus: " + responseCode);
        cStore = cManager.getCookieStore();     //  視聴時のセッション更新(動画本体へのアクセス時に必要？)
        httpCookiesList = cStore.getCookies();
        for(int i = 0; i < httpCookiesList.size(); i++){
            HttpCookie hCookie = httpCookiesList.get(i);
//            System.out.println("Cookie[" + i + "]: " + hCookie);
        }
        hConnection.disconnect();
//        System.out.print("\n");

        //  動画のダウンロード
        url = new URL(videoURL);
        hConnection = (HttpURLConnection)url.openConnection();
        hConnection.setRequestProperty("Cookie", httpCookiesList.get(httpCookiesList.size() - 1).toString());
//        hConnection.setRequestProperty("Referer", watchURL);      //  リファラの指定(効果なし？)
        hConnection.setAllowUserInteraction(false);
        hConnection.setInstanceFollowRedirects(true);
        hConnection.setRequestMethod("GET");
        hConnection.connect();
        responseCode = hConnection.getResponseCode();
        System.out.println(url + "\nStatus: " + responseCode);
        DataInputStream dInputStream = new DataInputStream(hConnection.getInputStream());
        String fileName;        //  保存時のファイルネーム
        if(flvMap.get("url").endsWith("low")){      //  エコノミー時
            fileName = new String("./video/" + videoID + "low_" + videoTitle + "." + videoType);
        }
        else{       //  通常時
            fileName = new String("./video/" + videoID + "_" + videoTitle + "." + videoType);
        }
        DataOutputStream dOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
        byte[] bytes = new byte[4096];
        int readByte = 0;
        while((readByte = dInputStream.read(bytes)) != -1){
                dOutputStream.write(bytes, 0, readByte);
        }
        dInputStream.close();;
        dOutputStream.close();
        hConnection.disconnect();

        System.out.println("\nProgram End\n");
    }
}