import org.serviio.library.online.WebResourceUrlExtractor
import org.serviio.library.online.WebResourceContainer
import org.serviio.library.online.ContentURLContainer
import org.serviio.library.online.WebResourceItem
import org.serviio.library.online.PreferredQuality
import org.serviio.library.metadata.MediaFileType
import org.serviio.library.metadata.*
import org.serviio.library.online.*
import groovy.io.FileType

/**
 * Cablevisión
 * 
 * Based on youtube_dl Python script (http://rg3.github.com/youtube-dl/)
 * 
 * @see http://en.wikipedia.org/wiki/Youtube#Quality_and_codecs
 *  
 * @author Hernan Bazan
 *
 */
class Cablevision extends WebResourceUrlExtractor {
////////////////////////////////////////////[Constants]////////////////////////////////////////////
    final static VALID_WEB_RESOURCE_URL                     = '^(?:http?://)?(?:www\\.)?buscadorcablevision.*'
    final static DEFAULT_EPISODE_DOMAIN                     = "http://www.buscadorcablevision.com.ar"
    final static USER_AGENT                                 = 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1'
    final static HREF_REGEX                                 = /(?s)class="hora"><a\sname="\d*"><\/a>(\d\d:\d\d?)\<\/p>\s+\<div\sclass="canal">(.[^\<]*?)\<\/div>\s+\<div\sclass="description">\s+\<a\shref="#"\srev="ficha\.php\?verFicha=(\d+?)"\srel="#overlay_ficha"\sclass="titulo">(.[^\<]*?)\<\/a>/
    final static CURRENT_UL                                 = /(?s)\<ul\sclass="contentList">(.*?)\<\/ul>/
    final static THUMBNAIL_REGEX                            = /(?s)rc="(.[^\s]*?)" class="imgFichaAdentro"/
    final static SEARCH_REGEX                               = /(?s)A&ntilde;o\<\/li>\s+\<li\sclass="descf1">(\d\d\d\d?)\<\/li>\s+\<li\sclass="itemf1">.[^\<]*\<\/li>\s+\<li\sclass="descf1">(.[^\<]*?)\</
    final static CAST_REGEX                               = /(?s)Protagonistas\<\/li>\s+\<li\sclass="descf1">(.[^\<]*?)\</
    final static SEARCH_URL = 'https://gdata.youtube.com/feeds/api/videos?caption=false&max-results=1&v=2&q='
    final static EPISODE_URL_PREFIX                         = "http://www.buscadorcablevision.com.ar/ficha.php?verFicha=";
    final static VALID_PLAYER_URL = '^((?:https?://)?(?:youtu\\.be/|(?:\\w+\\.)?youtube(?:-nocookie)?\\.com/)(?:(?:(?:v|embed|e)/)|(?:(?:watch(?:_popup)?(?:\\.php)?)?(?:\\?|#!?)(?:.+&)?v=)))?([0-9A-Za-z_-]+)(.+)?$'
    final static VALID_FEED_URL = '^http(s)*://.*youtube\\..*$'
    /* Listed in order of quality */
    final static availableFormats = ['37', '46', '22', '45', '35', '34', '18', '44', '43', '6', '5']
    final static VIDEO_BASE = 'https://www.youtube.com/watch?v='
    final static VIDEO_REF = /.*video:(?<code>.*)\<\/id>.*/

    /////////////////////////////////////////////[Methods]/////////////////////////////////////////////
    @Override
    boolean extractorMatches(URL url) {
        return url ==~ VALID_WEB_RESOURCE_URL
    }

    @Override
    String getExtractorName() {
        return getClass().getName()
    }    

    @Override
    protected WebResourceContainer extractItems(URL url, int i) {
        def videoURLsSet = retrieveURLs(url);
        return new WebResourceContainer(title: url.toString(), items: videoURLsSet)
    }

