import java.text.SimpleDateFormat;
import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.apache.commons.lang.*
import groovy.json.*

/**
 * RSS feed plugin for Serviio Media Server
 * Targeted at MTG sites tv3play, tv6play, tv8play, tv10play in different top domains
 * 
 * Use legacy RSS feeds such as:
 *
 * http://www.tv3play.se/rss/recent
 * http://legacy.tv3play.se/rss/recent
 * http://www.tv3play.se/rss/mostviewed
 * http://www.tv3play.se/rss/highestrated
 *
 * ##############################################
 * Credits
 * ############################################## 
 *
 * @author Otto Dandenell 
 */
 
class Tv3_6_8_10_PlayRss extends FeedItemUrlExtractor {

    final boolean debug = false

    Tv3_6_8_10_PlayCommon mtgPlayCommon = new Tv3_6_8_10_PlayCommon(debug)

    final RSS_RESOURCE_URL = '^http(?:s)?://(?:www|legacy)\\.(?:tv(?:3|6|8|10)|viasat4)play\\.[a-z.]*?[a-z]{2,}/rss/.*$'

    final int VERSION = 2

    static main(args) {
    
        def TestUrlSe = new URL("http://www.tv3play.se/program/blacklist")
        def TestUrlEe = new URL("http://www.tv3play.ee/sisu/suure-paugu-teooria")
        def TestUrlLt = new URL("http://www.tv3play.lt/programos/adrenalinas")
        def TestUrlNo = new URL("http://www.viasat4play.no/programmer/seinfeld")
        def TestUrlDk = new URL("http://www.tv3play.dk/programmer/aktionen")
        
        def TestUrlRss = new URL("http://www.tv8play.se/rss/recent")
        def TestUrlRss2 = new URL("http://legacy.tv3play.se/rss/recent")
        def TestUrlExternal = new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") 
        
        Tv3_6_8_10_PlayRss extractor = new Tv3_6_8_10_PlayRss()
        assert !extractor.extractorMatches( TestUrlSe )
        assert !extractor.extractorMatches( TestUrlEe )
        assert extractor.extractorMatches( TestUrlRss )
        assert extractor.extractorMatches( TestUrlRss2 )
        assert !extractor.extractorMatches( TestUrlExternal )
    
        println "PluginName                  : " + extractor.getExtractorName();
        println "Plugin version              : " + extractor.getVersion();
        
        // Fake the extraction of rss feed items
        java.util.Map<String,URL> links = new java.util.HashMap<String,URL>()
        links.put('default', new URL('http://tv3play.se/play/278587'))
        links.put('thumbnailUrl', 'thumnailURL')
        ContentURLContainer result = extractor.extractUrl(links, PreferredQuality.HIGH)
        println 'result.contentUrl: ' + result.contentUrl
    
    }
    
    String getExtractorName() {
        return getClass().getName()
    }

    int getVersion() {
        log ('getVersion() called, returning ' + VERSION)
        return VERSION
    }
    
    boolean extractorMatches(URL resourceUrl) {
        log ('extractorMatches(' + resourceUrl + ') called')
        return (resourceUrl ==~ RSS_RESOURCE_URL)
    }
    
    ContentURLContainer extractUrl(java.util.Map<String,URL> links, PreferredQuality requestedQuality) {
       // link
       URL videoArticleUrl = links.get("default")
       URL thumbnailImageUrl = links.get("thumbnailImage")
       
       
       if (debug) println 'videoArticleUrl: ' + videoArticleUrl
       log('videoArticleUrl: ' + videoArticleUrl)

       String strThumbnailImageUrl = null
       if (thumbnailImageUrl != null)
       {
           strThumbnailImageUrl = thumbnailImageUrl.toString()
       }
       
       log('Tv3_6_8_10_PlayRss.extractUrl, videoArticleUrl: ' + videoArticleUrl)
       log('Tv3_6_8_10_PlayRss.extractUrl, strThumbnailImageUrl: ' + strThumbnailImageUrl)
       return mtgPlayCommon.extractUrl(videoArticleUrl, requestedQuality, strThumbnailImageUrl, false, null, null)
        
    }
}

/*
* Helper class for Web resource plugin as well as RSS feed plugin, 
* inherinting from AbstractUrlExtractor in order to get access to the shared log() method.
* Targeted at MTG sites tv3play, tv6play, tv8play, tv10play in different top domains.
*
* This class will help extract stream URL:s from playable web page URL:s as well as from 
* links grabbed from RSS feeds.
*
* Due to problems importing classes between groovy files,
* a copy of this class also resides in the Web resource plugin file.
* So when one is updated the other should be as well
*/
class Tv3_6_8_10_PlayCommon extends AbstractUrlExtractor {

    final VALID_RESOURCE_URL = '^http(?:s)?://(?:www\\.)?(?:tv(?:3|6|8|10)|viasat4)play\\.[a-z.]*?[a-z]{2,}/.*$'
    final RSS_RESOURCE_URL = '^http(?:s)?://(?:www\\.)?(?:tv(?:3|6|8|10)|viasat4)play\\.[a-z.]*?[a-z]{2,}/rss/.*$'

