import groovy.util.XmlParser
import org.serviio.library.metadata.*
import org.serviio.library.online.*
import groovy.json.JsonSlurper

 /********************************************************************
 * CBS.com plugin for Serviio (US ONLY)
 * Player/clip logic based on CBS Plugin for Plex
 * 
 *
 * Version:
 *    V1: - x x, xxx - Initial Release by Mike_Metro
 *    V2: - Aug  35, 2012 - updated to reflect new feed url, updated by: X S Inattar
 *    V3: - Apr  13, 2013 - New carousel link, PID not available in the carousel
 *    V4: - Apr  17, 2013 - New carousel link and new json format
 *    V5: - Apr  26, 2013 - New carousel link and versioning
 *	  V6: - May  12. 2013 - Better error handler, implemented maxItemsToRetrieve, fixed bug on show with only one quality
 * Must be installed as a WebResource
 * Only available in US
 * Sample URLs: http://www.cbs.com/shows/csi/video/
 
 ********************************************************************/
  


class CBS extends WebResourceUrlExtractor {
    
    final VALID_FEED_URL = '^(?:http://)?(?:www\\.)?cbs\\.com/shows/(.*?)/video(?:/)?$'
	//final SHOW_LIST = 'http://www.cbs.com/carousels/videosBySection/%s/0/15/' // V3
    //final SHOW_LIST = 'http://www.cbs.com/carousels/videosBySection/%s/offset/0/limit/15/'  // V4
	//final SHOW_LIST = 'http://www.cbs.com/carousels/videosBySection/%s/offset/0/limit/15/xs/0'  // V5 
	final SHOW_LIST = 'http://www.cbs.com/carousels/videosBySection/%s/offset/0/limit/%s/xs/0'  // V6
	


    
	final PRE_SMIL_URL ='http://www.cbs.com%s'
	final SMIL_URL ='http://link.theplatform.com/s/dJ5BDC/%s?format=SMIL&Tracking=true&mbr=true'
	
   	int getVersion() {
		return 6
	}
	
	int getExtractItemsTimeout(){
		return 20
	}
	
	WebResourceContainer errorHandlerWRC(String e){
		List<WebResourceItem> items = []
		println e
		log(e)
		items <<  new WebResourceItem(title: e, additionalInfo: ['url':'http://error','thumbnailUrl':'http://fake.jpg'])
		WebResourceContainer wrc = new WebResourceContainer(title: "Error", items: items)
		return wrc
	}
	
	void errorHandler(String e){
		println e
		log(e)
		return 
	}