    List<WebResourceItem> retrieveURLs(URL url) {
        Set<String> pages = new HashSet<String>();
        List<WebResourceItem> items = []
        BufferedReader reader = new BufferedReader(new InputStreamReader(url.newInputStream()));
        StringBuffer response = new StringBuffer();
        String inputLine;
        while ((inputLine = reader.readLine()) != null)
        {
            response.append(inputLine)
        }
        reader.close()
        String pageSource = response.toString()
        String hora;
        String canal;
        String ficha;
        String titulo;
        String cast;
        String currentUL = pageSource
        if(pageSource=~CURRENT_UL)
            currentUL = ((pageSource=~CURRENT_UL))[0][1].toString().trim()
        (currentUL=~HREF_REGEX).each {
            hora = (it-null)[1];
            canal = (it-null)[2];
            ficha = (it-null)[3];
            titulo = (it-null)[4];
            String fichaSource = openURL(new URL(EPISODE_URL_PREFIX+ficha), USER_AGENT)
            String img = null
            if(fichaSource=~THUMBNAIL_REGEX)
                img = ((fichaSource=~THUMBNAIL_REGEX))[0][1].toString().trim()
            String year = ""
            if(fichaSource=~SEARCH_REGEX)
                year = ((fichaSource=~SEARCH_REGEX))[0][1].toString().trim()
            String title = titulo
            if(fichaSource=~SEARCH_REGEX)
                title = ((fichaSource=~SEARCH_REGEX))[0][2].toString().trim()
            String uri = new URL(SEARCH_URL+/+/+title+/+/+year+/+trailer/)
            Map<String,String> additionalInfo = new HashMap<String, String>();
            additionalInfo.put("videoUrl",uri)
            additionalInfo.put("thumbUrl",img)
            if(fichaSource=~CAST_REGEX)
                cast = " | "+((fichaSource=~CAST_REGEX))[0][1].toString().trim()
            else
                cast = ""
            if(year.size()>0)
                year = " ("+year+")"
            else
                year = ""
            items << new WebResourceItem(title: hora+" ["+canal+"] "+titulo+year+cast, additionalInfo: additionalInfo)
        }
        print items
        return items
    }
    
    @Override
    protected ContentURLContainer extractUrl(WebResourceItem webResourceItem, PreferredQuality preferredQuality) {
        URL videoPageURL = new URL(webResourceItem.getAdditionalInfo().get("videoUrl"))
        String thumbnailUrl = webResourceItem.getAdditionalInfo()['thumbUrl']
        log(String.format("[%s] Starting video URL extraction for %s",getExtractorName(),videoPageURL))
        ContentURLContainer contentURLContainer = new ContentURLContainer();
        try{
            getYoutube(videoPageURL, thumbnailUrl, preferredQuality, contentURLContainer)
            return contentURLContainer
        }catch (IndexOutOfBoundsException e){
            log.error(String.format("%s , Failed to create resource item for URL: %s",getExtractorName(), it), log.isDebugEnabled()? e:null)
        }
        return null
    }
     
