package eu.dnetlib.social.consumer;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.common.collect.Lists;
import com.google.gdata.client.media.ResumableGDataFileUploader;
import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.media.MediaFileSource;
import com.google.gdata.data.media.mediarss.MediaCategory;
import com.google.gdata.data.media.mediarss.MediaDescription;
import com.google.gdata.data.media.mediarss.MediaKeywords;
import com.google.gdata.data.media.mediarss.MediaTitle;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.YouTubeMediaGroup;
import com.google.gdata.data.youtube.YouTubeNamespace;
import com.google.gdata.util.AuthenticationException;

import eu.dnetlib.social.SocialException;
import eu.dnetlib.social.SocialUploadable;
import eu.dnetlib.social.YouTubeObject;

/**
 * @author alessia
 * 
 */
public class YouTubePublisher extends AbstractSocialUploader {
	/** YouTube endpoint. */
	private YouTubeService youTubeService;
	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(YouTubePublisher.class);
	/**
	 * The URL used to resumable upload TODO: check the difference between this URL and
	 * http://uploads.gdata.youtube.com/feeds/api/users/default/uploads: Can I use resumable upload for VideoFeeds too?
	 */
	private String uploadURL = "http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads";
	/** Time interval at which upload task will notify about the progress. */
	private int progressUpdateInterval = 10000;
	private SocialProgressListener progressListener;
	/** Max size for each upload chunk. */
	private int chunkSize = 200000;
	/** Target directory of the downloaded video files. */
	private String targetDir;
	private ResumableGDataFileUploader uploader;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.social.AbstractSocialUploader#authenticate(java.lang.String, java.lang.String)
	 */
	@Override
	protected void authenticate(final String userName, final String password) {
		try {
			youTubeService.setUserCredentials(userName, password);
		} catch (AuthenticationException e) {
			log.debug(e);
			throw new SocialException("Cannot authenticate user " + userName + " to YouTube. Please check password", e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.social.AbstractSocialUploader#batchUpload(java.util.Collection)
	 */
	@Override
	protected void batchUpload(final Collection<SocialUploadable> objects) {
		throw new UnsupportedOperationException("Publishing a collection of objects is not supported.");

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.social.AbstractSocialUploader#upload(eu.dnetlib.social.SocialObject)
	 */
	@Override
	protected void upload(final SocialUploadable object) {
		this.upload((YouTubeObject) object);
	}

	protected void upload(final YouTubeObject object) {
		try {
			File downloaded = this.download(new URL(object.getDownloadURL()), object.getName());
			log.debug("File: " + object.getName() + " downloaded in " + downloaded.getAbsolutePath());
			//now it's time to create the video entry
			VideoEntry e = this.createVideoEntry(downloaded, object);
			//now let us upload it!
			this.startResumableUpload(e);
		} catch (IOException e) {
			throw new SocialException(e);
		}
	}

	public VideoEntry createVideoEntry(final File f, final YouTubeObject object) {
		MediaFileSource source = new MediaFileSource(f, object.getMimeType());
		source.setName(object.getName());
		VideoEntry entry = new VideoEntry();
		entry.setTitle(new PlainTextConstruct(object.getTitle()));
		entry.setMediaSource(source);
		YouTubeMediaGroup mg = entry.getOrCreateMediaGroup();
		mg.setTitle(new MediaTitle());
		mg.getTitle().setPlainTextContent(object.getTitle());
		mg.setDescription(new MediaDescription());
		mg.getDescription().setPlainTextContent(object.getDescription());
		mg.setKeywords(new MediaKeywords());
		mg.getKeywords().addKeyword("ISTI-Hope-test");
		mg.getKeywords().addKeywords(Lists.newArrayList(object.getKeywords()));
		for (String cat : object.getCategories()) {
			mg.getCategories().add(new MediaCategory(YouTubeNamespace.CATEGORY_SCHEME, cat));
		}
		return entry;
	}

	public File download(final URL downloadURL, final String videoName) throws IOException {
		String targetFile = this.targetDir + File.separator + videoName;
		File f = new File(targetFile);
		if (f.exists())
			log.debug("File already downloaded, using that!");
		else {
			log.debug("Starting download: Video " + videoName + " in " + targetFile);
			long startTime = System.currentTimeMillis();
			FileUtils.copyURLToFile(downloadURL, f);
			long endTime = System.currentTimeMillis();
			log.debug("Download ended in " + (endTime - startTime) + " ms");
		}
		return f;
	}

	public void startResumableUpload(final VideoEntry entry) {
		try {
			setUploader(new ResumableGDataFileUploader.Builder(this.getYouTubeService(), new URL(this.uploadURL), (MediaFileSource) entry
					.getMediaSource(), entry).title(entry.getTitle().getPlainText()).trackProgress(this.progressListener, this.progressUpdateInterval)
					.chunkSize(this.chunkSize).build());
			uploader.start();
		} catch (Exception e) {
			e.printStackTrace();
			throw new SocialException(e);
		}
	}

	public YouTubeService getYouTubeService() {
		return youTubeService;
	}

	public void setYouTubeService(final YouTubeService youTubeService) {
		this.youTubeService = youTubeService;
	}

	@Override
	public String getUploadURL() {
		return uploadURL;
	}

	@Override
	public void setUploadURL(final String uploadURL) {
		this.uploadURL = uploadURL;
	}

	public int getProgressUpdateInterval() {
		return progressUpdateInterval;
	}

	public void setProgressUpdateInterval(final int progressUpdateInterval) {
		this.progressUpdateInterval = progressUpdateInterval;
	}

	public int getChunkSize() {
		return chunkSize;
	}

	public void setChunkSize(final int chunkSize) {
		this.chunkSize = chunkSize;
	}

	public void setProgressListener(final SocialProgressListener progressListener) {
		this.progressListener = progressListener;
	}

	public SocialProgressListener getProgressListener() {
		return progressListener;
	}

	public String getTargetDir() {
		return targetDir;
	}

	public void setTargetDir(final String targetDir) {
		this.targetDir = targetDir;
	}

	public void setUploader(final ResumableGDataFileUploader uploader) {
		this.uploader = uploader;
	}

	public ResumableGDataFileUploader getUploader() {
		return uploader;
	}

}
