/* 
 * Copyright 2012 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.tr064.model;

import java.util.Date;

import de.avm.android.tr064.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.format.DateFormat;

/* Represents a call in the calllist */
public class Call implements Parcelable
{
	public enum CALL_TYPE
	{
		MISSED, INCOMING, OUTGOING, REJECTED, UNSPECIFIED,
		ACTIVE_INCOMING, ACTIVE_OUTGOING;

		/**
		 * Gets the icon for call type.
		 * 
		 * @param context
		 *            a valid context
		 * 
		 * @return the icon for call type
		 */
		public Drawable getIconForCallType(Context context)
		{
			int id;
			switch (this)
			{
				case MISSED:
					id = R.drawable.call_missed;
					break;
				case INCOMING:
				case ACTIVE_INCOMING:
					id = R.drawable.call_incoming;
					break;
				case OUTGOING:
				case ACTIVE_OUTGOING:
					id = R.drawable.call_outgoing;
					break;
				case REJECTED:
					id = R.drawable.call_rejected;
					break;
				default:
					id = R.drawable.call_unspecified;
					break;
			}
			return context.getResources().getDrawable(id);
		}

		/**
		 * Gets the label text for call type.
		 * 
		 * @param context
		 *            a valid context
		 * 
		 * @return the label text for call type
		 */
		public String getLabelForCallType(Context context)
		{
			int id;
			switch (this)
			{
				case MISSED:
					id = R.string.call_log_missed;
					break;
				case INCOMING:
				case ACTIVE_INCOMING:
					id = R.string.call_log_incoming;
					break;
				case OUTGOING:
				case ACTIVE_OUTGOING:
					id = R.string.call_log_outgoing;
					break;
				case REJECTED:
					id = R.string.call_log_rejected;
					break;
				default:
					return "";
			}
			return context.getResources().getString(id);
		}
	};

	public enum EXTERN_PORT_TYPE
	{
		FIXEDLINE, SIP, MOBILE, UNSPECIFIED;

		public static final char TAG_SEPARATOR = ':';
		public static final String TAG_SIP = "SIP";
		public static final String TAG_MOBILE = "Mobilfunk";
		
		/**
		 * Gets the label text for external port.
		 * 
		 * @param context
		 *            a valid context
		 * 
		 * @return the label text for external port
		 */
		public String getLabelForExternPort(Context context)
		{
			int id;
			switch (this)
			{
				case FIXEDLINE:
					id = R.string.externport_fixedline;
					break;
				case SIP:
					id = R.string.externport_sip;
					break;
				case MOBILE:
					id = R.string.externport_mobile;
					break;
				default:
					return "";
			}
			return context.getResources().getString(id);
		}
	};

	protected CALL_TYPE mType = CALL_TYPE.UNSPECIFIED;
	protected Date mTimeStamp = new Date(0);
	protected String mCalledNumber = "";
	protected String mCallerNumber = "";
	protected String mPartnerName = "";
	protected String mInternPortName = "";
	protected String mInternPort = "";
	protected String mId = "";
	protected int mCount = 0;
	protected int mDuration = 0; // in minutes
	protected String mPath = "";

	/**
	 * Instantiates a new call.
	 * 
	 * @param in
	 *            the in
	 */
	public Call(Parcel in)
	{
		mPartnerName = in.readString();
		mDuration = in.readInt();
		mPath = in.readString();
		mCalledNumber = in.readString();
		mCallerNumber = in.readString();
		mInternPortName = in.readString();
		mInternPort = in.readString();
		mTimeStamp = new Date(in.readLong());
		mType = CALL_TYPE.values()[in.readInt()];
	}

	/**
	 * Gets the call type for key. Mapping from FRITZ!Box-key
	 * 
	 * @param aString
	 *            the a string
	 * 
	 * @return the call type for key
	 */
	public static CALL_TYPE getCallTypeForKey(String aString)
	{
		try
		{
			switch (Integer.parseInt(aString))
			{
				case 1: return CALL_TYPE.INCOMING;
				case 2: return CALL_TYPE.MISSED;
				case 3: return CALL_TYPE.OUTGOING;
				case 9: return CALL_TYPE.ACTIVE_INCOMING;
				case 10: return CALL_TYPE.REJECTED;
				case 11: return CALL_TYPE.ACTIVE_OUTGOING;
				default:
					return CALL_TYPE.UNSPECIFIED;
			}
		}
		catch(NumberFormatException e)
		{
			return CALL_TYPE.UNSPECIFIED;
		}
	}

	/**
	 * Instantiates a new call.
	 */
	public Call() {
		super();
	}

	public void setCalledNumber(String called)
	{
		mCalledNumber = (called == null) ? "" : called;
	}
	
	public String getCalledNumber()
	{
		return mCalledNumber;
	}

	public void setCallerNumber(String caller)
	{
		mCallerNumber = (caller == null) ? "" : caller;
	}
	