    boolean debug = true;
    
    private static SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mmz" )
    
    public Tv3_6_8_10_PlayCommon(boolean debug)
    {
        this.debug = debug
    }

    String getExtractorName() {
        return getClass().getName()
    }

    boolean extractorMatches(URL resourceUrl) {
        // Should never be called
        return (resourceUrl ==~ VALID_RESOURCE_URL) && !(resourceUrl ==~ RSS_RESOURCE_URL)
    }

    ContentURLContainer extractUrl(URL videoArticleUrl, PreferredQuality requestedQuality, String thumbnailUrl, boolean blnIsLive, Date releaseDate, String swfUrl) {
      
        
        // The video article URL may be on the format http://www.tv3play.se/program/blacklist/466763?autostart=true
        // We want to grab the id
        def strVideoId = ""
        def idExtractorMatcher = (videoArticleUrl =~ 'http(?:s)?://(?:www\\.)?(?:tv(?:3|6|8|10)|viasat4)play\\.[a-z.]*?[a-z]{2,}/.*/([0-9]*?)(?:\\?autostart=true$|$)')
        if (idExtractorMatcher != null && idExtractorMatcher.getCount() > 0)
        {
            strVideoId = idExtractorMatcher[0][1]
        }
        else
        {
            return null;
        }
        
        if (debug)
        {            
            println 'strVideoId: ' + strVideoId
        }
                
        // Parse the json resource url from the data-json attribute.
        def videoJsonUrl = new URL('http://playapi.mtgx.tv/v3/videos/' + strVideoId)

        log('videoJsonUrl: ' + videoJsonUrl)
        if (debug) println 'videoJsonUrl: ' + videoJsonUrl
        def videoJsonContent
        def connectionJson = videoJsonUrl.openConnection()
        if(connectionJson.responseCode == 200){
            videoJsonContent = connectionJson.content.text
        }
        else{
            return null
        }
       // log(' videoJsonContent: ' + videoJsonContent)
        if (debug)
        {
            println ' videoJsonContent: ' + videoJsonContent
        }
        
        def jsonSlurper = new JsonSlurper()
        def videoJsonObject = jsonSlurper.parseText(videoJsonContent);
        
        if (debug) println 'videoJsonObject.id: ' + videoJsonObject.id
        if (debug) println 'videoJsonObject._links.stream.href: ' + videoJsonObject._links.stream.href
        if (debug) println 'videoJsonObject.sami_path: ' + videoJsonObject.sami_path
        if (videoJsonObject.sami_path != null)
        {
            log ('Subtitles for episode can be found at: ' + videoJsonObject.sami_path)
        }
        
        def streamJsonContent
        def streamJsonUrl = new URL(videoJsonObject._links.stream.href)
        def streamJsonConnection = streamJsonUrl.openConnection()
        if(streamJsonConnection.responseCode == 200){
            streamJsonContent = streamJsonConnection.content.text
        }
        else{
            return null
        }
        if (debug) println 'streamJsonContent: ' + streamJsonContent
        
        def streamJsonObject = jsonSlurper.parseText(streamJsonContent)
        if (debug) println 'streamJsonObject.streams.hls: ' + streamJsonObject.streams.hls
        if (debug) println 'streamJsonObject.streams.high: ' + streamJsonObject.streams.high
        if (debug) println 'streamJsonObject.streams.medium: ' + streamJsonObject.streams.medium
        
        def strStreamUrl
        if (streamJsonObject.streams.hls != null)
        {
            strStreamUrl = streamJsonObject.streams.hls
        }
        else if (streamJsonObject.streams.high != null)
        {
            strStreamUrl = streamJsonObject.streams.high
        }
        else if (streamJsonObject.streams.medium != null)
        {
            strStreamUrl = streamJsonObject.streams.medium
        }
        if (debug) println 'strStreamUrl: ' + strStreamUrl
        
        def contentUrl = GetBestMatch(strStreamUrl, requestedQuality, swfUrl)  
        if (contentUrl == null)
            return null
         
        
        ContentURLContainer container =  new ContentURLContainer(fileType: MediaFileType.VIDEO, contentUrl: contentUrl, thumbnailUrl: thumbnailUrl, live: blnIsLive)
        if (blnIsLive) {
            // Set a cache key and mark this as expired on its startTime
            def strCacheKey = "" + videoArticleUrl + '_' + requestedQuality
            container.cacheKey = strCacheKey
            container.expiresOn = releaseDate
        }
        return container
    }