    String getExtractorName() {
        return 'CBS.com'
    }
    
    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }
       
    WebResourceContainer extractItems(URL resourceUrl, int maxItemsToRetrieve) {
        List<WebResourceItem> items = []
		Date releaseDate
	    def json

		def i=0
		
        def pageContent = resourceUrl.getText()
		
		def jsMatcher = pageContent =~ "id-carousel-(.*)\" class"  //V4
		
		if (jsMatcher.count <= 0) {
           return errorHandlerWRC("CBS: Carousel ID not found")
		   
		}
		
		def	section = jsMatcher[0][1]
		
		
		
		def nameMatcher = resourceUrl.toString() =~ VALID_FEED_URL
		if (nameMatcher.count <= 0) {
		   errorHandlerWRC("CBS: Cannot find show title")
		   
		}	
        def showTitle = nameMatcher[0][1]
		
		//modified in V6, added maxItemsToRetrieve
        def contentUrl = new URL(String.format(SHOW_LIST, section, maxItemsToRetrieve))

		try{
			 json = new JsonSlurper().parseText(contentUrl.getText())
		}catch (FileNotFoundException e){
			errorHandlerWRC("CBS: Carousel page not found")
		
		}
		
		//doesn't work, need to be fixed
		 /*json.itemList.find { 
            items << new WebResourceItem(title: title, releaseDate: new Date(airdate.toLong()), additionalInfo: [ID:url, thumbnailUrl: thumb.large])
            itemsAdded++
            if (maxItemsToRetrieve != -1 && itemsAdded >= maxItemsToRetrieve) return true
            return false
        }*/
		
		//working
		
		try{
		//V6 changed result.total to result.size
		for (i=0;i<Integer.valueOf(json.result.size);i++){
		
			Map<String,String> additionalInfo = new HashMap<String, String>();
		
			//thumbnailUrl=json.result.data[i].thumb.large
			
			additionalInfo.put("url",json.result.data[i].url)
			additionalInfo.put("thumbnailUrl",json.result.data[i].thumb.large)
	
			//removed in V4
			//airdate is available again, no need to parse
			//jsMatcher = thumbnailUrl =~ "([0-9]{4})/([0-9]{2})/([0-9]{2})"
			//releaseDate= Date.parse("yyyy/MM/dd", jsMatcher[0][0])
			
			releaseDate=Date.parse("MM/dd/yy",json.result.data[i].airdate)
			
			items << new WebResourceItem(title: json.result.data[i].title,releaseDate: releaseDate,additionalInfo: additionalInfo)
		}
		}catch(e){
			errorHandlerWRC("CBS: Json Error parsing" & e)
		}
		

		
        return new WebResourceContainer(title: showTitle, items: items)
    }

    ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {

	
		//added in V6
		if (item.additionalInfo.url.contains("http://error")){
			def rtmpUrl = "rtsp://error"
			def secCode = "abcdefghi"
			def cacheKey = "http://lastitem_" + secCode
			def expiresImmediately = false
			return new ContentURLContainer( contentUrl: rtmpUrl, thumbnailUrl: item.additionalInfo.thumbnailUrl,cacheKey: cacheKey, expiresImmediately: expiresImmediately, live: true)
		}
	
		//edited in V3
        def pageContent = new URL(String.format(PRE_SMIL_URL , item.additionalInfo.url)).getText()
		
		def PID = pageContent =~ "video.settings.pid = '(.*)';" 
		if (PID.count <= 0) {
           errorHandler("CBS: PID not found")
		   return null
		}
		
		def smil = new URL(String.format(SMIL_URL, PID[0][1])).getText()
		
		
        // xml parser/slurper had issues was decoding encoded characters &amp; etc.   
	
		
       // updated by: X S Inattar
       // updated on: August 25, 2012
       // begin
       //
       def stream_source_matcher = smil =~ 'meta base="(.*?)"'
       if (stream_source_matcher.count <= 0) {
			errorHandler("CBS: Cannot find stream source")
			return null
       }
	   
       def stream_source = stream_source_matcher[0][1]        
       
        def vidMatcher = smil =~ 'video src="(.*?)".*?system-bitrate="(.*?)"'
		if (vidMatcher.count <= 0) {
			errorHandler("CBS: Cannot find stream bitrate")
			return null
        }
		
	    
        def player
        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()
		
		//V6 formats.count was always null, change to keys.size
        if (requestedQuality == PreferredQuality.HIGH || keys.size == 1) {
            player = formats[keys[0]]        
        }    
        else if (requestedQuality == PreferredQuality.MEDIUM || keys.size == 2) {
            player = formats[keys[1]] 
        }
        else if (requestedQuality == PreferredQuality.LOW) {
            player = formats[keys[2]] 
        }    


        // updated by: X S Inattar
        // updated on: August 25, 2012
        // begin
        //
        //def clip = []        
        def clip = ''
        
        if (player.contains('.mp4')) { 
            player = player.replace('.mp4', '')
            //clip = player.split(';')
            //clip = 'mp4:' + clip[4]
            clip = 'mp4:' + player
        }
        else if (player.contains('.flv')) {
            player = player.replace('.flv', '')
            //clip = player.split(';')
            //clip = clip[4]
            clip = player
        }
        //def contentUrl = "${player} playpath=${clip}"
        def contentUrl = "${stream_source} playpath=${clip}"
        // end
        
        def cacheKey = "CBS_${item.additionalInfo.PID}_${requestedQuality}"

        return new ContentURLContainer(contentUrl: contentUrl, thumbnailUrl: item.additionalInfo.thumbnailUrl, expiresImmediately: true, cacheKey : cacheKey)    
    }
    
    static void main(args) {
        CBS extractor = new  CBS()
		//for testing
       //http://www.cbs.com/shows/big_bang_theory/video/
	   //http://www.cbs.com/shows/csi/video/
	   //http://www.cbs.com/shows/48_hours/video/
        WebResourceContainer container = extractor.extractItems( new URL("http://www.cbs.com/shows/48_hours/video/"), 4)
		
		if ( container ){
        container.getItems().each {
            ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.MEDIUM)
            println result 
		}
        }   
    }
}