	public String getCallerNumber()
	{
		return mCallerNumber;
	}

	public Date getTimeStamp()
	{
		return mTimeStamp;
	}

	public void setTimeStamp(Date date)
	{
		mTimeStamp = (date == null) ? new Date(0) : date;
	}

	public String getPartnerName()
	{
		return mPartnerName;
	}

	/**
	 * Gets the partner name or if that is empty the number.
	 * 
	 * @return the partner name if empty number
	 */
	public String getPartnerNameIfEmptyNumber()
	{
		if (isPartnerNameEmpty())
			return getPartnerNumber();
		return getPartnerName();
	}

	/**
	 * Checks if the partner name is empty.
	 * 
	 * @return true, if is partner name empty
	 */
	public boolean isPartnerNameEmpty()
	{
		return getPartnerName().length() == 0;
	}

	public void setPartnerName(String partnerName)
	{
		mPartnerName = (partnerName == null) ? "" : partnerName;
	}

	public CALL_TYPE getType()
	{
		return mType;
	}

	public void setType(CALL_TYPE type)
	{
		mType = (type == null) ? CALL_TYPE.UNSPECIFIED : type;
	}
	
	public boolean matchType(CALL_TYPE type)
	{
		// all
		if (type == CALL_TYPE.UNSPECIFIED)
			return true;
		
		// calls terminated by TAM are treated as missed calls
		if (((mType == CALL_TYPE.INCOMING) || (mType == CALL_TYPE.ACTIVE_INCOMING)) &&
				isTamPort())
			return type == CALL_TYPE.MISSED;
		
		// rejected calls are treated as missed calls
		if (mType == CALL_TYPE.REJECTED)
			return type == CALL_TYPE.MISSED;
		
		return type == mType;
	}

	public Drawable getIcon(Context context)
	{
		if ((mType == CALL_TYPE.INCOMING) || (mType == CALL_TYPE.ACTIVE_INCOMING))
		{
			if (isTamPort())
				return context.getResources().getDrawable(R.drawable.call_incoming_tam);
			else if (isFaxPort())
				return context.getResources().getDrawable(R.drawable.call_incoming_fax);
		}
		return mType.getIconForCallType(context);
	}

	public String getLabel(Context context)
	{
		return mType.getLabelForCallType(context);
	}
	
	/**
	 * Gets the partner number.
	 * 
	 * @return the partner number
	 */
	public String getPartnerNumber()
	{
		switch(getType())
		{
			case OUTGOING:
			case ACTIVE_OUTGOING:
				return mCalledNumber;

			case INCOMING:
			case ACTIVE_INCOMING:
			case MISSED:
			case REJECTED:
				return mCallerNumber;
		}
		
		return "";
	}

	/**
	 * Gets the external port.
	 * Only available for outgoing calls
	 * 
	 * @return the external port or empty
	 */
	public EXTERN_PORT_TYPE getExternPort()
	{
		String number = null;
		switch(getType())
		{
			case OUTGOING:
			case ACTIVE_OUTGOING:
				number = mCallerNumber;
				break;

			case INCOMING:
			case ACTIVE_INCOMING:
			case MISSED:
			case REJECTED:
				number = mCalledNumber;
				break;
		}

		if (number != null)
		{
			int separator = number.indexOf(EXTERN_PORT_TYPE.TAG_SEPARATOR);
			if (separator < 1) return EXTERN_PORT_TYPE.FIXEDLINE;
			
			if (number.substring(0, separator)
					.equalsIgnoreCase(EXTERN_PORT_TYPE.TAG_SIP))
				return EXTERN_PORT_TYPE.SIP;
			else if (number.substring(0, separator)
					.equalsIgnoreCase(EXTERN_PORT_TYPE.TAG_MOBILE))
				return EXTERN_PORT_TYPE.MOBILE;
		}
		
		return EXTERN_PORT_TYPE.UNSPECIFIED;
	}

	/**
	 * Gets the intern port.
	 * 
	 * @return the intern port
	 */
	public String getInternPort()
	{
		return mInternPort;
	}

	/**
	 * Sets the intern port.
	 * 
	 * @param port
	 *            the new intern port
	 */
	public void setInternPort(String port)
	{
		mInternPort = (port == null) ? "" : port;
	}

	/**
	 * Is InternPort a TAM?
	 * 
	 * @return true if TAM
	 */
	public boolean isTamPort()
	{
		try
		{
			int port = Integer.parseInt(mInternPort);
			return (port == 6) || ((port >= 40) && (port <= 49));
		}
		catch (NumberFormatException e) { /* ignore */ }
		return false;
	}

	/**
	 * Is InternPort a Fax?
	 * 
	 * @return true if Fax
	 */
	public boolean isFaxPort()
	{
		try
		{
			int port = Integer.parseInt(mInternPort);
			return port == 5;
		}
		catch (NumberFormatException e) { /* ignore */ }
		return false;
	}

