package com.infideap.blockedittext;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.app.infideap.stylishwidget.util.Utils;
import com.app.infideap.stylishwidget.view.AEditText;
import com.app.infideap.stylishwidget.view.ATextView;

import java.util.ArrayList;
import java.util.List;

public class BlockEditText extends FrameLayout {
    private static final int AMEX = 1;
    private static final int MASTERCARD = 2;
    private static final int VISA = 3;

    private int noOfBlock = 1;
    private LinearLayout linearLayout;
    private LinearLayout blockLinearLayout;
    private SparseIntArray lengths = new SparseIntArray();
    private SparseIntArray lengthUsed;
    private int defaultLength = 1;
    private ATextView hintTextView;
    private int inputType = InputType.TYPE_CLASS_TEXT;
    private TextWatcher watcher;
    private ActionMode.Callback callback;
    private SparseArray<AEditText> editTexts = new SparseArray<>();
    private Character separator;
    private int separatorTextAppearance = android.support.v7.appcompat.R.style.Base_TextAppearance_AppCompat_Medium;
    private int separatorPadding = 16;
    private float separatorTextSize;
    private float textSize;
    private float hintTextSize;
    private int textAppearance = android.support.v7.appcompat.R.style.Base_TextAppearance_AppCompat_Medium;
    private int hintTextAppearance = android.support.v7.appcompat.R.style.Base_TextAppearance_AppCompat_Medium;
    private Drawable editTextBackground;
    private String hint;
    private ColorStateList hintColorDefault;
    private ColorStateList hintColorFocus;

    private boolean shiftPosition = true;

    private List<CardPrefix> cardPrefixes = new ArrayList<>();
    private ImageView iconImageView;
    private OnCardPrefixListener cardPrefixListener;
    private int cardIconSize = (int) Utils.convertDpToPixel(48);
    private boolean isEnabled = true;
    private boolean isShowCardIcon;
    private int editTextStyle;

    public BlockEditText(@NonNull Context context) {
        super(context);
        init(context, null);

    }

