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
 *    V4: July 07, 2013 - Added proxy support
 *
 ********************************************************************/

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'

    final PROXY_HOST = '69.197.132.80'
    final PROXY_PORT = 8089
    boolean IS_PROXY_NEEDED

    String getExtractorName() {
        return 'Syfy.com'
    }

    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }

    public int getVersion() {
        return 4
    }

    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
        if (!IS_PROXY_NEEDED) smil = new URL(smilUrl).getText()
        if (IS_PROXY_NEEDED || smil != null && smil.contains("This content is not available in your location."))  smil = getContent(smilUrl, false)

        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
            if (!IS_PROXY_NEEDED) smilNode = new XmlParser(false, false).parse(smilWithM3U)
            if (IS_PROXY_NEEDED)  smilNode = getContent(smilWithM3U, true)
            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
                if (!IS_PROXY_NEEDED) m3uContent = new URL(m3uUrl).getText()
                if (IS_PROXY_NEEDED)  m3uContent = getContent(m3uUrl, false)

                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 getContent(String url, boolean xml){
        log("Using proxy --> ${PROXY_HOST}:${PROXY_PORT}")
        IS_PROXY_NEEDED = true
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT))
        HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(proxy)
        try {
            con.setConnectTimeout(5000)
            con.setReadTimeout(5000)
            con.setRequestProperty("User-Agent", "Mozilla/4.0")
            try {
                InputStream inn = con.getInputStream()
                if (!xml) return inn.getText()
                return new XmlParser(false, false).parse(inn)
            } catch(e) {
                return null
            }
        } finally {
            con.disconnect()
        }
    }

    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/Warehouse%2013/Full%20Episodes"), 5)
        container.getItems().each {
            ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.HIGH)
            if (result != null) println result
        }
    }
}