import java.text.SimpleDateFormat
import java.util.regex.Matcher

import org.apache.commons.lang.StringEscapeUtils
import org.restlet.*
import org.restlet.data.*
import org.restlet.engine.header.Header
import org.restlet.representation.*
import org.restlet.util.Series
import org.serviio.library.online.ContentURLContainer
import org.serviio.library.online.PreferredQuality
import org.serviio.library.online.WebResourceContainer
import org.serviio.library.online.WebResourceItem
import org.serviio.library.online.WebResourceUrlExtractor


/**
* WebResource extractor plugin for itv.com.
*
* @author Petr Nejedly
*
*/
class ITVPlayerExtractor extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^(?:https?://)?(?:www\\.)?itv\\.com/itvplayer/.*'
	final EPISODE_REXEG = '(?s)about="(.*?)".*?content="(.*?)".*?field-season-number.*?(\\d+).*?field-episode-number.*?(\\d+)'
	
	private static final httpClient = new Client(Protocol.HTTP)
	
	def videoId
	
	def getVideoId = { -> return videoId}
	def getRandomUUID = { -> return UUID.randomUUID().toString()}
	def soapMesage = """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:itv="http://schemas.datacontract.org/2004/07/Itv.BB.Mercury.Common.Types" xmlns:com="http://schemas.itv.com/2009/05/Common">
  <soapenv:Header/>
  <soapenv:Body>
    <tem:GetPlaylist>
      <tem:request>
        <itv:ProductionId>$getVideoId</itv:ProductionId>
        <itv:RequestGuid>$getRandomUUID</itv:RequestGuid>
        <itv:Vodcrid>
          <com:Id/>
          <com:Partition>itv.com</com:Partition>
        </itv:Vodcrid>
      </tem:request>
      <tem:userInfo>
        <itv:Broadcaster>Itv</itv:Broadcaster>
        <itv:GeoLocationToken>
          <itv:Token/>
        </itv:GeoLocationToken>
        <itv:RevenueScienceValue>ITVPLAYER.12.18.4</itv:RevenueScienceValue>
        <itv:SessionId/>
        <itv:SsoToken/>
        <itv:UserToken/>
      </tem:userInfo>
      <tem:siteInfo>
        <itv:AdvertisingRestriction>None</itv:AdvertisingRestriction>
        <itv:AdvertisingSite>ITV</itv:AdvertisingSite>
        <itv:AdvertisingType>Any</itv:AdvertisingType>
        <itv:Area>ITVPLAYER.VIDEO</itv:Area>
        <itv:Category/>
        <itv:Platform>DotCom</itv:Platform>
        <itv:Site>ItvCom</itv:Site>
      </tem:siteInfo>
      <tem:deviceInfo>
        <itv:ScreenSize>Big</itv:ScreenSize>
      </tem:deviceInfo>
      <tem:playerInfo>
        <itv:Version>2</itv:Version>
      </tem:playerInfo>
    </tem:GetPlaylist>
  </soapenv:Body>
</soapenv:Envelope>
	"""
		
	String getExtractorName() {
		return 'ITVPlayer (UK only)'
	}
	
	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}
	
	public int getVersion() {
		return 4
	}

	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
		String html = resourceUrl.getText()
				
		def titleMatcher = html =~ '(?s)<h1 class="title.*?>(.+?)<'
		assert titleMatcher.count == 1 && titleMatcher.hasGroup(), "Could not find item title. The URL is probably invalid or the plugin needs updating."
		String pageTitle = StringEscapeUtils.unescapeHtml(titleMatcher[0][1].trim())
		String webserver = resourceUrl.getProtocol() + "://" + resourceUrl.getHost()
		
		def containerStart = html.indexOf('class="catchup-container"')
		if(containerStart > -1) {
			html = html.substring(containerStart)
			html = removeHtml(html, 'rental-container')
			html = removeHtml(html, 'related_programmes')
			def episodeMatcher = html =~ EPISODE_REXEG
			
			List<WebResourceItem> items = []
			def itemsAdded = 0;
			def numberOfEpisodes = episodeMatcher.size()
			for( int i = 0; i < numberOfEpisodes && (maxItems == -1 || itemsAdded < maxItems); i++ ) {
				items << buildWebResourceItem(webserver, episodeMatcher, i)
				itemsAdded++
			}
				
			return new WebResourceContainer(title: pageTitle, items: items)
		} else {
			def episodeMatcher = html =~ EPISODE_REXEG
			assert episodeMatcher.size() == 1
			return new WebResourceContainer(title: pageTitle, items: [buildWebResourceItem(webserver, episodeMatcher, 0)])
		}
	}
	
	ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {
		String videoHtml = new URL(item.getAdditionalInfo()['videoUrl']).getText()
		def videoHtmlMatcher = videoHtml =~ '(?s)<param name="videoId" value="(.*?)"/>'
		assert videoHtmlMatcher.count == 1 && videoHtmlMatcher.hasGroup(), "Could not find videoId"
		String encodedVideoId = videoHtmlMatcher[0][1].trim()
		encodedVideoId = encodedVideoId.replaceAll("I_3","")
		encodedVideoId = encodedVideoId.replaceAll("_2f","/")
		encodedVideoId = encodedVideoId.replaceAll("_23","#")
		
		videoId = encodedVideoId
		assert videoId
		
		String description =  postSoap()
		if(description) {	
			//println description	
			def envelopeNode = new XmlParser().parseText(description)
			def playlistNode = envelopeNode.'s:Body'.'GetPlaylistResponse'.'GetPlaylistResult'.'Playlist'
			String expiryDate = playlistNode.'ExpiryDate'.text()?.trim()
			
			def mediaFilesNode = playlistNode.'VideoEntries'.'Video'.'MediaFiles'
			String mediaBase = mediaFilesNode.'@base'.text()
			List mediaFileNodeList = mediaFilesNode.'MediaFile'
			List sortedItems = mediaFileNodeList.sort { it.'@bitrate'.toInteger() }
			def mediaFilenode = findSuitableItem(sortedItems , requestedQuality )
			String playpath = mediaFilenode.'URL'.text()
			String contentUrl = "${mediaBase} swfurl=http://www.itv.com/mercury/Mercury_VideoPlayer.swf playpath=${playpath} swfvfy=true"
			String thumbnailUrl = playlistNode.'PosterFrame'.'URL'.text()
			return new ContentURLContainer( contentUrl: contentUrl, thumbnailUrl: thumbnailUrl, expiresOn: getExpiryDate(expiryDate), expiresImmediately:true, cacheKey: "ITV_PLAYER_${videoId}_${requestedQuality.toString()}")
		}
		log.debug("Stream description could not be obtained")
		return null
	}
	
	private def WebResourceItem buildWebResourceItem(String webserver, Matcher episodeMatcher, int i) {
		String videoUrl = webserver + episodeMatcher[i][1].trim()
		String relDate = new String(episodeMatcher[i][2].trim())
		String seriesNumber = episodeMatcher[i][3].trim()
		String episodeNumber = episodeMatcher[i][4].trim()
		String episodeTitle = "Series $seriesNumber Episode $episodeNumber"
		Date releaseDate = Date.parse("yyyy-MM-dd'T'HH:mm:ss", relDate)
		new WebResourceItem(title: episodeTitle, releaseDate: releaseDate, additionalInfo: ['videoUrl':videoUrl])
	}
	
	private def String removeHtml(String html, String startingText) {
		int pos = html.indexOf(startingText)
		if(pos > -1) html = html.substring(0, pos)
		return html
	}
	
	private def getExpiryDate(String expDate) {
		if(!expDate?.isEmpty()) {
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
			df.setTimeZone(TimeZone.getTimeZone("Europe/London"))
			return df.parse(expDate)			
		}
		null
	}
	
	private String postSoap() {
		Request request = new Request(Method.POST, "http://mercury.itv.com/PlaylistService.svc", new StringRepresentation(soapMesage.toString(), MediaType.TEXT_XML))
		request.setHostRef('mercury.itv.com')
		request.setReferrerRef('http://www.itv.com/mercury/Mercury_VideoPlayer.swf?v=1.6.479/[[DYNAMIC]]/2')
		
		Series<Header> requestHeaders = new Series<Header>(Header.class)
		requestHeaders.add('SOAPAction','http://tempuri.org/PlaylistService/GetPlaylist')
		request.getAttributes().put("org.restlet.http.headers", requestHeaders)
		Response response = httpClient.handle(request)
		if (!Status.SUCCESS_OK.equals(response.getStatus())) {			
			log.warn("Soap POST failed with status code " + response.getStatus().getCode())
			return null
		} else {			
       		return response.getEntity().getStream().getText()
		}
	}
		
	
	private def Node 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) {
		// this is just to test
		ITVPlayerExtractor extractor = new ITVPlayerExtractor()
		
		assert extractor.extractorMatches( new URL("https://www.itv.com/itvplayer/the-jeremy-kyle-show") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		
		WebResourceContainer container = extractor.extractItems( new URL("https://www.itv.com/itvplayer/the-jeremy-kyle-show"), 10)
		println container
		ContentURLContainer result = extractor.extractUrl(container.getItems()[2], PreferredQuality.MEDIUM)
		println result
		
		//WebResourceContainer rental = extractor.extractItems( new URL("https://www.itv.com/itvplayer/agatha-christie-s-poirot"), 10)
		//println rental
		
		//WebResourceContainer singleVideo = extractor.extractItems( new URL("https://www.itv.com/itvplayer/doors-open"), 10)
		//println singleVideo
		//ContentURLContainer svresult = extractor.extractUrl(singleVideo.getItems()[0], PreferredQuality.MEDIUM)
		//println svresult
		
	}
}