    public BlockEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public BlockEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public BlockEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);

    }

    private void init(Context context, AttributeSet attrs) {
        lengthUsed = lengths;

        linearLayout = new LinearLayout(getContext());
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        linearLayout.setLayoutParams(createWidthMatchParentLayoutParams());

        blockLinearLayout = new LinearLayout(getContext());
        blockLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
        blockLinearLayout.setLayoutParams(createWidthMatchParentLayoutParams());

        linearLayout.addView(blockLinearLayout);

        addView(linearLayout);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BlockEditText);
        editTextBackground = a.getDrawable(R.styleable.BlockEditText_bet_editTextBackground);
        hint = a.getString(R.styleable.BlockEditText_bet_hint);
        setHint(hint);

        String tempStr = a.getString(R.styleable.BlockEditText_bet_separatorCharacter);
        if (!TextUtils.isEmpty(tempStr)) {
            separator = tempStr.charAt(0);
        }

        noOfBlock = a.getInt(
                R.styleable.BlockEditText_bet_numberOfBlock,
                noOfBlock
        );

        defaultLength = a.getInt(
                R.styleable.BlockEditText_bet_defaultLength,
                defaultLength
        );

        textAppearance = a.getResourceId(
                R.styleable.BlockEditText_bet_hintTextAppearance,
                textAppearance
        );

        hintTextAppearance = a.getResourceId(
                R.styleable.BlockEditText_bet_hintTextAppearance,
                hintTextAppearance
        );

        separatorTextAppearance = a.getResourceId(
                R.styleable.BlockEditText_bet_separatorTextAppearance,
                separatorTextAppearance
        );

        textSize = a.getDimension(
                R.styleable.BlockEditText_bet_textSize,
                textSize
        );


        hintTextSize = a.getDimension(
                R.styleable.BlockEditText_bet_hintTextSize,
                hintTextSize
        );

        separatorTextSize = a.getDimension(
                R.styleable.BlockEditText_bet_separatorTextSize,
                separatorTextSize
        );

        cardIconSize = a.getDimensionPixelOffset(
                R.styleable.BlockEditText_bet_cardIconSize,
                cardIconSize
        );

        separatorPadding = a.getDimensionPixelOffset(
                R.styleable.BlockEditText_bet_hintTextSize,
                separatorPadding
        );

        inputType = a.getInt(
                R.styleable.BlockEditText_bet_inputType,
                inputType
        );


        shiftPosition = a.getBoolean(
                R.styleable.BlockEditText_bet_showCardIcon,
                true
        );

        editTextStyle = a.getResourceId(
                R.styleable.BlockEditText_bet_style, -1
        );

        int cardPrefix = a.getInt(
                R.styleable.BlockEditText_bet_cardPrefix,
                0
        );

        if (containsFlag(cardPrefix, AMEX)) {
            cardPrefixes.add(CardPrefix.amex(getContext()));
        }
        if (containsFlag(cardPrefix, MASTERCARD)) {
            cardPrefixes.add(CardPrefix.mastercard(getContext()));
        }
        if (containsFlag(cardPrefix, VISA)) {
            cardPrefixes.add(CardPrefix.visa(getContext()));
        }


        setHintTextAppearance(hintTextAppearance);

        shiftPosition = (a.getBoolean(
                R.styleable.BlockEditText_bet_shiftPosition,
                true
        ));

        initLayout();
        tempStr = a.getString(R.styleable.BlockEditText_bet_text);
        if (tempStr != null)
            setText(tempStr);
        a.recycle();

    }

    private boolean containsFlag(int flagSet, int flag) {
        return (flagSet | flag) == flagSet;
    }

    private ViewGroup.LayoutParams createWidthMatchParentLayoutParams() {
        return new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
        );
    }

    private void initLayout() {
        blockLinearLayout.removeAllViews();
        int i = 0;
        String text = getText();
        for (; i < noOfBlock; i++) {

            final int length = getLength(i);
            final AEditText editText;
            if (editTexts.get(i) == null) {
                if (editTextStyle == -1) {
                    editText = new AEditText(getContext());
                } else {
                    editText = new AEditText(getContext(), null, editTextStyle);

                }
                editText.addTextChangedListener(createTextChangeListener(editText, i));
                editTexts.put(i, editText);
                editText.setOnFocusChangeListener(new OnFocusChangeListener() {
                    @Override
                    public void onFocusChange(View v, boolean hasFocus) {
                        if (hintTextView != null)
                            hintTextView.setHintTextColor(hasFocus ? hintColorFocus : hintColorDefault);
                    }
                });

                editText.setSupportTextAppearance(textAppearance);
                setTextSize(editText, textSize);
                editText.setOnKeyListener(createKeyListener(editText, i));

            } else {
                editText = editTexts.get(i);
            }

            editText.setInputType(inputType);
            InputFilter[] filters = new InputFilter[1];
            filters[0] = new LengthFilter(editText, i);
            editText.setFilters(filters);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    0, LinearLayout.LayoutParams.WRAP_CONTENT, length
            );
            editText.setLayoutParams(params);
            editText.setGravity(Gravity.CENTER);
            blockLinearLayout.addView(editText);
            if (editTextBackground != null)
                setEdiTextBackground(editText, editTextBackground);

            if (i + 1 < noOfBlock && separator != null) {
                ATextView textView = new ATextView(getContext());
                textView.setText(String.valueOf(separator));
                LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT
                );
                textViewParams.gravity = Gravity.CENTER;
                textView.setLayoutParams(textViewParams);
                textView.setPadding(separatorPadding, 0, separatorPadding, 0);
                textView.setSupportTextAppearance(separatorTextAppearance);
                if (separatorTextSize > 0)
                    textView.setTextSize(separatorTextSize);
                blockLinearLayout.addView(textView);
            }

            editText.setText(null);

            setEditTextEnable(editText, i);


        }
        for (; i < editTexts.size(); ) {
            editTexts.remove(i);
        }

        setText(text);
        hideOrShowCardIcon();

    }

    private void setEditTextEnable(AEditText editText, int i) {
        if (shiftPosition && i > 0)
            editText.setEnabled(false);
        else
            editText.setEnabled(isEnabled);
    }

    Editable getText(EditText editText) {
        return editText.getText();
    }

    private OnKeyListener createKeyListener(final AEditText editText, final int i) {
        return new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_DEL) {
                    if (editText.getSelectionStart() == 0 && editText.getSelectionEnd() == 0) {
                        AEditText prevEditText = editTexts.get(i - 1);
                        if (prevEditText != null) {
                            prevEditText.requestFocus();
                            if (editText.length() > 0) {
                                prevEditText.getEditableText().delete(getText(prevEditText).length() - 1, prevEditText.getText().length());
                                prevEditText.setSelection(prevEditText.getText().length() - 1);
                            }
                            return true;

                        }
                    }
                }
                return false;
            }
        };
    }

    private void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (watcher != null) watcher.beforeTextChanged(s, start, count, after);
    }

    private void onTextChanged(CharSequence s, int start, int before, int count) {
        if (watcher != null) watcher.onTextChanged(s, start, before, count);

    }

    private void afterTextChanged(Editable s) {
        if (watcher != null) watcher.afterTextChanged(s);

    }

    public void setTextChangedListener(TextWatcher watcher) {
        this.watcher = watcher;
    }


    public void setCustomInsertionActionModeCallback(ActionMode.Callback callback) {
        this.callback = callback;
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            setCustomInsertionActionModeCallback(editText, callback);
        }
    }

    private void setCustomInsertionActionModeCallback(AEditText editText, ActionMode.Callback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            editText.setCustomInsertionActionModeCallback(callback);
        } else
            editText.setCustomSelectionActionModeCallback(callback);
    }

    public void setSeparatorCharacter(Character separator) {
        this.separator = separator;
        initLayout();
    }

    public void setText(CharSequence sequence) {
        int i = 0;
        for (; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            editText.setText(null);
        }
        if (sequence != null) {
            String text = String.valueOf(sequence);
            AEditText editText = editTexts.get(0);
            editText.getEditableText().insert(0, text);

        }


    }

    public String getText() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < editTexts.size(); i++) {

            AEditText editText = editTexts.get(i);
            builder.append(editText.getText());

        }

        return builder.toString();
    }


    public int getMaxLength() {
        int length = 0;
        for (int i = 0; i < editTexts.size(); i++) {
            length += getLength(i);

        }

        return length;
    }

    private int getLength(int i) {
        return lengthUsed.get(i, defaultLength);
    }

    public void setTextSize(float textSize) {
        this.textSize = textSize;
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            setTextSize(editText, textSize);
        }
    }

    private void setTextSize(AEditText editText, float textSize) {
        if (textSize > 0)
            editText.setTextSize(textSize);
    }

    public void setTextAppearance(int textAppearance) {
        this.textAppearance = textAppearance;
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            editText.setSupportTextAppearance(textAppearance);
        }
    }

    public void setInputType(int type) {
        inputType = type;
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            editText.setInputType(type);
        }
    }

    public void setNumberOfBlock(int block) {
        this.noOfBlock = block;
        initLayout();
    }

    public void setDefaultLength(int defaultLength) {
        this.defaultLength = defaultLength;
        initLayout();
    }

    public void setEdiTextBackground(Drawable drawable) {
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);

            setEdiTextBackground(editText, drawable);
        }
    }

    private void setEdiTextBackground(AEditText editText, Drawable drawable) {
        ViewCompat.setBackground(editText, drawable.getConstantState().newDrawable());
    }

    public void setLengthAt(int index, int length) {
        lengths.put(index, length);
        initLayout();
    }

    public void setHint(String hint) {
        if (hintTextView == null) {
            hintTextView = new ATextView(getContext());
            hintTextView.setPadding(16, 0, 16, 0);
            setHintTextAppearance(hintTextAppearance);
            setHintTextSize(hintTextAppearance);
            linearLayout.addView(hintTextView, 0);
            hintColorDefault = hintTextView.getHintTextColors();
            hintColorFocus = ContextCompat.getColorStateList(
                    getContext(),
                    R.color.colorAccent
            );


        }
        hintTextView.setVisibility(hint == null ? GONE : VISIBLE);
        hintTextView.setHint(hint);


    }

    public void setHintTextSize(float textSize) {
        this.hintTextSize = textSize;
        if (hintTextView != null && textSize > 0) {
            hintTextView.setTextSize(textSize);
        }
    }

    public void setHintTextAppearance(int textAppearance) {
        this.hintTextAppearance = textAppearance;
        if (hintTextView != null) {
            hintTextView.setSupportTextAppearance(textAppearance);
        }
    }


    public void setSeparatorTextAppearance(int textAppearance) {
        this.separatorTextAppearance = textAppearance;
        initLayout();
    }

    public void setSeparatorPadding(int padding) {
        this.separatorPadding = padding;
        initLayout();
    }

    public void setSeparatorTextSize(float textSize) {
        this.separatorTextSize = textSize;
        initLayout();
    }

    public void setSelection(int selection) {
        for (int i = 0; i < editTexts.size(); i++) {
            int length = getLength(i);
            if (selection < length) {
                AEditText editText = editTexts.get(i);
                editText.requestFocus();
                editText.setSelection(selection);

                break;
            }
            selection -= length;
        }
    }

    public void addCardPrefix(CardPrefix cardPrefix) {
        cardPrefixes.add(cardPrefix);
        hideOrShowCardIcon();
    }


    public void addCardPrefix(int index, CardPrefix cardPrefix) {
        cardPrefixes.add(index, cardPrefix);
        hideOrShowCardIcon();
    }

    public boolean removeCardPrefix(CardPrefix cardPrefix) {
        boolean b = cardPrefixes.remove(cardPrefix);
        hideOrShowCardIcon();
        return b;
    }

    public CardPrefix removeCardPrefix(int index) {
        CardPrefix cardPrefix = cardPrefixes.remove(index);
        hideOrShowCardIcon();
        return cardPrefix;
    }

    public void setCardIconSize(int size) {
        cardIconSize = size;
        if (iconImageView != null) {
            iconImageView.getLayoutParams().width = size;
            iconImageView.requestLayout();
        }
    }

    public void setEnabled(boolean isEnabled) {
        this.isEnabled = isEnabled;
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            editText.setEnabled(isEnabled);
        }
    }

    public void setShowCardIcon(boolean isShowCardIcon) {
        this.isShowCardIcon = isShowCardIcon;
        hideOrShowCardIcon();
    }

    public void setShiftPosition(boolean b) {

        shiftPosition = b;
        initLayout();
    }

    public boolean isEnabled() {
        return isEnabled;
    }

    public boolean isShiftPosition() {
        return shiftPosition;
    }

    private void hideOrShowCardIcon() {
        if (isShowCardIcon && cardPrefixes.size() > 0) {
            if (iconImageView == null) {
                iconImageView = new ImageView(getContext());
                iconImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                iconImageView.setAdjustViewBounds(true);
                ViewGroup.LayoutParams params = new LayoutParams(
                        cardIconSize,
                        ViewGroup.LayoutParams.MATCH_PARENT
                );
                iconImageView.setLayoutParams(params);
                blockLinearLayout.addView(iconImageView);
                updateCardIcon();
            } else if (iconImageView.getParent() == null) {
                blockLinearLayout.addView(iconImageView);
            }
            iconImageView.setVisibility(VISIBLE);

        } else if (iconImageView != null) {
            iconImageView.setVisibility(GONE);
        }


    }


    private void updateCardIcon() {

        if (cardPrefixes.size() > 0) {
            String text = getText();
            for (CardPrefix cardPrefix : cardPrefixes) {
                for (String prefix : cardPrefix.getPrefixes())
                    if (text.startsWith(prefix)) {
                        iconImageView.setImageDrawable(cardPrefix.getIcon());
                        if (cardPrefix.getLengths().size() > 0) {
                            lengthUsed = cardPrefix.getLengths();
                            updateEditTextLength();
                        }
                        if (cardPrefixListener != null)
                            cardPrefixListener.onCardUpdate(cardPrefix);

                        return;
                    }
            }
            lengthUsed = lengths;
            if (iconImageView != null) {
                iconImageView.setImageDrawable(null);
                updateEditTextLength();
            }

            if (cardPrefixListener != null)
                cardPrefixListener.onCardUpdate(null);


        }

    }

    private void updateEditTextLength() {
        for (int i = 0; i < editTexts.size(); i++) {
            AEditText editText = editTexts.get(i);
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) editText.getLayoutParams();
            params.weight = getLength(i);
            editText.requestLayout();
        }
    }

    public void setOnCardPrefixListener(OnCardPrefixListener listener) {
        this.cardPrefixListener = listener;
    }

    private ActionMode.Callback createActionModeCallback(AEditText editText) {
        return new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {

                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        };
    }

    private TextWatcher createTextChangeListener(final AEditText editText, final int index) {
        return new TextWatcher() {
            public CharSequence sequence = "";
            public CharSequence beforSequence = "";
            int prevLength = 0;
            int selection = 0;

            int start, before;

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                prevLength = s.length();
                selection = editText.getSelectionStart();
                beforSequence = sequence;
                for (int i = 0; i < index; i++) {
                    start += getLength(i);
                }
                this.start = start;
                this.before = beforSequence.length();
                BlockEditText.this.beforeTextChanged(beforSequence, this.start, this.before, this.before + (after - count));
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                EditText nextView = editTexts.get(index + 1);
                EditText prevView = editTexts.get(index - 1);
                if (s.length() > prevLength && editText.isFocused() && editText.getSelectionStart() == getLength(index))
                    if (s.length() == getLength(index) && nextView != null && nextView.getText().length() == 0)
                        nextView.requestFocus();
                    else if (s.length() == 0 && prevView != null)
                        prevView.requestFocus();


                if (shiftPosition && s.length() < getLength(index)) {
                    if (editText.getSelectionStart() == 0 && editText.isFocused() && prevView != null) {
                        prevView.requestFocus();
                        prevView.setSelection(prevView.getText().length());
                    }
                    if (nextView != null && !nextView.getText().toString().isEmpty()) {
                        int length = getLength(index) - s.length();
                        length = length > nextView.getText().length() ? nextView.getText().length() : length;
                        Editable editable = nextView.getText();
                        String temp = editable.toString().substring(0, length);
                        editable = editable.delete(0, length);
                        editText.append(temp);
                        editText.setSelection(selection);
                        nextView.setText(editable);
                    }
                }
                sequence = getText();
                BlockEditText.this.onTextChanged(sequence, this.start, this.before, sequence.length());


            }

            @Override
            public void afterTextChanged(Editable s) {
                EditText nextView = editTexts.get(index + 1);

                if (shiftPosition && nextView != null)
                    nextView.setEnabled(isEnabled && s.length() >= getLength(index));
                updateCardIcon();
                BlockEditText.this.afterTextChanged(s);
            }
        };
    }

    public class LengthFilter implements InputFilter {
        private final int index;
        private final AEditText editText;

        public LengthFilter(AEditText editText, int index) {
            this.index = index;
            this.editText = editText;
        }

        public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
                                   int dstart, int dend) {
            if (editText.getInputType() == InputType.TYPE_CLASS_NUMBER) {
                source = String.valueOf(source).replaceAll("[\\D]", "");
            } else {
                source = String.valueOf(source).replaceAll("[\\W]", "");
            }
            int mMax = getLength(index);


            int keep = mMax - (dest.length() - (dend - dstart));

            EditText nextView = editTexts.get(index + 1);

            if (source.length() == 0)
                return null;
            if (keep <= 0) {
                if (nextView != null && getText().length() < getMaxLength()) {
                    String s = source.toString();
                    String temp = editText.getText().toString();

                    int selection = editText.getSelectionStart();
                    temp = temp.substring(0, selection) + source + temp.substring(selection);
                    editText.setText(temp.substring(0, mMax));

                    temp = temp.substring(mMax);
                    if (selection + source.length() <= mMax)
                        editText.setSelection(selection + source.length());
                    else {
                        nextView.requestFocus();
                        nextView.setSelection(0);

                    }
                    if (temp.length() > 0) {
                        nextView.getEditableText().insert(0, temp);
                        int nextLength = getLength(index + 1);
                        nextView.setSelection(temp.length() < nextLength ? temp.length() : nextLength);
                    } else
                        nextView.setSelection(0);

                }
                return "";
            } else if (keep >= end - start) {
                return null; // keep original
            } else {
                if (source.length() > keep)
                    if (nextView != null && getText().length() < getMaxLength()) {
                        String s = source.toString();
                        String temp = editText.getText().toString();
                        int selection = editText.getSelectionStart();
                        temp = temp.substring(0, selection) + source + temp.substring(selection);
                        editText.setText(temp.substring(0, mMax));

                        temp = temp.substring(mMax);
                        if (selection + source.length() <= mMax)
                            editText.setSelection(selection + source.length());
                        else {
                            nextView.requestFocus();
                            nextView.setSelection(0);

                        }
                        if (temp.length() > 0) {
                            nextView.getEditableText().insert(0, temp);
                            int nextLength = getLength(index + 1);
                            nextView.setSelection(temp.length() < nextLength ? temp.length() : nextLength);
                        } else
                            nextView.setSelection(0);
                        return "";
                    }

                keep += start;
                if (Character.isHighSurrogate(source.charAt(keep - 1))) {
                    --keep;
                    if (keep == start) {
                        return "";
                    }
                }


                return source.subSequence(start, keep);
            }
        }

    }

    public interface OnCardPrefixListener {

        void onCardUpdate(CardPrefix cardPrefix);

    }

}