const FLV_EXTENSION = "flv";
const MP4_EXTENSION = "mp4";
const FLV_CONTENT_TYPE = "video/flv";
const MP4_CONTENT_TYPE = "video/mp4";

/* タイトルの編集 ex: videoID smXXXXXXX  videoTitle ～てみた */
var makeVideoTitle = function(videoID, videoTitle) {
    var title = videoTitle;
    /* Example */
//    var title = videoID+"."+videoTitle;
//    var title = videoTitle+"_"+videoID;

    return title;
}

var ucjsNicovideoDownloader = {

    init: function(){
        var contentAreaContextMenu = document.getElementById("contentAreaContextMenu");

        // コンテキストメニューにダウンロード用のアイテム追加
        this._menuItem  = document.createElement("menuitem");
        this._menuItem.setAttribute("label", "Download FLV/MP4");
        contentAreaContextMenu.insertBefore(this._menuItem,
                contentAreaContextMenu.firstChild);

        contentAreaContextMenu.addEventListener("popupshowing",
            function(){ ucjsNicovideoDownloader.onPopupShowing(this); }, false);
    },

    onPopupShowing: function(aPopup){
        this._menuItem.hidden = true;

        // nicovideo.jp かチェック
        var location = content.document.location;
        if(location.href.indexOf("nicovideo.jp/watch/") == -1) return;
        // Video ID とタイトルの取得
        /*
        var videoID = content.document.evaluate('.//object[@id="flvplayer"]/param[contains(@value,"flvplayer.swf")]',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
        if(!videoID.singleNodeValue) return;
        videoID = videoID.singleNodeValue.value;
        if(!videoID.match(/flvplayer\.swf\?v=(\w+)/)) return;
        var videoID = RegExp.$1;
        */
        var videoID = /nicovideo\.jp\/watch\/(\w+)/.exec(location.href)[1];

        var title = content.document.evaluate('.//div/h1/a/text()',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
        if(!title.singleNodeValue) return;
        var videoTitle = makeVideoTitle(videoID,String(title.singleNodeValue.textContent)).replace(/[\$\*\?\"\'\/\\\:]/g, "_");

        // メニューの有効化
        this._menuItem.setAttribute("oncommand", 
                "ucjsNicovideoDownloader.download('"+ videoTitle +"','"+ videoID +"')");
        this._menuItem.hidden = false;
    },

    download: function(aTitle, aVideoID){
        var downloader = new ucjsNicovideoDownloader.downloader();
        downloader.download(aTitle, aVideoID);
    }
}


ucjsNicovideoDownloader.downloader = function(){
    this.SKIP_SELECT_PROMPT = true;
}

ucjsNicovideoDownloader.downloader.prototype = {
    download: function(aTitle, aVideoID){
        this._title = aTitle;
        this._state = 0;

        this._ioService = Components.classes["@mozilla.org/network/io-service;1"]
                .getService(Components.interfaces.nsIIOService);
        this.wrappedJSObject = this;

        // Video ID から Player の URL を取得
        var nicovideoURLSpec = "http://www.nicovideo.jp/api/getflv?v=" + aVideoID;
        var nicovideoURL = this._ioService.newURI(nicovideoURLSpec, null, null);
        var httpChannel = this._ioService.newChannelFromURI(nicovideoURL)
                .QueryInterface(Components.interfaces.nsIHttpChannel);
        httpChannel.requestMethod = "GET";
        httpChannel.redirectionLimit = 0;
        httpChannel.asyncOpen(this.getListener(), this);
    },

    onDataAvailable: function(aRequest, aContext, aistream, offset, count){
        aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
        if(aRequest.responseStatus != 200){
            alert("ERROR: ucjsNicovideoDownloader");
            return;
        }

        var sstream
                = Components
                .classes ["@mozilla.org/scriptableinputstream;1"]
                .createInstance (Components.interfaces
                                 .nsIScriptableInputStream);
        sstream.init (aistream);
        result = sstream.read (-1);
        sstream.close ();
        var p = {};
        for each(var a in result.split("&")) {
           if(typeof a != "string") break;
           var t = a.split("=");
           p[t[0]] = t[1];
        }
        var url = unescape(p["url"]);
        this.saveVIDEO(this._title, url);
    },

    getListener: function(){
        var listener = {
            onStartRequest: function(){},
            onDataAvailable: function (aRequest, aContext, aistream, offset, count){
                aContext.wrappedJSObject.onDataAvailable(aRequest, aContext, aistream, offset, count);
            },
            onStopRequest: function(){
            }
        }
        return listener;
    },

    saveVIDEO: function(aTitle, aURLSpec){
        var VIDEO_EXTENSION = FLV_EXTENSION;
        var VIDEO_CONTENT_TYPE = FLV_CONTENT_TYPE;

        // Thanks to いつき http://blog.proj.jp/ituki/20080305.html#p02
        var media_type = /\.nicovideo\.jp\/smile\?(.)\=/.exec(aURLSpec)[1];
        if ( media_type && media_type == "m" ){
          // 多分mp4 download
          VIDEO_EXTENSION = MP4_EXTENSION;
          VIDEO_CONTENT_TYPE = MP4_CONTENT_TYPE;
        }

        var videoName = aTitle + "." + VIDEO_EXTENSION;
        var videoURL = this._ioService.newURI(aURLSpec, null, null)
                .QueryInterface(Components.interfaces.nsIURL);
        var fileInfo = new FileInfo(videoName, videoName, aTitle, VIDEO_EXTENSION, videoURL);

        var fpParams = {
            fpTitleKey: "",
            isDocument: false,
            fileInfo: fileInfo,
            contentType: VIDEO_CONTENT_TYPE,
            saveMode: GetSaveModeForContentType(VIDEO_CONTENT_TYPE),
            saveAsType: 0,
            file: null,
            fileURL: null
        };
        if(!getTargetFile(fpParams, this.SKIP_SELECT_PROMPT)){
            return;
        }

        var fileURL = fpParams.fileURL || makeFileURI(fpParams.file);

        var persist = makeWebBrowserPersist();
        var nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
        persist.persistFlags = 
            nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
            nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE;

        var transfer = Components.classes["@mozilla.org/transfer;1"]
                        .createInstance(Components.interfaces.nsITransfer);
        transfer.init(fileInfo.uri, fileURL, "", null, null, null, persist);
        persist.progressListener = transfer;
        persist.saveURI(fileInfo.uri, null, null, null, null, fileURL);
    }
}

ucjsNicovideoDownloader.init();
