package com.ericsson.uecontrol.core.behaviour;

import android.media.MediaPlayer;

import com.ericsson.uecontrol.core.UeBehaviour;
import com.ericsson.uecontrol.core.util.Configurator.Configurable;

import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Created by ezivkoc on 2014-08-05.
 */
public class UeBehaviourVideoStreaming extends UeBehaviour implements MediaPlayer.OnErrorListener{
    private static final Logger log = Logger.getLogger(UeBehaviourVideoStreaming.class);
    protected static final Pattern YOUTUBE_VIDEOURL_PATTERN = Pattern.compile(".*youtube.*[&\\?]v=([\\w_-]*)[&#]?");
    protected static final Pattern YOUTUBE_VIDEOID_PATTERN  = Pattern.compile("^[\\w_-]*$");
    protected static final String YOUTUBE_FEED_URL = "http://gdata.youtube.com/feeds/api/videos/";
    protected static final String YOUTUBE_VIDEO_URL = "https://www.youtube.com/watch?v=";

    @Configurable("Streaming URL")
    private String url = "I9L79_xEQ-U";

    private transient Exception playbackException;

    @Override
    protected void execute() throws Exception {
        MediaPlayer mp = new MediaPlayer();
        playbackException = null;
        mp.setOnErrorListener(this);

        try {
            String videoUrl = url.trim();
            if (isYouTubeUrl(videoUrl)) {
                log.debug("Detected youtube video url: " + videoUrl);

                String videoId = getYoutubeVideoId(videoUrl);
                videoUrl = getYoutubeFileUrl(videoId);
                if(videoUrl == null) {
                    log.warn("Unable to parse Youtube URL, using fallback rts url");
                    videoUrl = getYoutubeRtsUrl(videoId);
                }
            }
            else {
                log.debug("Video url: " + videoUrl);
            }
            mp.setDataSource(videoUrl);
            mp.prepare();
            mp.start();

            Thread.sleep(UeBehaviourSleep.SLEEP_PERIOD);
            while (mp.isPlaying() && playbackException == null) {
                super.setProgress((float) mp.getCurrentPosition() / mp.getDuration());

                if (super.stopExecution()) break;
                Thread.sleep(UeBehaviourSleep.SLEEP_PERIOD);
            }
        }catch(Exception e){
            throw e;
        }finally {
            log.debug("Stopping playback");
            mp.stop();
            mp.reset();
            mp.release();
        }
        if(playbackException != null)
            throw playbackException;
    }

    protected static boolean isYouTubeUrl(String url){
        return YOUTUBE_VIDEOURL_PATTERN.matcher(url).matches() ||
                YOUTUBE_VIDEOID_PATTERN.matcher(url).matches();
    }
    protected static String getYoutubeVideoId(String url){
        if(YOUTUBE_VIDEOID_PATTERN.matcher(url).matches())
            return url;

        Matcher match = YOUTUBE_VIDEOURL_PATTERN.matcher(url);
        if(match.find())
            return match.group(1);
        return null;
    }
    public static String getYoutubeFileUrl(String videoId) throws IOException{
        URL url = new URL(YOUTUBE_VIDEO_URL + videoId);
        URLConnection conn = url.openConnection();
        conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1;) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36");
        conn.connect();

        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while((line = in.readLine()) != null){
                if(line.contains("ytplayer.config")){
                    //log.debug("html: " + line);

                    Matcher matchUrlMap = Pattern.compile("\"url_encoded_fmt_stream_map\": \"(.*?)\"").matcher(line);
                    if(matchUrlMap.find()) {
                        String url_map = matchUrlMap.group(1);
                        log.debug("url_map: " + url_map);

                        Matcher matchStream = Pattern.compile("[^,]+").matcher(url_map);
                        while (matchStream.find()){
                            String stream = URLDecoder.decode(matchStream.group(), "UTF-8");
                            log.debug("stream: " + stream);

                            HashMap<String, String> values = new HashMap<String, String>();
                            for(String tag : stream.split("\\\\u0026")){
                                String[] key = tag.split("=", 2);
                                if(key.length > 1) {
                                    log.debug("    tag: " +key[0]+ "=" +key[1]);
                                    values.put(key[0], key[1]);
                                }
                            }

                            if(values.containsKey("sig") || values.containsKey("s")) {
                                log.warn("Scrambled Youtube URL not supported");
                                return null;
                            }
                            else if(values.get("type").contains("video/mp4")){
                                return values.get("url");
                            }
                        }
                    }
                }
            }
            in.close();
        } finally{
            if(in != null)
                try{ in.close(); }catch (IOException e){}
        }
        return null;
    }
    public static String getYoutubeRtsUrl(String videoId) throws DocumentException, MalformedURLException {
        URL feedUrl = new URL(YOUTUBE_FEED_URL + videoId);
        Document document = new SAXReader().read(feedUrl);
        Attribute attr = (Attribute)document.selectSingleNode( "//media:content[@yt:format='6']/@url" );
        if(attr != null){
            return attr.getValue();
        }
        return null;
    }



    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        playbackException = new Exception(
                "Playback error: "+getErrorString(what)+", "+getErrorString(extra));
        return false;
    }
    private String getErrorString(int errorCode){
        switch(errorCode){
            case 0x00000001: return "MEDIA_ERROR_UNKNOWN";
            case 0x00000064: return "MEDIA_ERROR_SERVER_DIED";
            case 0x000000c8: return "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK";
            case 0xfffffc11: return "MEDIA_ERROR_MALFORMED";
            case 0xfffffc14: return "MEDIA_ERROR_IO";
            case 0xfffffc0e: return "MEDIA_ERROR_UNSUPPORTED";
            case 0xffffff92: return "MEDIA_ERROR_TIMED_OUT";
        }
        return "UNKNOWN_ERROR_CODE("+errorCode+")";
    }



    @Override
    public String getName() {
        return "Video Streaming";
    }

    @Override
    public String toString() {
        return "Will stream video from: "+url;
    }
}
