import groovy.json.JsonSlurper

import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.Spliterators.AbstractDoubleSpliterator
import java.util.concurrent.ForkJoinPool.DefaultForkJoinWorkerThreadFactory

import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.serviio.util.*

/**
 * WebResource extractor plugin for tunein.com.
 * 
 * @author Petr Nejedly
 *
 */
class TuneIn extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^(?:https?://)?(?:www\\.)?tunein\\.com/(radio/|search/\\?query=).+'
	
	String getExtractorName() {
		return 'Tune In'
	}
	
	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}
	
	int getVersion() {
		8
	}
	
	String urlGetText(URL url) {
		String html = url.getText()
		for (int i=0; i<10; ++i) {
			//sometimes redirected to tune in beta site???
			if (html.startsWith("<html><head><title>Object moved</title>")) {
				Thread.currentThread().sleep(500);
				html = url.getText()
			} else {
				return html;
			}
		}
		return html
	}

	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
		String html = urlGetText(resourceUrl)
		
		def titleMatcher = html =~ '(?s)<title.*?>([^<|]+)'
		String pageTitle = titleMatcher[0][1].trim()
		//println "pagetitle = "+pageTitle
		
		def stations = html.split(/(?s)<h2.*?>\s*/).find { it.startsWith("Stations") }
		
		
		List<WebResourceItem> items = []
		String rowDelim = '<div class="row guide-item';
		def k = stations.indexOf(rowDelim);
		if (k != -1) {
			def rows = stations.substring(k+10).split(rowDelim, maxItems);
			rows.each {
				String url = null;
				url: {
					def urlMatcher = it =~ '(?s)<a .*?href="([^"]+)';
					//println(urlMatcher.count)
					url = urlMatcher[0][1]
				}
				String title = null;
				title: {
					def stationMatcher = it =~ '(?s)guideItemTitleSingleLine[^>]+>([^<]+)'
					title = stationMatcher[0][1]
					//println title
				}
				if (url.contains("-s")) {
					WebResourceItem item = new WebResourceItem(title: title, additionalInfo: ['stationUrl':url])
					items << item
				}
			}
		}
		
		return new WebResourceContainer(title: pageTitle, items: items)
	}
	
	ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {		
		String stationTitle = item.title
		String stationUrl = item.getAdditionalInfo()['stationUrl']
		assert stationUrl != null
		
		def idMatcher = stationUrl =~ '-(s\\d+)';
		def id = idMatcher[0][1];
		def url = "https://opml.radiotime.com/Tune.ashx?id=${id}&render=json&formats=mp3,aac,ogg&partnerId=RadioTime";
		def json = new JsonSlurper().parse(new URL(url))
		//println(json)
		Map jsonResult = (Map) json;
		List streams = jsonResult['body']
		streams = streams.findAll { it -> it['element'] == 'audio' && it['url'].startsWith('http') }
		streams = streams.sort { it -> it['bitrate'].toInteger() }
		Map selectedStream = streams.last()
		def streamUrl = selectedStream.url
		if (selectedStream['playlist_type'])
			streamUrl = getUrlFromPlaylist(new URL(selectedStream.url));
 
		return new ContentURLContainer(fileType: MediaFileType.AUDIO, contentUrl: streamUrl, live: true)
	}
	
	private String getStreamUrl(Map jsonResult) {
		List streams = jsonResult['Streams']
		// only get streams that are represented as playlists and are live streams
		streams = streams.findAll { it -> it['Type'] == 'Live' && ['MP3','Windows','AAC','Flash'].contains(it['MediaType']) && it['Url'].indexOf('adType') == -1 }
		if( streams.size() > 0 ) {
			// ignore quality, audio bitrate is too low to make any difference, deliver the best available
			streams = streams.sort { it -> it['Bandwidth'].toInteger() }
			Map selectedStream = streams.last()
			String streamUrl = selectedStream['Url']
			boolean hasPlaylist = Boolean.valueOf (selectedStream['HasPlaylist'])
			if(hasPlaylist && streamUrl.startsWith("http")) {
				return getUrlFromPlaylist(new URL(streamUrl))
			} else {
				return streamUrl
			}
		} else {
			return null
		}
	}
	
	/**
	 * Supports m3u playlists ATM
	 */
	protected getUrlFromPlaylist(URL playlistUrl) {
		assert playlistUrl != null
		
		String playlist = playlistUrl.getText()
		if(playlist.toLowerCase().startsWith("<asx")) {
			// asx playlists not supported
			return null			
		} else {
			// assume m3u playlist
		    List lines = playlist.readLines()
			String url = lines[0]
			if(url.startsWith ('[playlist]') ) {
				for(String line : lines) {
					if(line.startsWith('File1')) {
						url = line.substring(line.indexOf ('=') + 1).trim()
						break
					}
				}
			}
			if (url.endsWith('.')) {
				url = url.substring(0, url.size()-1)
			}
			if(url.indexOf('.m3u') > -1 || url.indexOf('.pls') > -1) {
				return getUrlFromPlaylist(new URL(url))
			}
			return url
		}
	}
	
	static void main(args) {
		// this is just to test
		TuneIn extractor = new TuneIn()
		if (args.length == 1) {
			WebResourceContainer container = extractor.extractItems(new URL(args[0]), 5)
			println container
			container.getItems().each {
				ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.MEDIUM)
				println result
			}
			System.exit(0);
		}
				
		assert extractor.extractorMatches( new URL("https://tunein.com/radio/London-United-Kingdom-r100780/?qlc=1") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		
		WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/radio/Capital-London-958-s16534/"), -1) // with embedded streams
		//WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/radio/London-United-Kingdom-r100780/?qlc=1"), -1) // with embedded streams
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/search/05301/"), -1) // with external streams
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/Christmas-Holiday-Favorites-s257618/"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/Christmas-Radio-s249952/"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/search/?query=kiss"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/search/?query=doowop"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/radio/Radio-Mizrahit-s171349/"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/radio/RussianFM-s118244/"), -1)
		//WebResourceContainer container = extractor.extractItems( new URL("https://tunein.com/radio/Non-Stop-Radio-1030-s14201/"), -1)
		println container
		
        container.getItems().each {
            ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.MEDIUM)
            println result
        }
	}
}