	/**
	 * Gets the intern port's name.
	 * 
	 * @return the intern port
	 */
	public String getInternPortName()
	{
		return mInternPortName;
	}
	
	/**
	 * Sets the intern port's name.
	 * 
	 * @param name
	 *            the new intern port's name
	 */
	public void setInternPortName(String name)
	{
		mInternPortName = (name == null) ? "" : name;
	}

	/**
	 * Gets the intern number.
	 * 
	 * @return the intern number
	 */
	public String getInternNumber()
	{
		String number = null;
		switch(getType())
		{
			case OUTGOING:
			case ACTIVE_OUTGOING:
				number = mCallerNumber;
				break;

			case INCOMING:
			case ACTIVE_INCOMING:
			case MISSED:
			case REJECTED:
				number = mCalledNumber;
				break;
		}
		
		if (number != null)
		{
			int separator = number.indexOf(EXTERN_PORT_TYPE.TAG_SEPARATOR);
			if (separator < 0) return number;
			return number.substring(separator + 1).trim();
		}
		return "";
	}

	/**
	 * Gets the duration.
	 * 
	 * @return the duration
	 */
	public int getDuration()
	{
		return mDuration;
	}

	/**
	 * Sets the duration.
	 * 
	 * @param duration
	 *            the new duration
	 */
	public void setDuration(int duration)
	{
		mDuration = (duration < 0) ? 0 : duration;
	}

	public boolean hasPath()
	{
		return !TextUtils.isEmpty(mPath);
	}
	
	/**
	 * Gets the path of an associated resource (e.g. TAM message).
	 * 
	 * @return the path
	 */
	public String getPath()
	{
		return mPath;
	}

	/**
	 * Sets the path of an associated resource (e.g. TAM message).
	 * 
	 * @param internPort
	 *            the new intern port
	 */
	public void setPath(String path)
	{
		mPath = (path == null) ? "" : path;
	}

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	public String getId()
	{
		return mId;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(String id)
	{
		mId = (id == null) ? "" : id;
	}

	/**
	 * Gets the count.
	 * 
	 * @return the count
	 */
	public int getCount()
	{
		return mCount;
	}

	/**
	 * Sets the count.
	 * 
	 * @param count
	 *            the new count
	 */
	public void setCount(int count)
	{
		mCount = (count < 0) ? 0 : count;
	}

	public int describeContents()
	{
		return 0;
	}

	public static final Parcelable.Creator<Call> CREATOR = new Parcelable.Creator<Call>()
	{
		public Call createFromParcel(Parcel in)
		{
			return new Call(in);
		}

		public Call[] newArray(int size)
		{
			return new Call[size];
		}
	};

	public void writeToParcel(Parcel out, int flags)
	{
		out.writeString(mPartnerName);
		out.writeInt(mDuration);
		out.writeString(mPath);
		out.writeString(mCalledNumber);
		out.writeString(mCallerNumber);
		out.writeString(mInternPortName);
		out.writeString(mInternPort);
		out.writeLong(mTimeStamp.getTime());
		out.writeInt(mType.ordinal());
	}

	/**
	 * Gibt Zeit und Datum des Anrufes "schön" formatiert zurück. Format:
	 * <VollesDatum_MonatAlsText> <Uhrzeit_StundeMinute>
	 * 
	 * @param c
	 *            a valid context
	 * 
	 * @return the pretty date full
	 */
	public String getPrettyDateFull(Context c)
	{
		return DateFormat.getMediumDateFormat(c).format(getTimeStamp()) + " "
				+ DateFormat.getTimeFormat(c).format(getTimeStamp());
	}
	
	/**
	 * Gets user friendly formatted call duration
	 * 
	 * @param c
	 *            a valid context
	 * @return the pretty duration string
	 */
	public String getPrettyDuration(Context c)
	{
		return String.format(c.getString(R.string.call_log_duration_fmt),
				getDuration());
	}
	
	/**
	 * Gets user friendly formatted call count
	 * (to be used for getCount()>1)
	 * 
	 * @param c
	 *            a valid context
	 * @return the pretty count string
	 */
	public String getPrettyCount(Context c)
	{
		return String.format(c.getString(R.string.call_log_count_fmt),
				getCount());
	}
	
	@Override
	public boolean equals(Object o)
	{
		if (o != null) return hashCode() == o.hashCode();
		return false;
	}
	
	@Override
	public int hashCode()
	{
	    int result = 17;
	    result = 31 * result + mType.hashCode();
	    result = 31 * result + mTimeStamp.hashCode();
	    result = 31 * result + mCalledNumber.hashCode();
	    result = 31 * result + mCallerNumber.hashCode();
	    result = 31 * result + mInternPortName.hashCode();
	    result = 31 * result + mInternPort.hashCode();
	    result = 31 * result + mCount;
	    result = 31 * result + mId.hashCode();
	    result = 31 * result + mPath.hashCode();
	    return result;
	}
}