/* 
 * Copyright 2015 by AVM GmbH <info@avm.de>
 *
 * This software contains free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License ("License") as 
 * published by the Free Software Foundation  (version 3 of the License). 
 * This software is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copy of the 
 * License you received along with this software for more details.
 */

package de.avm.android.fritzapp.gui;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.UnknownHostException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import de.avm.android.fritzapp.R;
import de.avm.android.fritzapp.com.ComSettingsChecker;
import de.avm.android.fritzapp.com.DataHub;
import de.avm.android.fritzapp.util.EnvironmentHelper;
import de.avm.android.fritzapp.util.OnClickCallNumber;
import de.avm.android.tr064.Tr064Capabilities;
import de.avm.android.tr064.exceptions.SslErrorException;
import de.avm.android.tr064.model.Call;
import de.avm.android.tr064.model.ContactNumber;
import de.avm.android.tr064.model.ContactPath;
import de.avm.android.tr064.model.IContactable;
import de.avm.android.tr064.net.GzipHttpRequestInterceptor;
import de.avm.android.tr064.net.GzipHttpResponseInterceptor;
import de.avm.android.tr064.net.SoapSSLClientFactory;
import de.avm.android.tr064.soap.deviceconfig.CreateUrlSid;
import de.avm.fundamentals.logger.FileLog;
import de.usbi.android.util.adapter.ArrayAdapterExt;

/* GUI for Call Details (one call of a calllist) */
public class CallDetailsActivity extends Activity
{
	private static final String TAG = "CallDetailsActivity";
	public static final String EXTRA_CALL_DATA = "CALL_DATA";
	public static final String EXTRA_CALL_IGNORE_PORTS = "CALL_IGNORE_PORTS";

	public enum PathStatus { IDLE, LOADING, PLAYING };
	