    void getYoutube(URL url, String thumbnailUrl, PreferredQuality requestedQuality, ContentURLContainer contentURLContainer) {
        def contentUrl
        def expiryDate
        def expiresImmediately
        def cacheKey

        String pageSource = openURL(url, USER_AGENT)

        URL linkUrl = new URL(VIDEO_BASE+((pageSource=~VIDEO_REF))[0][1])

        def matcher = linkUrl =~ VALID_PLAYER_URL
        assert matcher != null
        assert matcher.hasGroup()
        
        def videoId = matcher[0][2]

        for (elType in ['&el=embedded', '&el=detailpage', '&el=vevo', '']) {           
            def videoInfoUrl = "http://www.youtube.com/get_video_info?&video_id=$videoId$elType&ps=default&eurl=&gl=US&hl=en"
            //log("Loading video info: $videoInfoUrl")
            def videoInfoWebPage = new URL(videoInfoUrl).getText()
            def parameters = [:]
        
            videoInfoWebPage.split('&').each{item -> addParameter(item, parameters, '=')}
            if(parameters.containsKey('token')) {                
                def formatUrlMapString = parameters['fmt_url_map']    
                def urlEncodedUrlMapString = parameters['url_encoded_fmt_stream_map']
                def allFormatUrlMap = [:]
                if (formatUrlMapString != null && formatUrlMapString.length() > 0 ) {
                    URLDecoder.decode(formatUrlMapString,'UTF-8').split(',').each{item -> addParameter(item, allFormatUrlMap, '\\|')}                        
                } else if (urlEncodedUrlMapString != null && urlEncodedUrlMapString.length() > 0 ) {                     
                    URLDecoder.decode(urlEncodedUrlMapString,'UTF-8').split(',').each{item -> 
                        def streamParams = [:]
                        item.split('&').each{item2 -> addParameter(item2, streamParams, '=')}
                        String urlKeyName = streamParams.containsKey('url') ? 'url' : 'conn'
                        //String stream = URLDecoder.decode(streamParams['stream'],'UTF-8') //TODO stream is playpath
                        String streamUrl = URLDecoder.decode(streamParams[urlKeyName],'UTF-8')
                        String signature = streamParams['sig']
                        if(signature) streamUrl = streamUrl + "&signature=" + signature
                        allFormatUrlMap.put(streamParams['itag'], streamUrl )
                    }
                }                    
               // get available formats for requested quality, sorted by quality from highest
               def formatUrlMap = new LinkedHashMap()
               if(requestedQuality == PreferredQuality.HIGH) {
                   // best quality, get the first from the list
                   sortAvailableFormatUrls(availableFormats, allFormatUrlMap, formatUrlMap)
                   def selectedUrl = formatUrlMap.entrySet().toList().head()
                   contentUrl = selectedUrl.getValue()
                   cacheKey = getCacheKey(linkUrl, selectedUrl.getKey())
               } else if (requestedQuality == PreferredQuality.MEDIUM) {
                   // work with subset of available formats, starting at the position of format 35 and then take the best quality from there
                   sortAvailableFormatUrls(availableFormats.getAt(4..availableFormats.size-1), allFormatUrlMap, formatUrlMap)
                   def selectedUrl = formatUrlMap.entrySet().toList().head()
                   contentUrl = selectedUrl.getValue()
                   cacheKey = getCacheKey(linkUrl, selectedUrl.getKey())
               } else {
                   // worst quality, take the last url
                   sortAvailableFormatUrls(availableFormats, allFormatUrlMap, formatUrlMap)
                   def selectedUrl = formatUrlMap.entrySet().toList().last()
                   contentUrl = selectedUrl.getValue()
                   cacheKey = getCacheKey(linkUrl, selectedUrl.getKey())
               }              
               if(contentUrl != null) {
                   expiresImmediately = true
                   if(contentUrl.startsWith('http')) {
                       // http URL
                       def contentUrlParameters = [:]
                       contentUrl.split('&').each{item -> addParameter(item, contentUrlParameters, '=')}
                       if( contentUrlParameters['expire'] != null ) {   
                           //log(Long.parseLong(contentUrlParameters['expire']).toString())
                           expiryDate = new Date(Long.parseLong(contentUrlParameters['expire'])*1000)
                           expiresImmediately = false
                       }
                   } else {
                           // rtmp URL
                           def rtmpMatcher = contentUrl =~ 'rtmpe?://.*?/(.*)'
                        def app = rtmpMatcher[0][1]
                        // TODO load swf player URL from the HTML page
                           contentUrl = "$contentUrl app=$app swfUrl=http://s.ytimg.com/yt/swfbin/watch_as3-vflg0Q-LP.swf swfVfy=1"
                   }    
               }
                
                break
            }
        }    
        contentURLContainer.setContentUrl(contentUrl)
        contentURLContainer.setThumbnailUrl(thumbnailUrl)
        //return new ContentURLContainer(fileType: MediaFileType.VIDEO, contentUrl: contentUrl, thumbnailUrl: thumbnailUrl, expiresOn: expiryDate, expiresImmediately: expiresImmediately, cacheKey: cacheKey)
    }
 
    def addParameter(parameterString, parameters, separator) {
        def values = parameterString.split(separator)
        if( values.length == 2 ) {    
             parameters.put(values[0], values[1])
        }
    }   

    def String getCacheKey(URL linkUrl, String qualityId) {
        "${linkUrl}_${qualityId}"
    }
        
    def sortAvailableFormatUrls(List formatIds, Map sourceMap, Map targetMap) {
        formatIds.each{formatId ->
            if(sourceMap.containsKey(formatId) ) {                
                 targetMap.put(formatId, sourceMap.get(formatId))
            }
        }
    }
    
    static void main(args) {
        // this is just to test
        def TestUrl = new URL('http://www.buscadorcablevision.com.ar/dinamicas/cine/index.php?companiaSeleccionada=184&dia=1#')
        Cablevision extractor = new Cablevision()
         
        WebResourceContainer container = extractor.extractItems(TestUrl , -1);
        println container
        println "PluginName               : " + extractor.getExtractorName();
        container.getItems().each {
            println "URL                  : " + extractor.extractUrl(it, PreferredQuality.HIGH)
        }
    }
}