    String GetBestMatch(String streamUrl, PreferredQuality requestedQuality, String swfUrl) {
        String match = null                                        
        if (streamUrl.contains("m3u8"))
        {
            def m3u8URL = new URL(streamUrl)
            def m3u8Connection = m3u8URL.openConnection()
            if (m3u8Connection.responseCode == 200){
                def  m3u8Content = m3u8Connection.content.text
                if (debug) println 'm3u8Content: ' + m3u8Content
                
                List m3u8LinesByBitrate = m3u8Content.readLines()
                def qualityList = []
                for(int index = 0; index < m3u8LinesByBitrate.size() - 1; index++)
                {
                    def m3u8QualityMatcher = (m3u8LinesByBitrate.get(index) =~ '#EXT-X-STREAM-INF.*?BANDWIDTH=(\\d+?)(?:[^\\d]|$)')
                    
                    if (m3u8QualityMatcher != null && m3u8QualityMatcher.size() > 0)
                    {
                        qualityList << ['url': m3u8LinesByBitrate.get(index+1), 'bitrate': m3u8QualityMatcher[0][1].toInteger()] 
                    }
                }
                if (qualityList.size() > 0)
                {
                    if (debug)
                    {
                        for (int listIndex = 0; listIndex < qualityList.size(); listIndex++)
                        {
                            println 'qualityList[' + listIndex + '].bitrate: ' + qualityList[listIndex].bitrate
                            println 'qualityList[' + listIndex + '].url: ' + qualityList[listIndex].url
                        }
                    }
                    int priorityIndex
                    if(requestedQuality == PreferredQuality.HIGH){
                        priorityIndex = 0
                    } else {
                        if (requestedQuality == PreferredQuality.MEDIUM){
                            priorityIndex = qualityList.size() / 2
                        } else {
                            // LOW
                            priorityIndex = qualityList.size() - 1
                        }
                    }
                    
                    if (debug) println 'priorityIndex: ' + priorityIndex
                    
                    qualityList.sort { a, b ->
                        // Compare bandwidth descending
                        b.bitrate <=> a.bitrate
                    }
                    match = qualityList[priorityIndex].url 
                    if (debug) println 'match: ' + match
                }
                
                if (match == null)
                {
                    String[] priorityListHttp
                    if(requestedQuality == PreferredQuality.HIGH){
                        priorityListHttp = ['1280x720','1024x576','768x432','704x396','576x324','512x288','480x270','320x180']
                    } else {
                        if (requestedQuality == PreferredQuality.MEDIUM){
                            priorityListHttp = ['768x432','704x396','576x324','1024x576','512x288','480x270','1280x720','320x180']
                        } else {
                            // LOW
                            priorityListHttp = ['320x180','480x270','512x288','576x324','704x396','768x432','1024x576', '1280x720']
                        }
                    }
                                
                    List m3u8Lines = m3u8Content.readLines()
                    priorityListHttp.each{
                        
                        if(match == null)
                        {
                            for(int index = 0; index < m3u8Lines.size() - 1; index++)
                            {
                                def httpm3u8QualityMatcher = (m3u8Lines.get(index) =~ '#EXT-X-STREAM-INF\\:.*,RESOLUTION=' + it +'(?:,|$)')
                                if(httpm3u8QualityMatcher.size() > 0 )
                                {
                                    match = m3u8Lines.get(index+1)
                                    
                                    // clean up ugly stuff in the url
                                    match = (match =~ /\\?null=&/).replaceAll('')
                                    match = (match =~ /&id=$/).replaceAll('')                                    
                                }
                            }
                        }                                    
                    }
                }
            }
            if (match != null)
            {
                if (!(match ==~ 'http(?:s)?://.*'))
                {
                    def baseURLMatcher = streamUrl =~ '(http(?:s)?://.*?/)[^/]*$'
                    if (baseURLMatcher != null && baseURLMatcher.getCount() > 0)
                    {
                        match = baseURLMatcher[0][1] + match
                    }
                }
                log(' found m3u8 http stream url. Best match for requested quality: ' + match)
                if (debug)  println ' found m3u8 http stream url. Best match for requested quality: ' + match
            }
        }
        else 
        {
            if (swfUrl == null)
            {
                swfUrl = 'http://flvplayer.viastream.viasat.tv/flvplayer/play/swf/MTGXPlayer-1.9.1.swf'
            }
            def parts = streamUrl.split('/mp4:')
            
            if(parts.size() > 1)
            {
                match = parts[0] + '/ swfUrl=' + swfUrl + ' playpath=mp4:'+parts[1]+' swfVfy=1'
            }
            else 
            {
                match = streamUrl + ' swfUrl=' + swfUrl + ' swfVfy=1'
            }
        }

        return match
    }

    Date GetValidDate(strMarkupDate)
    {
        // Reformat date from html markup to parsable date string
        // NOTE: SimpleDateFormat uses GMT[-+]hh:mm for the TZ which breaks
        // things a bit.  Before we go on we have to repair this.

        //this is zero time so we need to add that TZ indicator for 
        if ( strMarkupDate.endsWith( "Z" ) ) {
            strMarkupDate = strMarkupDate.substring( 0, strMarkupDate.length() - 1) + "GMT-00:00";
        } else {
            int inset = 6
        
            String s0 = strMarkupDate.substring( 0, strMarkupDate.length() - inset );
            String s1 = strMarkupDate.substring( strMarkupDate.length() - inset, strMarkupDate.length() );

            strMarkupDate = s0 + "GMT" + s1
        }
        
        if (debug) println 're-structured strMarkupDate: ' + strMarkupDate
        
        return df.parse( strMarkupDate )
    }    
}