/* 
 * 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 de.avm.android.fritzapp.R;
import de.avm.android.fritzapp.util.PhoneNumberHelper;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.ToneGenerator;
import android.preference.PreferenceManager;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;

/**
 *	Wrapper for dialpad
 *	1st version to get code out of consuming activity
 *
 *	keyboard input hat to be relayed to this instance through interface
 * 	View.OnKeyListener
 * 
 * 	parent must have layout/dtmf_twelve_key_dialer in it's layout
 */
public class Dialpad extends SlidingDrawer implements View.OnClickListener,
		View.OnLongClickListener, View.OnKeyListener, View.OnTouchListener
{
    /**
     * Callback invoked when a digit has been added.
     */
    public static interface OnDtmfDigitListener
    {
        /**
         * @param digit 
         * @param dtmfTone as android.media.ToneGenerator.TONE_DTMF_*
         */
        public void onDtmfDigit(char digit, int dtmfTone);
    }
    
    /**
     * Callback invoked when TAM has to be called.
     * (setEnableNonDtmfInput(true) has to be called beforehand)
     */
    public static interface OnTamListener
    {
    	public void onTam();
    }

    private static final int REDIAL_MAX = 5;
	private static final String PREF_REDIAL = "redial";
	private static final String PREF_REDIAL_LIST = "redial-list";

	private OnDtmfDigitListener mOnDtmfDigitListener = null;
	private OnTamListener mOnTamListener = null;
	
	private boolean mEnableNonDtmfInput = false;
	private boolean mEnableTamInput = false;
	private boolean mInitiallyOpen = false;
	private String mInitialText = null;
	private int mHintId = 0;
	private int mLoadedRedial = -1;
	

	public Dialpad(Context context, AttributeSet attrs)
	{
        this(context, attrs, 0);
	}
	
	public Dialpad(Context context, AttributeSet attrs, int defStyle)
	{
        super(context, attrs, defStyle);
	}

    @Override
    protected void onFinishInflate()
    {
    	super.onFinishInflate();

        View parent = getContent();
	    mDigits = (EditText)parent.findViewById(R.id.digits);
	    if (mHintId > 0) mDigits.setHint(mHintId);
	    if (mInitialText != null) mDigits.setText(mInitialText);
	    mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

	    // input
	    parent.setOnKeyListener(this);
	    parent.setOnClickListener(new OnClickListener()
        {
			public void onClick(View view)
			{
		        // catch clicks on dialpad's background to avoid routing to
				// invisible controls under the dialpad
			}
        });
	    
	    // handle button clicks
	    View view = parent.findViewById(R.id.digits_back);
	    view.setOnTouchListener(this);
	    view.setOnClickListener(this);
	    view.setOnLongClickListener(this);
        for (int viewId : mButtonIds)
        {
        	view = parent.findViewById(viewId);
    	    view.setOnTouchListener(this);
        	view.setOnClickListener(this);
        	view.setOnLongClickListener(this);
        }

        // has to be opened?
        if (mInitiallyOpen) open();
    }

    public void setInitiallyOpen(boolean open)
    {
    	mInitiallyOpen = open;
		if (open && (getContent() != null) && !isOpened())
    	{
    		// init already done and not set properly
			open();
    	}
    }
    
    /**
     * Enables or disables Input some non-DTMF chars
     * (currently "+")
     * @param on true to enable
     */
    public void setEnableNonDtmfInput(boolean on)
    {
    	if (mEnableNonDtmfInput != on)
    	{
    		mEnableNonDtmfInput = on;
    		ImageButton view = (ImageButton)findViewById(R.id.zero);
    		if (view != null)
    			view.setImageResource((on) ? R.drawable.dial_num_0plus :
    					R.drawable.dial_num_0);
    	}
    }
    
    /**
     * Enables or disables TAM shortcut
     * @param on true to enable
     */
    public void setEnableTamInput(boolean on)
    {
    	if (mEnableTamInput != on)
    	{
    		mEnableTamInput = on;
    		ImageButton view = (ImageButton)findViewById(R.id.one);
    		if (view != null)
    			view.setImageResource((on) ? R.drawable.dial_num_1tam :
    					R.drawable.dial_num_1);
    	}
    }
    
	/**
	 * Gets current text
	 * @return
	 */
	public String getText()
	{
		if (mDigits != null)
			return PhoneNumberHelper.stripSeparators(
					mDigits.getText().toString());
		return "";
	}

	/**
	 * Sets current text
	 * @return
	 */
	public void setText(String text)
	{
		mInitialText = text;
		if (mDigits != null) mDigits.setText(text);
		mLoadedRedial = -1;
	}

	/**
	 * Sets hint for text input field
	 * @return
	 */
	public void setHint(int resid)
	{
		mHintId = resid;
        if (mDigits != null) mDigits.setHint(resid);
	}
	
	/**
	 * Saves text as most recent number
	 * @param context
	 * @param text
	 */
	public static void saveAsRedial(Context context, String text)
	{
		if (!TextUtils.isEmpty(text))
		{
			SharedPreferences prefs = PreferenceManager
					.getDefaultSharedPreferences(context);
			String redial = prefs.getString(PREF_REDIAL_LIST, null);
			String[] list = null;
			boolean removeOldPref = false;
			if (TextUtils.isEmpty(redial))
			{
				// old redial pref?
				redial = prefs.getString(PREF_REDIAL, null);
				if (!TextUtils.isEmpty(redial))
				{
					removeOldPref = true;
					list = new String[] { redial };
				}
			}
			else list = redial.split("\n");

			if (removeOldPref || (list == null) || !text.equals(list[0]))
			{			
				StringBuilder newRedial = new StringBuilder(text);
				for (int ii = 0, jj = 1; (list != null) && (jj < REDIAL_MAX) &&
						(ii < list.length); ii++)
				{
					if (!text.equals(list[ii]))
					{
						newRedial.append("\n" + list[ii]);
						jj++;
					}
				}
	
				Editor edit = prefs.edit();
				edit.putString(PREF_REDIAL_LIST, newRedial.toString());
				if (removeOldPref) edit.remove(PREF_REDIAL);
				edit.apply();
			}
		}
	}

	/**
	 * Restores redial number to edit field
	 * @param context
	 */
	public void loadRedial(Context context)
	{
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		String redial = prefs.getString(PREF_REDIAL_LIST, null);
		String[] list = null;
		if (TextUtils.isEmpty(redial))
		{
			// old redial pref?
			redial = prefs.getString(PREF_REDIAL, null);
			if (!TextUtils.isEmpty(redial))
				list = new String[] { redial };
		}
		else list = redial.split("\n");

		if (list != null)
		{
			mDigits.setText(""); // reset for PhoneNumberFormattingTextWatcher
			if ((mLoadedRedial >= 0) && ((mLoadedRedial + 1) < list.length))
				mDigits.setText(list[++mLoadedRedial]);
			else
				mDigits.setText(list[mLoadedRedial = 0]);
		}
	}

	/**
	 * Adds listener for listening to text changes
	 * @param textWatcher
	 */
	public void addTextChangedListener(TextWatcher textWatcher)
	{
		mDigits.addTextChangedListener(textWatcher);
	}
	
	/**
	 * Removes listener for listening to text changes
	 * @param textWatcher
	 */
	public void removeTextChangedListener(TextWatcher textWatcher)
	{
		mDigits.removeTextChangedListener(textWatcher);
	}

    /**
     * Sets the listener that receives a notification when the drawer becomes open.
     *
     * @param onDrawerOpenListener The listener to be notified when the drawer is opened.
     */
    public void setOnDtmfDigitListener(OnDtmfDigitListener onDtmfDigitListener)
    {
        mOnDtmfDigitListener = onDtmfDigitListener;
    }
    
    /**
     * Sets the listener that receives a notification when the TAM button has been invoked.
     *
     * @param onTamListener The listener
     */
    public void setOnTamListener(OnTamListener onTamListener)
    {
    	mOnTamListener = onTamListener;
    }

	private static int mButtonIds[] = new int[]
  	{
  		R.id.one, R.id.two, R.id.three, R.id.four, R.id.five,
  		R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.zero,
  		R.id.pound, R.id.star
  	};

  	private static char mCharacters[] = new char[]
	{
		'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '#', '*'
	};
  	                                         	
  	private static int mDtmfTones[] = new int[]
  	{
  		ToneGenerator.TONE_DTMF_1, ToneGenerator.TONE_DTMF_2,
  		ToneGenerator.TONE_DTMF_3, ToneGenerator.TONE_DTMF_4,
  		ToneGenerator.TONE_DTMF_5, ToneGenerator.TONE_DTMF_6,
  		ToneGenerator.TONE_DTMF_7, ToneGenerator.TONE_DTMF_8,
  		ToneGenerator.TONE_DTMF_9, ToneGenerator.TONE_DTMF_0,
  		ToneGenerator.TONE_DTMF_P, ToneGenerator.TONE_DTMF_S
  	};
   	
  	private static int NONDTMF_PLUS = 0;
  	private static char mNonDtmfCharacters[] = new char[]
	{
		'+'
	};
  	
  	EditText mDigits = null;

	public boolean onTouch(View view, MotionEvent event)
	{
  		if (event.getAction() == MotionEvent.ACTION_DOWN)
  			view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
		return false;
	}

	public void onClick(View v)
	{
		if (isOpened())
		{
	        int viewId = v.getId();
	        if (viewId == R.id.digits_back)
	        {
		    	removeLastDigit();
	        }
	        else
	        {
	    		for(int ii = 0; ii < mButtonIds.length; ++ii)
	    		{
	    			if (mButtonIds[ii] == viewId)
	    			{
	    	        	appendDigit(mCharacters[ii]);
	    	        	break;
	    			}
	    		}
	        }
		}
	}

	public boolean onLongClick(View v)
	{
		if (isOpened())
		{
	        int viewId = v.getId();
	        if (viewId == R.id.digits_back)
	        {
				mDigits.setText("");
				mLoadedRedial = -1;
				return true;
	        }
	        else if ((viewId == R.id.zero) && mEnableNonDtmfInput) 
	        {
	        	appendDigit(mNonDtmfCharacters[NONDTMF_PLUS]);
				return true;
	        }
	        else if ((viewId == R.id.one) && mEnableTamInput) 
	        {
	        	if (mOnTamListener != null) mOnTamListener.onTam();
				return true;
	        }
		}
		return false;
	}

	public boolean onKey(View v, int keyCode, KeyEvent event)
	{
		if (isOpened())
		{
			if (event.getKeyCode() == KeyEvent.KEYCODE_DEL)
			{
		    	removeLastDigit();
		    	return true;
			}
			else
			{
				char digit = event.getMatch(mCharacters);
				if ((digit == '\0') && mEnableNonDtmfInput)
					digit = event.getMatch(mNonDtmfCharacters);
		        if (digit != '\0')
		        {
		        	appendDigit(digit);
			    	return true;
		        }
			}
		}
		
		return false;
	}
	
	private void appendDigit(char digit)
	{
        mDigits.getText().append(digit);
		mLoadedRedial = -1;
        
        if (mOnDtmfDigitListener != null)
        {
    		for(int ii = 0; ii < mCharacters.length; ++ii)
    		{
    			if (mCharacters[ii] == digit)
    			{
    				mOnDtmfDigitListener.onDtmfDigit(digit, mDtmfTones[ii]);
    				break;
    			}
    		}
        }
	}
	
	private void removeLastDigit()
	{
		Editable text = mDigits.getText();
		if (text != null)
		{
			int len = text.length();
			if (len > 0) text.delete(len - 1, len);
		}
		mLoadedRedial = -1;
	}
}
