import groovy.json.JsonSlurper

import org.serviio.library.metadata.*
import org.serviio.library.online.*


/********************************************************************
 * Syfy.com plugin for Serviio
 * Logic based on XBMC -> BlueCop Repo -> Free Cable -> Syfy
 * Update based on: https://home.no/xbmc/load/syfy.py
 * 
 * @author X S Inattar
 * @author Zip
 *
 * Must be installed as a Video WebResource
 * Sample URLs: 
 *    http://www.syfy.com/videos/Eureka/Full%20Episodes
 *    http://www.syfy.com/videos/Ghost%20Hunters%20International/Full%20Episodes
 * 
 * Version:
 *    V1: June 27, 2012 - Initial Release
 *    V2: June 29, 2012 - Fixed some minor issues
 *    V3: June 10, 2013 - Fixed non-rtmp to properly parse to HLS
 *
 ********************************************************************/

class Syfy extends WebResourceUrlExtractor {
    
    final VALID_FEED_URL = '^(?:http://)?(?:www\\.)?syfy\\.com/videos/(.*?)/Full(.*?)Episodes(?:/)?$'
    final SHOW_FEED_PART1 = 'http://feed.theplatform.com/f/hQNl-B/2g1gkJT0urp6/?form=json&fields=guid,title,description,categories,content,:fullEpisode,defaultThumbnailUrl&fileFields=duration,url,width,height&count=true&byCategories=Shows%2F'
    final ONLINEORIGS_FEED_PART1 = 'http://feed.theplatform.com/f/hQNl-B/2g1gkJT0urp6/?form=json&fields=guid,title,description,categories,content,:fullEpisode,defaultThumbnailUrl&fileFields=duration,url,width,height&count=true&byCategories=Online+Originals%2F'
    final MOVIES_FEED_PART1 = 'http://feed.theplatform.com/f/hQNl-B/2g1gkJT0urp6/?form=json&fields=guid,title,description,categories,content,:fullEpisode,defaultThumbnailUrl&fileFields=duration,url,width,height&count=true&byCategories=Movies+%26+Events%2F'
    final FEED_PART2 = '&byCustomValue={fullEpisode}{true}'
    final SWF_URL = 'http://www.syfy.com/_utils/video/codebase/pdk/swf/flvPlayer.swf'