	private ListView mListView = null;
	private Call mCall = null;
	private boolean mTam = false;
	private boolean mFax = false;
	private LoadPathTask mLoaderTask = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.calllog_details);

		Intent intentCall = getIntent(); 
		mCall = intentCall.getParcelableExtra(EXTRA_CALL_DATA);
		if (mCall.hasPath())
		{
			if (mCall.isTamPort())
			{
				mTam = true;
			}
			else if (mCall.isFaxPort())
			{
				try
				{
					// test if PDF viewer available
					Intent intent = new Intent(Intent.ACTION_VIEW);
					intent.setDataAndType(Uri.parse("file:///fax.pdf"),
							"application/pdf");
					mFax = (intent.resolveActivity(getPackageManager()) != null);
				}
				catch(Exception e)
				{
                    FileLog.w(TAG, e.getMessage(), e);
				}
			}
		}
		
		ImageView type = (ImageView) findViewById(R.id.TypeIcon);
		type.setImageDrawable(mCall.getIcon(this));

		TextView name = (TextView) findViewById(R.id.CallLogEntryTitle);
		name.setText(mCall.getLabel(this));

		String infoText = mCall.getPrettyDateFull(getBaseContext());
		if ((mCall.getType() == Call.CALL_TYPE.MISSED) ||
				(mCall.getType() == Call.CALL_TYPE.REJECTED))
		{
			if (mCall.getCount() > 1)
				infoText += "\n" +  mCall.getPrettyCount(getBaseContext());
		}
		else if (mCall.getDuration() > 0)
		{
			infoText += "\n" +  mCall.getPrettyDuration(getBaseContext());
		}
		String externPort = (intentCall.getBooleanExtra(EXTRA_CALL_IGNORE_PORTS, false)) ?
				null : mCall.getExternPort().getLabelForExternPort(this);
		String internNumber = mCall.getInternNumber();
		if (!TextUtils.isEmpty(externPort))
		{
			infoText += "\n" + externPort;
			if (internNumber.length() > 0) infoText += ": " + internNumber;
		}
		else if (!TextUtils.isEmpty(internNumber)) infoText += "\n" + internNumber;
		TextView info = (TextView) findViewById(R.id.CallLogEntryInfo);
		info.setText(infoText);
		
		mListView = (ListView)findViewById(R.id.ContactNumbers);
		mListView.setSelector(R.drawable.list_selector_background);
		mListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
		{
	        public void onItemClick(AdapterView<?> parent, View view, int position, long id)
	        {
	            onListItemClick((ListView)parent, view, position, id);
	        }
		});
        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
        {
            public boolean onItemLongClick(AdapterView<?> parent, View view,
                    int position, long id)
            {
                return onListItemLongClick((ListView)parent, view, position, id);
            }
        });
		mListView.setAdapter(new CallListAdapter());
	}

	@Override
	protected void onPause()
	{
		super.onPause();
		if (mLoaderTask != null) mLoaderTask.release();
	}		

    private void onListItemClick(ListView listView, View view, int position, long id)
    {
    	IContactable item = (IContactable)listView.getItemAtPosition(position);
    	if (item != null)
    	{
	    	if (ContactNumber.class.equals(item.getClass()))
	    	{
	    		new OnClickCallNumber(mCall.getPartnerNumber()).onClick(view);
	    	}
	    	else if (ContactPath.class.equals(item.getClass()))
	    	{
	    		if (mLoaderTask == null)
	    		{
	    			if (mTam)
	    				mLoaderTask = (LoadPathTask)new LoadTamPathTask()
	    					.execute(mCall.getPath());
	    			else if (mFax)
	    				mLoaderTask = (LoadPathTask)new LoadFaxPathTask()
	    					.execute(mCall.getPath());
	    		}
	    		else if (mLoaderTask.getClass().equals(LoadTamPathTask.class))
	    		{
	    			mLoaderTask.release();
	    		}
	    	}
    	}
    }
    
    private boolean onListItemLongClick(ListView listView, View view, int position, long id)
    {
		IContactable item = (IContactable)listView.getItemAtPosition(position);
    	if ((item != null) && item.getClass().equals(ContactNumber.class))
    	{
    		new OnClickCallNumber(item.getAddress()).onLongClick(view);
    		return true;
    	}
    	
    	return false;
    }

	private void onPathStatus(final PathStatus newStatus)
	{
		runOnUiThread(new Runnable()
		{
			public void run()
			{
				// update item control
				int action = 0;
				if (mTam)
					action = (newStatus == PathStatus.IDLE) ?
						R.drawable.tam_play : R.drawable.tam_stop;
				else if (mFax)
					action = R.drawable.call_incoming_fax;
				else
					return;
					

				for (int ii = 0; ii < mListView.getChildCount(); ii++)
				{
					View child = mListView.getChildAt(ii);
					if (ContactPath.class.equals(child.getTag()))
					{
						ImageView actionView = (ImageView)child.findViewById(R.id.ActionIcon);
						View progressView = child.findViewById(R.id.Progress);
						if (newStatus == PathStatus.LOADING)
						{
							actionView.setVisibility(View.INVISIBLE);
							progressView.setVisibility(View.VISIBLE);
						}
						else
						{
							actionView.setImageResource(action);
							actionView.setVisibility(View.VISIBLE);
							progressView.setVisibility(View.INVISIBLE);						}
						break;
					}
				}
			}
		});
	}
    
	private class CallListAdapter extends ArrayAdapterExt<IContactable>
	{
		public CallListAdapter()
		{
			String number = mCall.getPartnerNumber(); 
			if (!TextUtils.isEmpty(number))
				addEntry(new ContactNumber(number));
			
			if (mTam)
			{
				addEntry(new ContactPath(mCall.getPath(), ContactPath.Type.TAM));
				setVolumeControlStream(AudioManager.STREAM_MUSIC);
			}
			else if (mFax)
			{
				addEntry(new ContactPath(mCall.getPath(), ContactPath.Type.FAX));
			}
		}

		@Override
		public View populateView(final IContactable item, View view, ViewGroup viewGroup)
		{
    		if (item.getClass().equals(ContactNumber.class))
    		{
    			if ((view == null) || !ContactNumber.class.equals(view.getTag()))
    			{
    				view = View.inflate(getBaseContext(),
    						R.layout.t_contactnumberlistitem, null);
    				view.setTag(ContactNumber.class);
    			}
    			TextView numberType = (TextView)view.findViewById(R.id.Action);
				if (!mCall.isPartnerNameEmpty())
					numberType.setText(String.format(
							getResources().getString(R.string.contact_details_callX),
							mCall.getPartnerName()));
					
				else
					numberType.setText(R.string.call_log_call_now);
    			((TextView)view.findViewById(R.id.Target))
						.setText(item.getAddressDisplay());
    		}
    		else if (item.getClass().equals(ContactPath.class))
    		{
    			if ((view == null) || !ContactPath.class.equals(view.getTag()))
    			{
    				view = View.inflate(getBaseContext(),
    						R.layout.t_contactpathlistitem, null);
    				view.setTag(ContactPath.class);
    			}
    			String fmt = "%s";
    			if (mTam)
    			{
    				fmt = getString(R.string.contact_details_tamX);
    			}
    			else if (mFax)
    			{
    				((ImageView)view.findViewById(R.id.ActionIcon))
    						.setImageResource(R.drawable.call_incoming_fax);
    				fmt = getString(R.string.contact_details_faxX);
    			}
    			
    			((TextView)view.findViewById(R.id.Action)).setText(String.format(fmt,
						item.getTypeDisplay(getBaseContext())));
    		}

			return view;
		}
	}

	private abstract class LoadPathTask extends AsyncTask<String, Integer, Exception>
	{
		protected abstract FileOutputStream createOutputFile() throws Exception;

		public abstract void release();
		
		@Override
		protected Exception doInBackground(String... params)
		{
	    	String host = "";
	    	int port = 0;
	    	String url = "";
	    	OutputStream fileOutput = null;
	    	InputStream download = null;
			try
			{
				// download to file
				host = ComSettingsChecker.getLocationHost();
		    	boolean noSsl = SettingsTestActivity.isNoHttps(CallDetailsActivity.this);
				port = (noSsl) ? ComSettingsChecker.getLocationPort() :
						ComSettingsChecker.getLocationSslPort();
				if ((port < 1) || TextUtils.isEmpty(host)) throw new UnknownHostException();
				url = ((noSsl) ? "http://" : "https://") +
				host + ":" + Integer.toString(port) + params[0];

				DataHub.SoapCredentials soapCredentials =
						new DataHub.SoapCredentials(CallDetailsActivity.this);
				
				DefaultHttpClient client = SoapSSLClientFactory.getClientWithDigestAuth(port,
						soapCredentials.getUsername(), soapCredentials.getPassword(),
						soapCredentials.getPinningStore());
				client.addRequestInterceptor(new GzipHttpRequestInterceptor());
				client.addResponseInterceptor(new GzipHttpResponseInterceptor());

				Tr064Capabilities capabilities = ComSettingsChecker.getTr064Capabilities();
				if ((capabilities != null) &&
						capabilities.has(Tr064Capabilities.Capability.SID_FOR_URLS))
				{
					// urls mit ssid
					url += "&" + new CreateUrlSid(soapCredentials).getQualifiedResult();
				}
				
				HttpResponse response = client.execute(new HttpGet(url));
				if (response.getStatusLine().getStatusCode() >= 300)
					throw new HttpResponseException(
							response.getStatusLine().getStatusCode(),
							response.getStatusLine().getReasonPhrase());
				HttpEntity entity = response.getEntity();
				if (entity != null)
				{
					fileOutput = createOutputFile();
					download = entity.getContent();
					
					byte[] buffer = new byte[8 * 1024];
					for(int read = download.read(buffer); read > -1; read = download.read(buffer))
						fileOutput.write(buffer, 0, read);
				}
				else throw new HttpResponseException(response.getStatusLine().getStatusCode(),
						"HTTP response has no content.");

			}
			catch(Exception e)
			{
				FileLog.e(TAG, String.format("Could not download and save from %s:%d to %s",
                        host, port, url), e);
				return e;
			}
			finally
			{
				if (download != null) try { download.close(); } catch(Exception e) {}
				if (fileOutput != null) try { fileOutput.close(); } catch(Exception e) {}
			}
			return null;
		}
	}

	private class LoadTamPathTask extends LoadPathTask
			implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener
	{
		private static final String FILE_NAME = "tam-message";
		private boolean doneDownload = false;
		private MediaPlayer mMediaplayer = null;

		@Override
		public void release()
		{
			onPathStatus(PathStatus.IDLE);
			if (mLoaderTask == this) mLoaderTask = null;
			MediaPlayer mp = mMediaplayer; 
			if (mp != null)
			{
				mMediaplayer = null;
				if (mp.isPlaying()) mp.stop();
				mp.release();
			}
			deleteFile(FILE_NAME);
		}
		
		@SuppressLint("WorldReadableFiles")
		@Override
		protected FileOutputStream createOutputFile()
			throws Exception
		{
			deleteFile(FILE_NAME);
			return openFileOutput(FILE_NAME, Context.MODE_WORLD_READABLE);
		}

		@Override
		protected void onPreExecute ()
		{
			doneDownload = false;
			mMediaplayer = new MediaPlayer();
			mMediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			mMediaplayer.setOnErrorListener(this);
			mMediaplayer.setOnCompletionListener(this);

			onPathStatus(PathStatus.LOADING);
		}

		@Override
		protected Exception doInBackground(String... params)
		{
			Exception error = super.doInBackground(params);

			if ((error == null) && (mLoaderTask == this))
			{
				doneDownload = true;
				onPathStatus(PathStatus.PLAYING);
				Uri uri = Uri.fromFile(new File(getFilesDir(), FILE_NAME));
				try
				{
					// play file
					mMediaplayer.setDataSource(getApplicationContext(), uri);
					mMediaplayer.prepare();
					mMediaplayer.start();
				}
				catch(Exception e)
				{
					FileLog.e(TAG, "Could not play " + uri, e);
					return e;
				}
			}
			
			return error;
		}
		
		@Override
		protected void onPostExecute (Exception error)
		{
			super.onPostExecute(error);

			if ((error != null) && (mLoaderTask == this))
			{
				onPathStatus(PathStatus.IDLE);
				
				String message;
				if (SslErrorException.isSslError(error))
					message = SslErrorException.getDisplayMessage(
							CallDetailsActivity.this, error);
				else
					message = getString((doneDownload) ?
							R.string.error_tam_mediaplayer :
							R.string.soap_tranfer_failed);
				release();
				TextDialog.createOk(CallDetailsActivity.this, message,
						android.R.drawable.ic_dialog_alert).show();
			}
		}

		public boolean onError(final MediaPlayer mp, int what, int extra)
		{
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					if ((mLoaderTask == LoadTamPathTask.this) &&
							(mMediaplayer == mp))
					{
						release();
						TextDialog.createOk(CallDetailsActivity.this,
								getString(R.string.error_tam_mediaplayer),
								android.R.drawable.ic_dialog_alert).show();
					}
				}
			});
			return true;
		}

		public void onCompletion(final MediaPlayer mp)
		{
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					if ((mLoaderTask == LoadTamPathTask.this) &&
							(mMediaplayer == mp))
						release();
				}
			});
		}
	}

	private class LoadFaxPathTask extends LoadPathTask
	{
		private static final String FILE_NAME = "fax.pdf";
		private Uri mUriDownload = null;

		@SuppressLint("WorldReadableFiles")
		@Override
		protected FileOutputStream createOutputFile() throws Exception
		{
			FileOutputStream stream = createExternalOutputFile();
			if (stream == null)
			{
				// save in internal storage
				deleteFile(FILE_NAME);
				stream = openFileOutput(FILE_NAME, Context.MODE_WORLD_READABLE);
				mUriDownload = Uri.fromFile(new File(getFilesDir(), FILE_NAME));
			}
			return stream;
		}

		@Override
		public void release()
		{
			onPathStatus(PathStatus.IDLE);
			if (mLoaderTask == this) mLoaderTask = null;
		}
		
		private FileOutputStream createExternalOutputFile()
		{
			if (!Environment.MEDIA_MOUNTED.equals(
					Environment.getExternalStorageState()))
				return null;

			File directory = EnvironmentHelper
					.getExternalFilesDir(CallDetailsActivity.this);
			if (directory == null) return null;
			
			try
			{
				File file = new File(directory, FILE_NAME);
				if (file.exists()) file.delete();
				FileOutputStream stream = new FileOutputStream(file);
				mUriDownload = Uri.fromFile(file);
				return stream;
			}
			catch(Exception e)
			{
				FileLog.w(TAG, "Cannot create file on external storage " +
						directory.getPath(), e);
				return null;
			}
		}

		@Override
		protected void onPreExecute ()
		{
			onPathStatus(PathStatus.LOADING);
		}

		@Override
		protected void onPostExecute (Exception error)
		{
			super.onPostExecute(error);

			if (mLoaderTask == this)
			{
				release();
				String errorMessage = null; 
				if (error == null)
				{
					try
					{
						Intent intent = new Intent(Intent.ACTION_VIEW); 
						intent.setDataAndType(mUriDownload, "application/pdf");
						startActivity(intent);
					}
					catch(ActivityNotFoundException e)
					{
                        FileLog.w(TAG, e.getMessage(), e);
						errorMessage = getString(R.string.error_fax_view);
					}
				}	
				else if (SslErrorException.isSslError(error))
				{
					errorMessage = SslErrorException.getDisplayMessage(
							CallDetailsActivity.this, error);
				}
				else errorMessage = getString(R.string.soap_tranfer_failed);

				if (!TextUtils.isEmpty(errorMessage))
					TextDialog.createOk(CallDetailsActivity.this, errorMessage,
							android.R.drawable.ic_dialog_alert).show();
			}
		}			
	}
}
