package com.wowza.wms.plugin.livestreamrecord;

import java.util.*;
import java.io.*;

import com.wowza.wms.module.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.application.*;
import com.wowza.wms.client.IClient;
import com.wowza.wms.stream.*;
import com.wowza.wms.stream.mediacaster.MediaStreamMediaCasterUtils;
import com.wowza.wms.vhost.StreamItem;
import com.wowza.wms.vhost.StreamList;
import com.wowza.wms.request.*;
import com.wowza.wms.plugin.integration.liverecord.*;

public class ModuleLiveStreamRecord  extends ModuleBase
{
	public static final int FORMAT_UNKNOWN = 0;
	public static final int FORMAT_FLV = 1;
	public static final int FORMAT_MP4 = 2;
	
	private Map<String, ILiveStreamRecord> recorders = new HashMap<String, ILiveStreamRecord>();
	private IApplicationInstance appInstance = null;
	
	public void recordStream(String streamName, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
	{
		IMediaStream stream = appInstance.getStreams().getStream(streamName);
		if (stream != null)
			recordStream(stream, format, append, outputPath, versionFile, startOnKeyFrame, recordData);
		else
			getLogger().warn("ModuleLiveStreamRecord.recordStream: Stream not found: "+streamName);
	}
	
	private void recordStream(IMediaStream stream, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
	{

		String streamName = stream.getName();
		
		// if a format was not specified then check the stream prefix and choose accordingly
		if (format == FORMAT_UNKNOWN)
		{
			format = FORMAT_FLV;
			String extStr = stream.getExt();
			if (extStr.equals("mp4"))
				format = FORMAT_MP4;
		}
		
		String params = "stream:"+streamName;
		params += " format:"+(format==FORMAT_MP4?"mp4":"flv");
		params += " append:"+append;
		if (outputPath != null)
			params += " outputPath:"+outputPath;
		else
		{
			File writeFile = stream.getStreamFileForWrite(stream.getName(), (format==FORMAT_MP4?MediaStream.MP4_STREAM_EXT:MediaStream.BASE_STREAM_EXT), stream.getQueryStr());
			params += " outputPath:"+writeFile.getAbsolutePath();
		}
		params += " versionFile:"+versionFile;
		params += " startOnKeyFrame:"+startOnKeyFrame;
		params += " recordData:"+recordData;
		
		getLogger().info("ModuleStreamRecord.startRecording: "+params);
		
		// create a stream recorder and save it in a map of recorders
		ILiveStreamRecord recorder = null;
		
		// create the correct recorder based on format
		if (format == FORMAT_MP4)
			recorder = new LiveStreamRecorderMP4();
		else
			recorder = new LiveStreamRecorderFLV();
		
		// add it to the recorders list
		synchronized (recorders)
		{
			ILiveStreamRecord prevRecorder = recorders.get(streamName);
			if (prevRecorder != null)
				prevRecorder.stopRecording();
			recorders.put(streamName, recorder);
		}
		
		// if you want to record data packets as well as video/audio
		recorder.setRecordData(recordData);
		
		// Set to true if you want to version the previous file rather than overwrite it
		recorder.setVersionFile(versionFile);
		
		// If recording only audio set this to false so the recording starts immediately
		recorder.setStartOnKeyFrame(startOnKeyFrame);
		
		// start recording
		recorder.startRecording(stream, outputPath, append);
	}
		
	public void startRecording(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		
		int format = FORMAT_UNKNOWN;
		boolean append = true;
		String outputPath = null;
		boolean versionFile = true;
		boolean startOnKeyFrame = true;
		boolean recordData = false;

		// Decode options if sent
		if (PARAM2 < params.size())
		{
			AMFDataObj dataObj = params.getObject(PARAM2);
			
			while(true)
			{
				if (dataObj == null)
					break;
				
				List<String> keys = dataObj.getKeys();
				Iterator<String> iter = keys.iterator();
				while(iter.hasNext())
				{
					String key = iter.next();
					
					try
					{
						if (key.equalsIgnoreCase("format"))
						{
							String value = dataObj.getString(key);
							if (value.equals("flv"))
								format = FORMAT_FLV;
							else if (value.equals("mp4"))
								format = FORMAT_MP4;
						}
						else if (key.equalsIgnoreCase("append"))
						{
							append = dataObj.getBoolean(key);
						}
						else if (key.equalsIgnoreCase("outputPath"))
						{
							outputPath = client.getAppInstance().decodeStorageDir(dataObj.getString(key));
						}
						else if (key.equalsIgnoreCase("versionFile"))
						{
							versionFile = dataObj.getBoolean(key);
						}
						else if (key.equalsIgnoreCase("startOnKeyFrame"))
						{
							startOnKeyFrame = dataObj.getBoolean(key);
						}
						else if (key.equalsIgnoreCase("recordData"))
						{
							recordData = dataObj.getBoolean(key);
						}
					}
					catch (Exception e)
					{
						getLogger().error("ModuleLiveStreamRecord.startRecording: Error processing param["+key+"]: "+e.toString());
					}
				}
				break;
			}
		}
		
		IApplicationInstance appInstance = client.getAppInstance();

		String streamTypeStr = "default";
		String tmpStr = client.getStreamType();
		if (tmpStr != null)
			streamTypeStr = tmpStr;
		
		boolean isLiveRepeaterEdge = false;
		while(true)
		{
			StreamList streamDefs = appInstance.getVHost().getStreamTypes();
			StreamItem streamDef = streamDefs.getStreamDef(streamTypeStr);
			if (streamDef == null)
				break;			
			isLiveRepeaterEdge = streamDef.getProperties().getPropertyBoolean("isLiveRepeaterEdge", isLiveRepeaterEdge);
			break;
		}
		
		if (isLiveRepeaterEdge)
			streamName = MediaStreamMediaCasterUtils.mapMediaCasterName(appInstance, client, streamName);
		
		IMediaStream stream = appInstance.getStreams().getStream(streamName);
		if (stream != null)
			recordStream(stream, format, append, outputPath, versionFile, startOnKeyFrame, recordData);
		else
			getLogger().warn("ModuleStreamRecord.startRecording: stream not found: "+streamName);
	}
	
	public String[] getRecorderNames()
	{
		List<String> retList = new ArrayList<String>();
		retList.addAll(recorders.keySet());
		return retList.toArray(new String[retList.size()]);
	}
	
	public void getPublishStreamNames(IClient client, RequestFunction function, AMFDataList params)
	{
		AMFDataArray ret = new AMFDataArray();
		
		getLogger().info("ModuleStreamRecord.getPublishStreamNames");

		List<String> namesList = client.getAppInstance().getPublishStreamNames();
		Iterator<String> iter = namesList.iterator();
		int index = 0;
		while(iter.hasNext())
		{
			String streamName = iter.next();
			ret.add(new AMFDataItem(streamName));
			getLogger().info("  publishName["+index+"]: "+streamName);
			index++;
		}
		
		sendResult(client, params, ret);
	}

	public String stopRecording(String streamName)
	{
		ILiveStreamRecord recorder = null;
		synchronized (recorders)
		{
			recorder = recorders.remove(streamName);
		}
		
		String outputPath  = null;
		if (recorder != null)
		{
			// grab the current path to the recorded file
			outputPath = recorder.getFilePath();
			
			// stop recording
			recorder.stopRecording();
		}
		else
			getLogger().warn("ModuleStreamRecord.stopRecording: stream recorder not found: "+streamName);
		
		return outputPath;
	}
	
	public void stopRecording(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		getLogger().info("ModuleStreamRecord.stopRecording: "+streamName);
		
		ILiveStreamRecord recorder = null;
		synchronized (recorders)
		{
			recorder = recorders.remove(streamName);
		}
		
		if (recorder != null)
		{
			// grab the current path to the recorded file
			String outputPath = recorder.getFilePath();
			
			// stop recording
			recorder.stopRecording();
		}
		else
			getLogger().warn("ModuleStreamRecord.stopRecording: stream recorder not found: "+streamName);
	}
	
	public void onAppStart(IApplicationInstance appInstance)
	{
		this.appInstance = appInstance;
		
		getLogger().info("ModuleStreamRecord.onAppStart");
	}
	
	public void onAppStop(IApplicationInstance appInstance)
	{
		getLogger().info("ModuleStreamRecord.onAppStop");
		
		// cleanup any recorders that are still running
		synchronized (recorders)
		{
			Iterator<String> iter = recorders.keySet().iterator();
			while(iter.hasNext())
			{
				String streamName = iter.next();
				ILiveStreamRecord recorder = recorders.get(streamName);
				recorder.stopRecording();
				getLogger().info("  stopRecording: "+streamName);
			}
			recorders.clear();
		}
	}
}