    String getExtractorName() {
        return 'Syfy.com'
    }
    
    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }
    
    public int getVersion() {
        return 3;
    }
       
    WebResourceContainer extractItems(URL resourceUrl, int maxItemsToRetrieve) {
        
        List<WebResourceItem> items = []
        def itemsAdded = 0
               
        def nameMatcher = resourceUrl.toString() =~ VALID_FEED_URL
        def showTitle = nameMatcher[0][1] 
        showTitle = showTitle.replace("%20", " ")
        def showTitleForSearch = showTitle.replace(" ", "+")
        
        def contentUrl
        def contentText
        def json
        contentUrl = new URL(SHOW_FEED_PART1 + showTitleForSearch + FEED_PART2)
        contentText = contentUrl.getText();
        json = new JsonSlurper().parseText(contentText)
        if (json.totalResults <= 0) {
            contentUrl = new URL(MOVIES_FEED_PART1 + showTitleForSearch + FEED_PART2)
            contentText = contentUrl.getText();
            json = new JsonSlurper().parseText(contentText)
            if (json.totalResults <= 0) {
                contentUrl = new URL(ONLINEORIGS_FEED_PART1 + showTitleForSearch + FEED_PART2)
                contentText = contentUrl.getText();
                json = new JsonSlurper().parseText(contentText)
            }
        }
        
        json.entries.find {
            items << new WebResourceItem(title: it.title, additionalInfo: [GUID: it.guid, thumbnailUrl: it.plmedia$defaultThumbnailUrl, contentUrl: it.media$content[0].plfile$url])
            itemsAdded++
            if (maxItemsToRetrieve != -1 && itemsAdded >= maxItemsToRetrieve) return true
            return false
        }              

        return new WebResourceContainer(title: showTitle, items: items)
    }

    ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {

        def smilUrl = item.additionalInfo.contentUrl
        def smil = new URL(smilUrl).getText()

        def rtmpUrlSet = false
        def baseUrl = smil =~ 'meta base="(rtmp.*?)".*?'        
        def rtmpUrl
        if (baseUrl.count > 0) {            
            rtmpUrl = baseUrl[0][1]
            rtmpUrlSet = true
        }

        def contentUrl 
        if (rtmpUrlSet == true) {
            def vidMatcher = smil =~ 'video src="(.*?)".*?system-bitrate="(.*?)"'
            def clip
            def formats = [:]
            
            for (i in 0..<vidMatcher.count) {
                formats[vidMatcher[i][2].padLeft(10,"0")] = vidMatcher[i][1]
            }
                 
            Comparator comparator = [compare: {a , b ->
                b.compareTo(a)
            }] as Comparator
            
            formats = formats.sort(comparator)
            def keys = formats.keySet().toList()
            
            if (requestedQuality == PreferredQuality.HIGH || formats.size() == 1) {
                clip = formats[keys[0]]
            }
            else if (requestedQuality == PreferredQuality.MEDIUM || formats.size() == 2) {
                clip = formats[keys[1]]
            }
            else if (requestedQuality == PreferredQuality.LOW) {
                clip = formats[keys[2]]
            }
            
            if (clip.contains('.mp4')) { 
                clip = clip.replace('.mp4', '')
                clip = 'mp4:' + clip
            }
            else if (clip.contains('.flv')) {
                clip = clip.replace('.flv', '')
            }
            
            contentUrl = "${rtmpUrl} playpath=${clip} swfUrl=${SWF_URL} swfVfy=0"
        } else {       
            // get it as HLS
            String smilWithM3U = "${smilUrl}&manifest=m3u"
            def smilNode = new XmlParser(false, false).parse(smilWithM3U)
            if(smilNode.body.seq.par.size() == 0) {
                log("No content for item ${item.additionalInfo.GUID}, possible territorial restritions")
                return null
            } else {
                def no = smilNode.body.seq.par
                def m3uUrl = smilNode.body.seq.par[0].video.'@src'.text()
                String m3uContent = new URL(m3uUrl).getText()
                def streamMatcher = m3uContent =~ '(?ismd)BANDWIDTH=(\\d+).*?\\n(.*?)\\n'
                def numberOfStreams = streamMatcher.size()
                List<Tuple> streams = []
                for( int i = 0; i < numberOfStreams; i++ ) {
                    streams << new Tuple(streamMatcher[i][1], streamMatcher[i][2])
                }
                streams = streams.sort { it[0] as Long }
                String streamName = findSuitableItem(streams, requestedQuality)[1]
                contentUrl = m3uUrl.substring(0, m3uUrl.lastIndexOf('/') + 1) + streamName
            }
        }
        def cacheKey = "SYFY_${item.additionalInfo.GUID}_${requestedQuality}"
       
        return new ContentURLContainer(contentUrl: contentUrl, thumbnailUrl: item.additionalInfo.thumbnailUrl, expiresImmediately: true, cacheKey : cacheKey)    
    }
    
    private def Tuple findSuitableItem(List items, PreferredQuality requestedQuality) {
        if(requestedQuality == PreferredQuality.LOW) {
            // worst quality, get the first from the list
            return items.head()
        } else if (requestedQuality == PreferredQuality.MEDIUM) {
            // get item from the middle
            return items.get(Math.round(items.size()/2).toInteger())
        } else {
            // best quality, take the last url
            return items.last()
        }
    }
        
    static void main(args) {
        Syfy extractor = new  Syfy()
        
        WebResourceContainer container = extractor.extractItems( new URL("http://www.syfy.com/videos/Syfy%20Movies/Full%20Episodes"), 5)
        container.getItems().each {
            ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.HIGH)
            println result 
        }   
    }
}