package com.ssomai.android.scalablelayout;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;

public class ScalableLayout extends FrameLayout {
    private static final float Default_Scale_Base_Width = 100f;
    private static final float Default_Scale_Base_Height = 100f;
//    private static final int Default_Scale_Base_BasePosition = Gravity.LEFT | Gravity.TOP;

    private static final float Default_Scale_Left = 0f;
    private static final int Default_Scale_Left_BasePosition = 0;
    private static final float Default_Scale_Top = 0f;
    private static final int Default_Scale_Top_BasePosition = 0;
    private static final float Default_Scale_Width = 100f;
    private static final float Default_Scale_Height = 100f;
    private static final float Default_TextView_WrapContent_Scale_MaxWidth = -1f;

    private static final float Default_Scale_TextSize = 100f;

    private float mScale_Root_Width 	= Default_Scale_Base_Width;
    private float mScale_Root_Height 	= Default_Scale_Base_Height;
    private float mRatioOfWidthHeight 	= mScale_Root_Height / mScale_Root_Width;

//    private int mScale_Root_BasePosition = Default_Scale_Base_BasePosition;

    public float getScaleWidth() { return mScale_Root_Width; }
    public float getScaleHeight() { return mScale_Root_Height; }

    public void setScaleWidth(float pWidth) { setScaleSize(pWidth, mScale_Root_Height); }
    public void setScaleHeight(float pHeight) { setScaleSize(mScale_Root_Width, pHeight); }
    public void setScaleSize(float pWidth, float pHeight) {
        setScaleSize(pWidth, pHeight, true);
    }
    private void setScaleSize(float pWidth, float pHeight, boolean pWithInvalidate) {
        mScale_Root_Width = pWidth;
        mScale_Root_Height = pHeight;
        mRatioOfWidthHeight = mScale_Root_Height / mScale_Root_Width;
        if(pWithInvalidate) {
            postDelayedRequestLayout();
        }
    }

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            requestLayout();
            forceLayout();
        }
    };
    private long mLastRequestPostTime = 0;
    private void postDelayedRequestLayout() {
        if(isInEditMode()) {
            return;
        }

        long now = System.currentTimeMillis();
        if(mLastRequestPostTime < now - 50 || now < mLastRequestPostTime) {
            mLastRequestPostTime = now;
            postDelayed(mRunnable, 10);
        }
    }

    /**
     * Simple constructor to use when creating a view from code.
     * Scale_Width, Scale_Height uses a default value of 100.
     *

     * code에서 ScalableLayout을 생성할 때 사용하는 constructor.
     * Scale_Width, Scale_Height를 기본값인 100으로 세팅함.
     *
     * @param pContext
     */
    public ScalableLayout(Context pContext) {
        this(pContext, Default_Scale_Base_Width, Default_Scale_Base_Height);
    }

    /**
     * Constructor that is called when inflating a view from XML.
     *

     * XML에서 ScalableLayout을 생성할 때 사용하는 constructor.
     *
     * @param pContext
     * @param pAttrs
     */
    public ScalableLayout(Context pContext, AttributeSet pAttrs) {
        this(pContext, pAttrs,
                pContext.obtainStyledAttributes(pAttrs, com.ssomai.android.scalablelayout.R.styleable.ScalableLayout).getFloat(com.ssomai.android.scalablelayout.R.styleable.ScalableLayout_scale_base_width, Default_Scale_Base_Width),
                pContext.obtainStyledAttributes(pAttrs, com.ssomai.android.scalablelayout.R.styleable.ScalableLayout).getFloat(com.ssomai.android.scalablelayout.R.styleable.ScalableLayout_scale_base_height, Default_Scale_Base_Height)
        );
    }

    /**
     * Simple constructor to use when creating a view from code.
     *

     * code에서 ScalableLayout을 생성할 때 사용하는 constructor.
     *
     * @param pContext
     * @param pScale_Width Scale width of ScalableLayout.
     * @param pScale_Height Scale height of ScalableLayout.
     */
    public ScalableLayout(Context pContext, float pScale_Width, float pScale_Height) {
        this(pContext, null, pScale_Width, pScale_Height);
    }
//    public ScalableLayout(Context pContext, float pScale_Width, float pScale_Height, int pScale_BasePosition) {
//        this(pContext, null, pScale_Width, pScale_Height, pScale_BasePosition);
//    }

    private ScalableLayout(Context pContext, AttributeSet pAttrs, float pScale_Width, float pScale_Height) {
        super(pContext, pAttrs);
        setScaleSize(pScale_Width, pScale_Height, true);
//        mScale_Root_BasePosition = pScale_BasePosition;

//        mTV_Log = new TextView(getContext());
//        mTV_Log.setBackgroundColor(0xFF333300);
//        addView(mTV_Log, new ScalableLayout.LayoutParams(0, 0, pScale_Width, pScale_Height));
    }

    /**
     * @param pChild
     * @return Scalable.LayoutParams of pChild
     */
    public ScalableLayout.LayoutParams getChildLayoutParams(View pChild) {
        ViewGroup.LayoutParams lp = pChild.getLayoutParams();
        if(lp == null) {
            lp = generateDefaultLayoutParams();
            pChild.setLayoutParams(lp);
        }
        if(lp instanceof ScalableLayout.LayoutParams == false) {
            throw new IllegalArgumentException("pChild has not ScalableLayout.LayoutParams "+pChild.getLayoutParams());
        }
        return (ScalableLayout.LayoutParams) pChild.getLayoutParams();
    }

    /**
     * sets TextSize of TextView and EdiText to scale automatically
     * @param pTextView
     * @param pScale_TextSize
     */
    public void setScale_TextSize(TextView pTextView, float pScale_TextSize) {
        getChildLayoutParams(pTextView).setScale_TextSize(pScale_TextSize);
    }
    /**
     * @param pTextView target textview to wrap content
     * @param pDirection
     * @param pRescaleSurrounded
     */
    public void setTextView_WrapContent(TextView pTextView, TextView_WrapContent_Direction pDirection, boolean pRescaleSurrounded) {
        setTextView_WrapContent(pTextView, pDirection, pRescaleSurrounded, true);
    }
    public void setTextView_WrapContent(TextView pTextView, TextView_WrapContent_Direction pDirection, boolean pRescaleSurrounded, boolean pMoveSibligs) {
        getChildLayoutParams(pTextView).setTextView_WrapContent(pDirection, pRescaleSurrounded, pMoveSibligs);
        refreshTextChangedListener(pTextView);
    }
    private TextWatcher mTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence pS, int pStart, int pBefore, int pCount) {
            postDelayedRequestLayout();
        }
        @Override
        public void beforeTextChanged(CharSequence pS, int pStart, int pCount, int pAfter) { }
        @Override
        public void afterTextChanged(Editable pS) {
            postDelayedRequestLayout();
        }
    };
    private void refreshTextChangedListener(TextView pTextView) {
        LayoutParams lSLLP = getChildLayoutParams(pTextView);

        try {
            pTextView.removeTextChangedListener(mTextWatcher);
        } catch (Throwable e) {
            ex(e);
        }
        if(lSLLP.mTextView_WrapContent_Direction != TextView_WrapContent_Direction.None) {
            pTextView.addTextChangedListener(mTextWatcher);
        }
    }

    @Override
    public void addView(View pChild) { addView(pChild, getChildCount()); }
    @Override
    public void addView(View pChild, int pIndex) { addView(pChild, pIndex, generateDefaultLayoutParams()); }
    @Override
    public void addView(View pChild, int pWidth, int pHeight) { addView(pChild, new ViewGroup.LayoutParams(pWidth, pHeight)); }
    @Override
    public void addView(View pChild, ViewGroup.LayoutParams pParams) { addView(pChild, getChildCount(), pParams); }
    @Override
    public void addView(View pChild, int pIndex, ViewGroup.LayoutParams pParams) {
        if(pParams instanceof ScalableLayout.LayoutParams) {
            addView_Final(pChild, pIndex, (ScalableLayout.LayoutParams) pParams);
        } else {
            addView_Final(pChild, pIndex, generateLayoutParams(pParams));
        }
    }
    public void addView(View pChild, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        addView_Final(pChild, getChildCount(), new ScalableLayout.LayoutParams(pScale_Left, pScale_Top, pScale_Width, pScale_Height));
    }
    private final void addView_Final(View pChild, int pIndex, ScalableLayout.LayoutParams pScaledLayoutParams) {
        super.addView(pChild, pIndex, pScaledLayoutParams);
    }

    @Override
    protected ScalableLayout.LayoutParams generateDefaultLayoutParams() {
        return new ScalableLayout.LayoutParams(0f, 0f, getScaleWidth(), getScaleHeight());
    }
    @Override
    public ScalableLayout.LayoutParams generateLayoutParams(AttributeSet pAttrs) {
        return new ScalableLayout.LayoutParams(getContext(), pAttrs);
    }
    @Override
    protected ScalableLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams pP) {
        if(pP instanceof ScalableLayout.LayoutParams) {
            return (ScalableLayout.LayoutParams) pP;
        }
        return new ScalableLayout.LayoutParams(pP);
    }

    /**
     * move childView inside ScalableLayout
     * @param pChildView view to move. should be child of ScaleLayout
     * @param pScale_Left
     * @param pScale_Top
     */
    public void moveChildView(View pChildView, float pScale_Left, float pScale_Top) {
        ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(pChildView);
        lSLLP.mScale_Left = pScale_Left;
        lSLLP.mScale_Top = pScale_Top;
        postInvalidate();
    }

    /**
     * move and resize childView inside ScalableLayout
     * @param pChildView view to move. should be child of ScaleLayout
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     */
    public void moveChildView(View pChildView, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(pChildView);
        lSLLP.mScale_Left = pScale_Left;
        lSLLP.mScale_Top = pScale_Top;
        lSLLP.mScale_Width = pScale_Width;
        lSLLP.mScale_Height = pScale_Height;
        postInvalidate();
    }

    @Override
    protected void onMeasure(int pWidthMeasureSpec, int pHeightMeasureSpec) {
//		log("onMeasure ================ Start "+this.toString());

//		float 	lRoot_Width 					= 0;
        float 	lRoot_Width_Max 				= 0;
        int 	lRoot_MeasureSpec_WidthMode 	= MeasureSpec.getMode(pWidthMeasureSpec);

//		float 	lRoot_Height 					= 0;
        float 	lRoot_Height_Max 				= 0;
        int 	lRoot_MeasureSpec_HeightMode 	= MeasureSpec.getMode(pHeightMeasureSpec);

        // get condition of ScalableLayout
        // ScalableLayout이 지켜야 되는 조건들을 확인함
        {
            int lRoot_MeasureSpec_Width 	= MeasureSpec.getSize(pWidthMeasureSpec);
            int lRoot_MeasureSpec_Height 	= MeasureSpec.getSize(pHeightMeasureSpec);
            switch (lRoot_MeasureSpec_WidthMode) {
                case MeasureSpec.EXACTLY: {
//				log("  onMeasure Width  Exactly "+lRoot_Width+" = min("+mScale_Root_Width+", "+lRoot_MeasureSpec_Width+")");
                    lRoot_Width_Max = lRoot_MeasureSpec_Width;
//				log("  onMeasure Width  Exactly = "+lRoot_Width_Max);
//				lRoot_Width 		= lRoot_MeasureSpec_Width;
                } break;
                case MeasureSpec.AT_MOST: {
                    if (lRoot_MeasureSpec_HeightMode == MeasureSpec.EXACTLY) {
//					lRoot_Width_Max 	= lRoot_MeasureSpec_Width;
                        lRoot_Width_Max = Float.MAX_VALUE;
                    } else {
                        lRoot_Width_Max = lRoot_MeasureSpec_Width;
                    }
//				lRoot_Width 		= lRoot_MeasureSpec_Width;
//				log("  onMeasure Width  AtMost  = "+lRoot_Width_Max);
                } break;
                default: {
//				log("  onMeasure Width  Unspecified "+lRoot_Width_Max+" = "+mScale_Root_Width);
//				lRoot_Width 		= mScale_Root_Width;
//				lRoot_Width_Max 	= Float.MAX_VALUE;
//				lRoot_Width 		= lRoot_WidthSize;
//				lRoot_Width_Max 	= lRoot_WidthSize;
//				break;
//				throw new IllegalArgumentException(String.format("  onMeasure Width  Unspecified %08X", lRoot_MeasureSpec_Width));
                    lRoot_Width_Max = Float.MAX_VALUE;
//				throw new IllegalArgumentException(String.format("  onMeasure Width Unspecified %08X lRoot_MeasureSpec_WidthMode:%d lRoot_MeasureSpec_Width:%d",
//					lRoot_MeasureSpec_Width, lRoot_MeasureSpec_WidthMode, lRoot_MeasureSpec_Width));
                }
            }

            switch (lRoot_MeasureSpec_HeightMode) {
                case MeasureSpec.EXACTLY: {
                    lRoot_Height_Max = lRoot_MeasureSpec_Height;
//				log("  onMeasure Height Exactly = "+lRoot_Height_Max);
                } break;
                case MeasureSpec.AT_MOST: {
                    if (lRoot_MeasureSpec_WidthMode == MeasureSpec.EXACTLY) {
//					lRoot_Height_Max 	= lRoot_MeasureSpec_Height;
                        lRoot_Height_Max = Float.MAX_VALUE;
                    } else {
                        lRoot_Height_Max = lRoot_MeasureSpec_Height;
                    }
//				log("  onMeasure Height AtMost  = "+lRoot_Height_Max);
                } break;
                default: {
//				lRoot_Height = lRoot_Width * mRatioOfWidthHeight;
////			log("  onMeasure Height Unspecified "+lRootHeight+" = "+lRootWidth+"*"+mRatioOfWidthHeight);
//				break;
                    lRoot_Height_Max = Float.MAX_VALUE;
//				throw new IllegalArgumentException(String.format("  onMeasure Height Unspecified %08X lRoot_MeasureSpec_HeightMode:%d lRoot_MeasureSpec_Height:%d",
//					lRoot_MeasureSpec_Height, lRoot_MeasureSpec_HeightMode, lRoot_MeasureSpec_Height));
                }
            }
        }

//		log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d Root:(%5.3f, %5.3f)", (lRootWidth/mScale_Full_Width), getChildCount(), lRootWidth, lRootHeight));
//		log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d", (lRootWidth/mScale_Full_Width), getChildCount()));

        float 	lRoot_Width 					= 0;
        float 	lRoot_Height 					= 0;
        for(int tryi = 0 ; tryi < 3 ; tryi++) {
            float lScale_Ratio_Pre = Math.min(lRoot_Width_Max / mScale_Root_Width, lRoot_Height_Max / mScale_Root_Height);
//			log(String.format("onMeasure 1 lScale_Ratio:%5.3f Scale_Ratio:(%5.3f, %5.3f) Scale:(%5.3f, %5.3f)",
//				lScale_Ratio_Pre, (lRoot_Width_Max/mScale_Root_Width), (lRoot_Height_Max / mScale_Root_Height), mScale_Root_Width, mScale_Root_Height));

            //////////////////////// Text Measure
            // TODO english
            // ScalableLayout내의 TextView중에 TextView_WrapContent_Direction 세팅되어 있을 경우 ScalableLayout의 Scale Size와 이웃한 View들의 Scalable LayoutParams를 재계산
            for (int i=0;i<getChildCount();i++) {
                View lView = getChildAt(i);

                if(lView instanceof TextView) {
                    updateTextViewSize((TextView)lView, lScale_Ratio_Pre);
                }
            }

            // calculate final size of ScalableLayout
            // ScalableLayout의 최종적인 크기를 결정지음
            float lScale_Ratio_Post = Math.min(lRoot_Width_Max / mScale_Root_Width, lRoot_Height_Max / mScale_Root_Height);

            {
                lRoot_Width 	= mScale_Root_Width * lScale_Ratio_Post;
                lRoot_Height 	= mScale_Root_Height * lScale_Ratio_Post;
            }

            {
                // 최종적으로 배치 위치를 세팅하기 위해 각 자식 뷰들의 layoutparams를 업데이트함
                // update layoutparams of each child view to arrange finally

//				log("  onMeasure ("+lRoot_Width+","+lRoot_Height+") Ratio:"+mRatioOfWidthHeight+" lScale_TextViewMeasure_Post:"+lScale_Ratio_Post+" lTopMarginFromWeight:"+lTopMarginFromWeight);
                for (int i=0;i<getChildCount();i++) {
                    View lView = getChildAt(i);

                    ScalableLayout.LayoutParams lParams = getChildLayoutParams(lView);

                    boolean dif = false;

                    int lParams_Left = Math.round(lScale_Ratio_Post * lParams.getScale_Left());
                    if(lParams.leftMargin != lParams_Left)
                        dif = true;
                    lParams.leftMargin = lParams_Left;

                    int lParams_Width = Math.round(lScale_Ratio_Post * lParams.getScale_Width());
                    if(lParams.width != lParams_Width)
                        dif = true;
                    lParams.width = lParams_Width;
                    switch (lParams.mScale_Left_BasePosition) {
                        case 0: { // left
                        } break;
                        case 1: { // center
                            lParams.leftMargin -= lParams.width / 2;
                        } break;
                        case 2: { // right
                            lParams.leftMargin -= lParams.width;
                        } break;
                    }

//                    log(" lTopMarginFromWeight:"+lTopMarginFromWeight);
//                    int lParams_Top = Math.round(lScale_Ratio_Post * lParams.mScale_Top + lTopMarginFromWeight);
                    int lParams_Top = Math.round(lScale_Ratio_Post * lParams.getScale_Top());
                    if(lParams.topMargin != lParams_Top)
                        dif = true;
                    lParams.topMargin = lParams_Top;

                    int lParams_Height = Math.round(lScale_Ratio_Post * lParams.getScale_Height());
//                    log("lParams_Height "+lParams_Height+" "+lScale_Ratio_Post+" "+lParams_Height);
                    if(lParams.height != lParams_Height)
                        dif = true;
//                    lParams.height = lParams_Height;
                    lParams.height = lParams_Height;
                    switch (lParams.mScale_Top_BasePosition) {
                        case 0: { // left
                        } break;
                        case 1: { // center
                            lParams.topMargin -= lParams.height / 2;
                        } break;
                        case 2: { // right
                            lParams.topMargin -= lParams.height;
                        } break;
                    }

                    if(lParams.mScale_TextSize != -1) {
                        if(lView instanceof TextView) {
                            TextView v = (TextView) lView;
                            if(isDifferentSufficiently(lParams.mScale_TextSize * lScale_Ratio_Post, v.getTextSize())) {
                                v.setTextSize(TypedValue.COMPLEX_UNIT_PX, lParams.mScale_TextSize * lScale_Ratio_Post);
                            }
                        }
                    }

                    if(dif) {
                        lView.setLayoutParams(lParams);
                    }
                }
            }
            if(isDifferentSufficiently(lScale_Ratio_Post, lScale_Ratio_Pre, 1.01f) == false) {
                break;
            }
        }

//		log("onMeasure ================ End   "+this.toString());
        super.onMeasure(MeasureSpec.makeMeasureSpec(Math.round(lRoot_Width), lRoot_MeasureSpec_WidthMode), MeasureSpec.makeMeasureSpec(Math.round(lRoot_Height), lRoot_MeasureSpec_HeightMode));
        setMeasuredDimension(Math.round(lRoot_Width), Math.round(lRoot_Height));
    }

    private boolean isDifferentSufficiently(float pNew, float pOld) {
        return isDifferentSufficiently(pNew, pOld, 1.1f);
    }
    private boolean isDifferentSufficiently(float pNew, float pOld, float pDiffDelta) {
        if( pNew < pOld / pDiffDelta || pOld * pDiffDelta < pNew ) {
            return true;
        }
        return false;
    }

    private enum ViewPosition {
        Top,
        Bottom,
        Left,
        Right,
        Surrounded,
        Nothing
    }
    private ViewPosition getViewPosition(LayoutParams pSrc, LayoutParams pDst) {
        // 위에 있는 경우
        if(pSrc.getScale_Top() >= pDst.getScale_Bottom() &&
                (
                        ( pSrc.getScale_Left() <= pDst.getScale_Left() && pDst.getScale_Left() <= pSrc.getScale_Right())
                                ||
                                ( pSrc.getScale_Left() <= pDst.getScale_Right() && pDst.getScale_Right() <= pSrc.getScale_Right())
                                ||
                                ( pDst.getScale_Left() <= pSrc.getScale_Left() && pSrc.getScale_Right() <= pDst.getScale_Right())
                )) {
            return ViewPosition.Top;
        }
        // 아래에 있는 경우
        else if(pSrc.getScale_Bottom() <= pDst.getScale_Top() &&
                (
                        ( pSrc.getScale_Left() <= pDst.getScale_Left() && pDst.getScale_Left() <= pSrc.getScale_Right())
                                ||
                                ( pSrc.getScale_Left() <= pDst.getScale_Right() && pDst.getScale_Right() <= pSrc.getScale_Right())
                                ||
                                ( pDst.getScale_Left() <= pSrc.getScale_Left() && pSrc.getScale_Right() <= pDst.getScale_Right())
                )) {
            return ViewPosition.Bottom;
        }
        // 왼쪽에 있는 경우
        else if(pSrc.getScale_Left() >= pDst.getScale_Right() &&
                (
                        ( pSrc.getScale_Top() <= pDst.getScale_Top() && pDst.getScale_Top() <= pSrc.getScale_Bottom())
                                ||
                                ( pSrc.getScale_Top() <= pDst.getScale_Bottom() && pDst.getScale_Bottom() <= pSrc.getScale_Bottom())
                                ||
                                ( pSrc.getScale_Top() >= pDst.getScale_Top() &&  pDst.getScale_Bottom() >= pSrc.getScale_Bottom())
                )) {
            return ViewPosition.Left;
        }
        // 오른쪽에 있는 경우
        else if(pSrc.getScale_Right() <= pDst.getScale_Left() &&
                (
                        ( pSrc.getScale_Top() <= pDst.getScale_Top() && pDst.getScale_Top() <= pSrc.getScale_Bottom())
                                ||
                                ( pSrc.getScale_Top() <= pDst.getScale_Bottom() && pDst.getScale_Bottom() <= pSrc.getScale_Bottom())
                                ||
                                ( pSrc.getScale_Top() >= pDst.getScale_Top() &&  pDst.getScale_Bottom() >= pSrc.getScale_Bottom())
                )) {
            return ViewPosition.Right;
        }
        // 감싸고 있는 경우
        else if(pDst.getScale_Top() <= pSrc.getScale_Top() &&
                pDst.getScale_Left() <= pSrc.getScale_Left() &&
                pDst.getScale_Right() >= pSrc.getScale_Right() &&
                pDst.getScale_Bottom() >= pSrc.getScale_Bottom()) {
            return ViewPosition.Surrounded;
        }

        return ViewPosition.Nothing;
    }

    private void updateTextViewSize(TextView pTV_Text, float pScale_TextViewMeasure_Pre) {
        refreshTextChangedListener(pTV_Text);

        ScalableLayout.LayoutParams lTV_SLLP = getChildLayoutParams(pTV_Text);
        TextView_WrapContent_Direction lTextView_WrapContent_Direction = lTV_SLLP.mTextView_WrapContent_Direction;
        if(lTextView_WrapContent_Direction == TextView_WrapContent_Direction.None) {
            return;
        }

        float lTextView_ScaleWidth_Old 		= lTV_SLLP.getScale_Width();
        float lTextView_ScaleHeight_Old 	= lTV_SLLP.getScale_Height();

        float lTextView_ScaleWidth_New 		= lTextView_ScaleWidth_Old;
        float lTextView_ScaleHeight_New 	= lTextView_ScaleHeight_Old;
        {
            // get new scale size
            // 새로운 Scale 크기를 가져옴
            float lTextSize_New = lTV_SLLP.mScale_TextSize * pScale_TextViewMeasure_Pre;
//			log("updateTextViewHeight 1.1 lTextView_ScaleHeight_Old:"+lTextView_ScaleHeight_Old+" getScaleHeight():"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
//			log("updateTextViewHeight 1.2 lTextSize_New:"+lTextSize_New+" pTV_Text.getTextSize():"+pTV_Text.getTextSize());
            if(isDifferentSufficiently(lTextSize_New, pTV_Text.getTextSize())) {
//				log("updateTextViewHeight 1.2.1 TextSize:"+pTV_Text.getTextSize()+"->"+lTextSize_New);
                pTV_Text.setTextSize(TypedValue.COMPLEX_UNIT_PX, lTextSize_New);
            }

            switch (lTextView_WrapContent_Direction) {
                case Top:
                case Bottom:
                case Center_Vertical:
                {
                    int widthMeasureSpec 		= MeasureSpec.makeMeasureSpec((int) (lTextView_ScaleWidth_Old * pScale_TextViewMeasure_Pre), MeasureSpec.EXACTLY);
                    int heightMeasureSpec 		= MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);

                    float lTextView_Height_New 	= pTV_Text.getMeasuredHeight() * 1.01f;
                    lTextView_ScaleHeight_New 	= lTextView_Height_New / pScale_TextViewMeasure_Pre;
                } break;
                case Left:
                case Right:
                case Center_Horizontal:
                {
                    int widthMeasureSpec 		= MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                    int heightMeasureSpec 		= MeasureSpec.makeMeasureSpec((int) (lTextView_ScaleHeight_Old * pScale_TextViewMeasure_Pre), MeasureSpec.EXACTLY);
                    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);

                    float lTextView_Width_New 	= pTV_Text.getMeasuredWidth() * 1.01f;
                    lTextView_ScaleWidth_New 	= lTextView_Width_New / pScale_TextViewMeasure_Pre;
//                    lTextView_ScaleWidth_New = 1000;
//                    // TODO 2017.08.04 wrapcontent_scale_maxwidth 작업중, 현재 preview에서 왜 바로 적용이 안 되는지 원인 파악이 안 된 상태
                    if(lTV_SLLP.getTextView_WrapContent_Scale_MaxWidth() != Default_TextView_WrapContent_Scale_MaxWidth) {
                        if(lTextView_ScaleWidth_New > lTV_SLLP.getTextView_WrapContent_Scale_MaxWidth()) {
                            lTextView_ScaleWidth_New = lTV_SLLP.getTextView_WrapContent_Scale_MaxWidth();
                        }
//                        else {
//                            lTextView_ScaleWidth_New = 50;
//                        }
                    }
//                    else {
//                        lTextView_ScaleWidth_New = -1;
////                        lTextView_ScaleWidth_New = lTV_SLLP.getTextView_WrapContent_Scale_MaxWidth();
////                        lTextView_ScaleWidth_New = 0;
//                    }

                    // log("updateTextViewWidth 1.3 lTextView_NewWidth:"+lTextView_Width_New+" lTextView_ScaleWidth_New:"+lTextView_ScaleWidth_New);
                } break;
                default:
                    break;
            }
        }
//		log("updateTextViewWidth 1.4 " +
//			" lTextView_ScaleWidth_New:"+lTextView_ScaleWidth_New+" lTextView_ScaleWidth_Old:"+lTextView_ScaleWidth_Old+
//			" lTextView_ScaleHeight_New:"+lTextView_ScaleHeight_New+" lTextView_ScaleHeight_Old:"+lTextView_ScaleHeight_Old);

        if(
                isDifferentSufficiently(lTextView_ScaleWidth_New, lTextView_ScaleWidth_Old) == false &&
                isDifferentSufficiently(lTextView_ScaleHeight_New, lTextView_ScaleHeight_Old) == false) {
            return;
        }

        float lTextView_ScaleWidth_Diff 	= lTextView_ScaleWidth_New - lTextView_ScaleWidth_Old;
//        log("lTextView_ScaleWidth_Diff:"+lTextView_ScaleWidth_Diff+" "+lTextView_ScaleWidth_New+" "+lTextView_ScaleWidth_Old);
        float lTextView_ScaleHeight_Diff 	= lTextView_ScaleHeight_New - lTextView_ScaleHeight_Old;
        if(lTV_SLLP.mTextView_WrapContent_MoveSiblings == true) {
            for(int i = 0; i < getChildCount(); i++) {
                View v = getChildAt(i);
                if(v == pTV_Text) {
                    continue;
                }
                ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(v);
                ViewPosition position = getViewPosition(lTV_SLLP, lSLLP);

                switch (lTextView_WrapContent_Direction) {
                    case Top: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Top: {
                                } break;
                                case Surrounded: {
                                    moveChildView(v,
                                            lSLLP.getScale_Left(), lSLLP.getScale_Top(),
                                            lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()+lTextView_ScaleHeight_Diff);
                                } break;
                            }
                        } else {
                            switch (position) {
                                case Top: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()-lTextView_ScaleHeight_Diff);
                                } break;
                            }
                        }
                    } break;
                    case Bottom: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                           switch (position) {
                               case Bottom: {
                                   moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff);
                               } break;
                               case Surrounded: {
                                   moveChildView(v,
                                            lSLLP.getScale_Left(), lSLLP.getScale_Top(),
                                            lSLLP.getScale_Width(), lSLLP.getScale_Height() + lTextView_ScaleHeight_Diff);
                               } break;
                           }
                        } else {
                            switch (position) {
                                case Bottom: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff);
                                } break;
                            } break;
                        }
                    } break;
                    case Center_Vertical: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Top: {
                                } break;
                                case Bottom: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff);
                                } break;
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff / 2, lSLLP.getScale_Width(), lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                            switch (position) {
                                case Top: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() - lTextView_ScaleHeight_Diff / 2);
                                } break;
                                case Bottom: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff / 2);
                                } break;
                            }
                        }
                    } break;
                    case Left: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Left: {
                                } break;
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                            }
                        } else {
                            switch (position) {
                                case Left: {
                                    moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                            }
                        }
                    } break;
                    case Right: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Right: {
                                    moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                            switch (position) {
                                case Right: {
                                    moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                            }
                        }
                    } break;
                    case Center_Horizontal: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Left: {
                                } break;
                                case Right: {
                                    moveChildView(v, lSLLP.getScale_Left() + lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width() + lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left() + lTextView_ScaleWidth_Diff / 2, lSLLP.getScale_Top(), lSLLP.getScale_Width(), lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                            switch (position) {
                                case Left: {
                                    moveChildView(v, lSLLP.getScale_Left() - lTextView_ScaleWidth_Diff / 2, lSLLP.getScale_Top());
                                } break;
                                case Right: {
                                    moveChildView(v, lSLLP.getScale_Left() + lTextView_ScaleWidth_Diff / 2, lSLLP.getScale_Top());
                                } break;
                            }
                        }
                    } break;
                    default: {
                    } break;
                }
            }
        } else {
            for(int i = 0; i < getChildCount(); i++) {
                View v = getChildAt(i);
                if(v == pTV_Text) {
                    continue;
                }
                ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(v);
                ViewPosition position = getViewPosition(lTV_SLLP, lSLLP);

                switch (lTextView_WrapContent_Direction) {
                    case Top: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v,
                                            lSLLP.getScale_Left(), lSLLP.getScale_Top(),
                                            lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()+lTextView_ScaleHeight_Diff);
                                } break;
                            }
                        } else {
                        }
                    } break;
                    case Bottom: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v,
                                            lSLLP.getScale_Left(), lSLLP.getScale_Top(),
                                            lSLLP.getScale_Width(), lSLLP.getScale_Height() + lTextView_ScaleHeight_Diff);
                                } break;
                            }
                        } else {
                        }
                    } break;
                    case Center_Vertical: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top() + lTextView_ScaleHeight_Diff / 2, lSLLP.getScale_Width(), lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                        }
                    } break;
                    case Left: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                                } break;
                            }
                        } else {
                        }
                    } break;
                    case Right: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                        }
                    } break;
                    case Center_Horizontal: {
                        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                            switch (position) {
                                case Surrounded: {
                                    moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width() + lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                                } break;
                                default: {
                                    moveChildView(v, lSLLP.getScale_Left() + lTextView_ScaleWidth_Diff / 2, lSLLP.getScale_Top(), lSLLP.getScale_Width(), lSLLP.getScale_Height());
                                } break;
                            }
                        } else {
                        }
                    } break;
                    default: {
                    } break;
                }
            }
        }

        switch (lTextView_WrapContent_Direction) {
            case Top: {
                if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
                } else {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top() - lTextView_ScaleHeight_Diff, lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
                }
            } break;
            case Bottom: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
            } break;
            case Center_Vertical: {
                if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
                } else {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top() - lTextView_ScaleHeight_Diff / 2, lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
                }
            } break;
            case Left: {
                if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
                } else {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left() - lTextView_ScaleWidth_Diff, lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
                }
            } break;
            case Right: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
            } break;
            case Center_Horizontal: {
                if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
                } else {
                    moveChildView(pTV_Text, lTV_SLLP.getScale_Left() - lTextView_ScaleWidth_Diff / 2, lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
                }
            } break;
            default:
                break;
        }

        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
//			log("setScaleSize From:"+getScaleHeight()+" To:"+(getScaleHeight()+lTextView_ScaleHeight_Diff)+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
            setScaleSize(getScaleWidth()+lTextView_ScaleWidth_Diff, getScaleHeight()+lTextView_ScaleHeight_Diff, false);
        }
    }

    public enum TextView_WrapContent_Direction {
        None(0), Left(10), Center_Horizontal(20), Right(30), Top(100), Center_Vertical(200), Bottom(300),
        ;
        int mValue = 0;
        TextView_WrapContent_Direction(int pValue) {
            mValue = pValue;
        }
    }
    /**
     * ScalableLayout.LayoutParams
     */
    public static class LayoutParams extends FrameLayout.LayoutParams {
//        public CharSequence mTest;

        public LayoutParams(Context pContext, AttributeSet pAttrs) {
            super(pContext, pAttrs);

            TextView_WrapContent_Direction dir = TextView_WrapContent_Direction.None;

            // 2017.09.05 XML preview 안되던 현상  <eat-comment/> 덕분인듯?
            // 그건 아닌듯 그냥 build후에 하니까 되는듯
                TypedArray attrs_view = pContext.obtainStyledAttributes(pAttrs, com.ssomai.android.scalablelayout.R.styleable.View2);
            float lScale_Left = attrs_view.getFloat(com.ssomai.android.scalablelayout.R.styleable.View2_scale_left, Default_Scale_Left);
                int lLeft_BasePosition = attrs_view.getInteger(com.ssomai.android.scalablelayout.R.styleable.View2_scale_left_baseposition, Default_Scale_Left_BasePosition);
            float lScale_Top = attrs_view.getFloat(com.ssomai.android.scalablelayout.R.styleable.View2_scale_top, Default_Scale_Top);
                int lTop_BasePosition = attrs_view.getInteger(com.ssomai.android.scalablelayout.R.styleable.View2_scale_top_baseposition, Default_Scale_Top_BasePosition);

                setScale_Left(lScale_Left);
                setScale_Left_BasePosition(lLeft_BasePosition);
                setScale_Top(lScale_Top);
                setScale_Top_BasePosition(lTop_BasePosition);
                setScale_Width(attrs_view.getFloat(com.ssomai.android.scalablelayout.R.styleable.View2_scale_width, Default_Scale_Width));
                setScale_Height(attrs_view.getFloat(com.ssomai.android.scalablelayout.R.styleable.View2_scale_height, Default_Scale_Height));
                setScale_TextSize(attrs_view.getFloat(com.ssomai.android.scalablelayout.R.styleable.View2_scale_textsize, Default_Scale_TextSize));

                TypedArray attrs_textview = pContext.obtainStyledAttributes(pAttrs, com.ssomai.android.scalablelayout.R.styleable.TextView);
                int dirstr = attrs_textview.getInteger(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_direction, 0);
                for (TextView_WrapContent_Direction d : TextView_WrapContent_Direction.values()) {
                    if (d.mValue == dirstr) {
                        dir = d;
                        break;
                    }
                }
                setTextView_WrapContent(dir,
                        attrs_textview.getBoolean(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_resizesurrounded, false),
                        attrs_textview.getBoolean(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_movesiblings, true));
                setTextView_WrapContent_Scale_MaxWidth(attrs_textview.getFloat(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_scale_maxwidth, Default_TextView_WrapContent_Scale_MaxWidth));

//            try {
//                TypedArray attrs_textview = pContext.obtainStyledAttributes(pAttrs, com.ssomai.android.scalablelayout.R.styleable.TextView);
//                int dirstr = attrs_textview.getInteger(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_direction, 0);
//                for (TextView_WrapContent_Direction d : TextView_WrapContent_Direction.values()) {
//                    if (d.mValue == dirstr) {
//                        dir = d;
//                        break;
//                    }
//                }
//                setTextView_WrapContent(dir,
//                        attrs_textview.getBoolean(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_resizesurrounded, false),
//                        attrs_textview.getBoolean(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_movesiblings, true));
//                setTextView_WrapContent_Scale_MaxWidth(attrs_textview.getFloat(com.ssomai.android.scalablelayout.R.styleable.TextView_textview_wrapcontent_scale_maxwidth, Default_TextView_WrapContent_Scale_MaxWidth));
////                setTextView_WrapContent_Scale_MaxWidth(100f);
//            } catch (Throwable e) {
//                ex(e);
//            }
        }
        public LayoutParams(
                float pScale_Left, float pScale_Top,
                float pScale_Width, float pScale_Height) {
            this(
                    pScale_Left, Default_Scale_Left_BasePosition,
                    pScale_Top, Default_Scale_Top_BasePosition,
                    pScale_Width, pScale_Height);
        }
        public LayoutParams(
                float pScale_Left, int pScale_Left_BasePosition,
                float pScale_Top, int pScale_Top_BasePosition,
                float pScale_Width, float pScale_Height) {
            this(
                    pScale_Left, pScale_Left_BasePosition,
                    pScale_Top, pScale_Top_BasePosition,
                    pScale_Width, pScale_Height, Default_Scale_TextSize, TextView_WrapContent_Direction.None, false, true);
        }

        private LayoutParams(
                float pScale_Left, int pScale_Left_BasePosition,
                float pScale_Top, int pScale_Top_BasePosition,
                float pScale_Width, float pScale_Height, float pScale_TextSize,
                TextView_WrapContent_Direction pTextView_WrapContent_Direction, boolean pTextView_WrapContent_ResizeSurrounded, boolean pTextView_WrapContent_MoveSiblings) {
            super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP);

            setScale_Left(pScale_Left);
            setScale_Left_BasePosition(pScale_Left_BasePosition);
            setScale_Top(pScale_Top);
            setScale_Top_BasePosition(pScale_Top_BasePosition);
            setScale_Width(pScale_Width);
            setScale_Height(pScale_Height);

            setScale_TextSize(pScale_TextSize);

            setTextView_WrapContent(pTextView_WrapContent_Direction, pTextView_WrapContent_ResizeSurrounded, pTextView_WrapContent_MoveSiblings);
        }

        private LayoutParams(android.view.ViewGroup.LayoutParams pLayoutParams) {
            this(
                    Default_Scale_Left, Default_Scale_Top,
                    Default_Scale_Width, Default_Scale_Height);

            width = pLayoutParams.width;
            height = pLayoutParams.height;
            layoutAnimationParameters = pLayoutParams.layoutAnimationParameters;
            gravity = Gravity.LEFT | Gravity.TOP;
        }

        @Override
        public String toString() {
            return String.format("%08x (%6.3f, %6.3f) (%6.3f, %6.3f)", hashCode(), getScale_Left(), getScale_Top(), getScale_Right(), getScale_Bottom());
        }
        private float mScale_Left = Default_Scale_Left;
        public float getScale_Left() { return mScale_Left; }
        public void setScale_Left(float pScale_Left) { mScale_Left = pScale_Left; }
        public float getScale_Right() { return getScale_Left()+getScale_Width(); }
        private int mScale_Left_BasePosition;
        public int getScale_Left_BasePosition() { return mScale_Left_BasePosition; }
        public void setScale_Left_BasePosition(int pScale_Left_BasePosition) { mScale_Left_BasePosition = pScale_Left_BasePosition; }

        private float mScale_Top = Default_Scale_Top;
        public float getScale_Top() { return mScale_Top; }
        public void setScale_Top(float pScale_Top) { mScale_Top = pScale_Top; }
        public float getScale_Bottom() { return getScale_Top()+getScale_Height(); }
        private int mScale_Top_BasePosition;
        public int getScale_Top_BasePosition() { return mScale_Top_BasePosition; }
        public void setScale_Top_BasePosition(int pScale_Top_BasePosition) { mScale_Top_BasePosition = pScale_Top_BasePosition; }

        private float mScale_Width = Default_Scale_Width;
        public float getScale_Width() { return mScale_Width; }
        public void setScale_Width(float pScale_Width) { mScale_Width = pScale_Width; }
        private float mScale_Height = Default_Scale_Height;
        public float getScale_Height() { return mScale_Height; }
        public void setScale_Height(float pScale_Height) { mScale_Height = pScale_Height; }

//        private float mScale_MaxWidth = Default_Scale_MaxWidth;
//        public float getScale_MaxWidth() { return mScale_MaxWidth; }
//        public void setScale_MaxWidth(float pScale_MaxWidth) { mScale_MaxWidth = pScale_MaxWidth; }
//        private float mScale_MaxHeight = Default_Scale_MaxHeight;
//        public float getScale_MaxHeight() { return mScale_MaxHeight; }
//        public void setScale_MaxHeight(float pScale_MaxHeight) { mScale_MaxHeight = pScale_MaxHeight; }

        private float mScale_TextSize = -1f;
        public float getScale_TextSize() { return mScale_TextSize; }
        public void setScale_TextSize(float pScale_TextSize) { mScale_TextSize = pScale_TextSize; }

        private float mTextView_WrapContent_Scale_MaxWidth = Default_TextView_WrapContent_Scale_MaxWidth;
        public float getTextView_WrapContent_Scale_MaxWidth() { return mTextView_WrapContent_Scale_MaxWidth; }
        public void setTextView_WrapContent_Scale_MaxWidth(float pTextView_WrapContent_Scale_MaxWidth) { mTextView_WrapContent_Scale_MaxWidth = pTextView_WrapContent_Scale_MaxWidth; }
        private TextView_WrapContent_Direction 		mTextView_WrapContent_Direction 		= TextView_WrapContent_Direction.None;
        private boolean 							mTextView_WrapContent_ResizeSurrounded 	= false;
        private boolean 							mTextView_WrapContent_MoveSiblings 		= true;
        public void setTextView_WrapContent(TextView_WrapContent_Direction pTextView_WrapContent_Direction, boolean pTextView_WrapContent_ResizeSurrounded, boolean pTextView_WrapContent_MoveSiblings) {
            mTextView_WrapContent_Direction 		= pTextView_WrapContent_Direction;
            mTextView_WrapContent_ResizeSurrounded 	= pTextView_WrapContent_ResizeSurrounded;
            mTextView_WrapContent_MoveSiblings 		= pTextView_WrapContent_MoveSiblings;
        }
    }

    //////////////// 편리하게 View를 추가하기 위한 함수들 //////////////
    /**
     * adds new TextView with (TextSize, Left, Top, Width, Height) parameters
     * DuplicateState is false by default.
     * @param pText
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created TextView instance
     */
    public TextView addNewTextView(String pText,
                                   float pScale_TextSize,
                                   float pScale_Left, float pScale_Top,
                                   float pScale_Width, float pScale_Height) {
        return addNewTextView(pText, pScale_TextSize, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }

    /**
     * adds new TextView with (TextSize, Left, Top, Width, Height, duplicateState) parameters
     * @param pText
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return created TextView instance
     */
    public TextView addNewTextView(String pText,
                                   float pScale_TextSize,
                                   float pScale_Left, float pScale_Top,
                                   float pScale_Width, float pScale_Height,
                                   boolean pDuplicateState) {
        TextView ret = new TextView(getContext());
        addView(ret, pScale_Left, pScale_Top, pScale_Width, pScale_Height);

        setScale_TextSize(ret, pScale_TextSize);
        ret.setText(pText);
        ret.setGravity(Gravity.CENTER);
        ret.setTextColor(Color.BLACK);
        ret.setDuplicateParentStateEnabled(pDuplicateState);

        return ret;
    }

    /**
     * adds new EditText with (TextSize, Left, Top, Width, Height) parameters
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created EditText instance
     */
    public EditText addNewEditText(float pScale_TextSize, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        EditText ret = new EditText(getContext());
        addView(ret, pScale_Left, pScale_Top, pScale_Width, pScale_Height);

        setScale_TextSize(ret, pScale_TextSize);
        ret.setGravity(Gravity.CENTER);
        ret.setTextColor(Color.BLACK);
        return ret;
    }

    /**
     * adds new ImageView with (Left, Top, Width, Height) parameters
     * Drawable is null by default.
     * DuplicateState is false by default.
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView((Drawable)null, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }

    /**
     * adds new ImageView with (Bitmap, Left, Top, Width, Height) parameters
     * sets Image of ImageView with Bitmap parameter
     * DuplicateState is false by default.
     * @param pBitmap
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(Bitmap pBitmap, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        BitmapDrawable bm = new BitmapDrawable(getResources(), pBitmap);
        return addNewImageView(bm, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (ResourceID, Left, Top, Width, Height) parameters
     * sets Image of ImageView with ResourceID parameter
     * DuplicateState is false by default.
     * @param pResID
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(int pResID, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView(pResID, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (ResourceID, Left, Top, Width, Height, DuplicateState) parameters
     * sets Image of ImageView with ResourceID parameter
     * @param pResID
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return created ImageView instance
     */
    public ImageView addNewImageView(int pResID, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height, boolean pDuplicateState) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return addNewImageView(
                new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), pResID, options)),
                pScale_Left, pScale_Top, pScale_Width, pScale_Height, pDuplicateState);
    }
    /**
     * adds new ImageView with (Drawable, Left, Top, Width, Height) parameters
     * sets Image of ImageView with Drawable parameter
     * DuplicateState is false by default.
     * @param drawable
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(Drawable drawable, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView(drawable, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (Drawable, Left, Top, Width, Height, DuplicateState) parameters
     * sets Image of ImageView with Drawable parameter
     * @param drawable
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return
     */
    public ImageView addNewImageView(Drawable drawable, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height, boolean pDuplicateState) {
        ImageView lNewImageView = new ImageView(getContext());
        lNewImageView.setImageDrawable(drawable);
        lNewImageView.setScaleType(ScaleType.FIT_XY);
        lNewImageView.setDuplicateParentStateEnabled(pDuplicateState);

        addView(lNewImageView, pScale_Left, pScale_Top, pScale_Width, pScale_Height);
        return lNewImageView;
    }

    ////////////////////////// Log
    @Override
    public String toString() {
        return String.format("{ScalableLayout:%08x}", this.hashCode());
    }
    void log(String pLog) {
        if(sLogTag_Global != null) {
            Log.e(sLogTag_Global, this+"] "+pLog);
        }
        if(mLogTag_This != null) {
            Log.e(mLogTag_This, this+"] "+pLog);
        }
    }
//    private static TextView mTV_Log;
//    private static void logS(String pLog) {
//        if(mTV_Log != null) {
//            mTV_Log.setText(pLog+"\n"+mTV_Log.getText());
//        }
//    }

    private String mLogTag_This = null;
    public String getLogTag_This() {
        return mLogTag_This;
    }
    /**
     * setLoggable("ScalableLayout");
     */
    public void setThisLoggable() {
        setThisLoggable("ScalableLayout");
    }
    /**
     * Log를 출력할수 있게함
     * @param pLogTag DDMS Log Tag를 지정
     */
    public void setThisLoggable(String pLogTag) {
        mLogTag_This = pLogTag;
    }

    private static String getStackTrace(StackTraceElement[] ste) {
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<ste.length;i++){
            sb.append("\t"+ste[i].toString()+"\n");
        }
        return sb.toString();
    }
    private static void ex(Throwable e) {

    }

    private static String sLogTag_Global = null;
    /**
     * setLoggable("ScalableLayout");
     */
    public static void setLoggable() {
        setLoggable("ScalableLayout");
    }
    /**
     * Log를 출력할수 있게함
     * @param pLogTag DDMS Log Tag를 지정
     */
    public static void setLoggable(String pLogTag) {
        sLogTag_Global = pLogTag;
    }

}


package com.ssomai.android.sample_scalablelayout;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

import com.ssomai.android.scalablelayout.ScalableLayout;

public class MainActivity extends ActionBarActivity {

//    private static final String DebugTag = "ScalableLayout_TestAndroid";
    private static final String DebugTag = "TestAndroid";
    private TextView mTV_Log;
    private ScalableLayout mSL;

    private static void log(String pLog) {
        Log.e(DebugTag, "MainActivity] " + pLog);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        ScalableLayout.setLoggable("MainActivity");
//        ScalableLayout.setLogger(new ScalableLayout.ILogger() {
//            @Override
//            public void log(String pLog) {
//                throw new NullPointerException();
////                mTV_Log.setText(pLog+"\n"+mTV_Log.getText()+"ssdfsdfsdfsdf");
//            }
//        });

		setContentView(R.layout.activity_main);
//        mTV_Log = (TextView) findViewById(R.id.tv_log);

////		ScalableLayout.setLoggable(DebugTag);
////
////		LinearLayout lLL = new LinearLayout(this);
////		lLL.setOrientation(LinearLayout.VERTICAL);
////		lLL.setBackgroundColor(Color.CYAN);
////		setContentView(lLL);
////
////		{
////			Button btn = new Button(this);
////			btn.setText("늘리기");
////			btn.setOnClickListener(new View.OnClickListener() {
////				@Override
////				public void onClick(View pV) {
////					mTV_Text.setText(mTV_Text.getText()+"ㅁㄷㄷㄷ디");
////				}
////			});
////			lLL.addView(btn);
////		}
////		{
////			Button btn = new Button(this);
////			btn.setText("줄이기");
////			btn.setOnClickListener(new View.OnClickListener() {
////				@Override
////				public void onClick(View pV) {
////					mTV_Text.setText(mTV_Text.getText().subSequence(0, mTV_Text.getText().length()/2));
////				}
////			});
////			lLL.addView(btn);
////		}
////
////
////		mSL = new ScalableLayout(this, 400, 400);
////		mSL.setBackgroundColor(Color.LTGRAY);
////		lLL.addView(mSL, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
////
////		ImageView iv = new ImageView(this);
////		iv.setImageResource(R.drawable.ic_launcher);
////		mSL.addView(iv, 200f, 30f, 50f, 50f);
////
////		ImageView iv_surrounded = new ImageView(this);
////		iv_surrounded.setBackgroundColor(Color.BLUE);
////		mSL.addView(iv_surrounded, 80f, 80f, 240f, 90f);
////
////		ImageView iv2 = new ImageView(this);
////		iv2.setImageResource(R.drawable.ic_launcher);
////		mSL.addView(iv2, 220f, 160f, 50f, 50f);
////
////		TextView tv3 = new TextView(this);
////		tv3.setText("test");
////		tv3.setBackgroundColor(Color.YELLOW);
////		mSL.addView(tv3, 0f, 210f, 60f, 30f);
////		mSL.setScale_TextSize(tv3, 20f);
////
////		TextView tv4 = new TextView(this);
////		tv4.setText("test");
////		tv4.setBackgroundColor(Color.YELLOW);
////		mSL.addView(tv4, 150f, 250f, 100f, 30f);
////		mSL.setScale_TextSize(tv4, 20f);
////
////		TextView tv5 = new TextView(this);
////		tv5.setText("test");
////		tv5.setBackgroundColor(Color.YELLOW);
////		mSL.addView(tv5, 350f, 300f, 100f, 30f);
////		mSL.setScale_TextSize(tv5, 20f);
////
////
////		mTV_Text = new TextView(this);
////		mTV_Text.setText("test");
////		mTV_Text.setBackgroundColor(Color.RED);
////		mSL.addView(mTV_Text, 40f, 170f, 100f, 0f);
////		mSL.setScale_TextSize(mTV_Text, 20f);
////		mSL.setTextView_WrapContent(mTV_Text, TextView_WrapContent_Direction.Bottom, true);
//
//
//
////		setContentView(R.layout.activity_main);
//
//        // init base width and height of scalable layout.
//        mSL = new ScalableLayout(this, 400, 500);
////		mSL = (ScalableLayout) findViewById(R.id.main_sl);
//        mSL.setBackgroundColor(Color.LTGRAY);
        ScalableLayout.setLoggable(DebugTag);
//
////		RelativeLayout rl = (RelativeLayout)findViewById(R.id.main_relativelayout);
////		RelativeLayout.LayoutParams rllp = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
////		rllp.addRule(RelativeLayout.BELOW, R.id.main_textview);
////		rl.addView(mSL, rllp);
//
//        setContentView(mSL);
//
//
//        // insert TextView
////		TextView tv = new TextView(this);
////		tv.setText("test");
////		tv.setBackgroundColor(Color.YELLOW);
////		mSL.addView(tv, 20f, 40f, 100f, 30f);
////		mSL.setScale_TextSize(tv, 20f);
//
//        // insert ImageView
//        ImageView iv = new ImageView(this);
//        iv.setImageResource(com.ssomai.android.sample_scalablelayout.R.drawable.ic_launcher);
//        mSL.addView(iv, 200f, 30f, 50f, 50f);
//
//        ImageView iv_surrounded = new ImageView(this);
//        iv_surrounded.setBackgroundColor(Color.BLUE);
//        mSL.addView(iv_surrounded, 80f, 80f, 240f, 90f);
////		mSL.addView(mTV_Text, 100, 100, 200, 50);
//
//
////		mTV_Text = (TextView) findViewById(R.id.sl_textview);
////		mTV_Text = new EditText(this) {
//////			@Override
//////			protected void onTextChanged(CharSequence pText, int pStart, int pLengthBefore, int pLengthAfter) {
//////			    super.onTextChanged(pText, pStart, pLengthBefore, pLengthAfter);
//////				log("getHeight "+getHeight2(getContext(), (String)pText, (int)getTextSize(), getWidth()));
//////				log("aa "+pText);
//////			}
////		};
////		mTV_Text.setBackgroundColor(Color.RED);
////		mTV_Text.setText("test");
//////		mTV_Text.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
//////			 @SuppressLint("NewApi")
//////			 @SuppressWarnings("deprecation")
//////			 @Override
//////			  public void onGlobalLayout() {
//////				log("onGlobalLayout "+mTV_Text.getWidth());
//////			   //now we can retrieve the width and height
////////			   int width = view.getWidth();
////////			   int height = view.getHeight();
//////			   //...
//////			   //do whatever you want with them
//////			   //...
//////			   //this is an important step not to keep receiving callbacks:
//////			   //we should remove this listener
//////			   //I use the function to remove it based on the api level!
//////
////////			if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
////////			    view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
////////			   else
////////			    view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//////			  }
//////			 });
////		mSL.addView(mTV_Text, 100, 100, 200, 50);
////		mSL.setScale_TextSize(mTV_Text, 30);
//////		mTV_Text.setSingleLine();
////
////		mSL.setTextView_WrapContent(mTV_Text, TextView_WrapContent_Direction.Bottom, true);
//
////		mTV_Text.addTextChangedListener(new TextWatcher() {
////			@Override
////			public void onTextChanged(CharSequence pS, int pStart, int pBefore, int pCount) { }
////			@Override
////			public void beforeTextChanged(CharSequence pS, int pStart, int pCount, int pAfter) { }
////			@Override
////			public void afterTextChanged(Editable pS) {
////				ScalableLayout.LayoutParams lTV_SLLP = (ScalableLayout.LayoutParams) mTV_Text.getLayoutParams();
////
////				float lNewViewHeight = getHeight2(mTV_Text.getContext(), (String)pS.toString(), (int)mTV_Text.getTextSize(), mTV_Text.getWidth());
////				float lOldScaleHeight = lTV_SLLP.getScale_Height();
////				float lOldViewHeight = mTV_Text.getHeight();
////				float lNewScaleHeight = (lNewViewHeight * lOldScaleHeight / lOldViewHeight);
////
////				log("getHeight1 "+lNewViewHeight);
//////				log("getHeight2 "+mTV_Text.getHeight());
////				mSL.moveChildView(mTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width(), lNewScaleHeight);
////
////				for(int i=0;i<mSL.getChildCount();i++) {
////					View v = mSL.getChildAt(i);
////					if(v == mTV_Text) {
////						continue;
////					}
////					ScalableLayout.LayoutParams lSLLP = (ScalableLayout.LayoutParams) v.getLayoutParams();
////					if(
////						lSLLP.getScale_Top() >= lTV_SLLP.getScale_Top()+lOldScaleHeight &&
////						(
////						( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Left() && lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left()+lTV_SLLP.getScale_Width() )
////						||
////						( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Left()+lSLLP.getScale_Width() && lSLLP.getScale_Left()+lSLLP.getScale_Width() <= lTV_SLLP.getScale_Left()+lTV_SLLP.getScale_Width())
////						)) {
////
////						mSL.moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()+lNewScaleHeight-lOldScaleHeight);
////					}
////				}
////				mSL.setScaleHeight(mSL.getScaleHeight()+lNewScaleHeight-lOldScaleHeight);
////
////				mSL.requestLayout();
////				mSL.forceLayout();
////			}
////		});
//
//        ImageView iv2 = new ImageView(this);
//        iv2.setImageResource(com.ssomai.android.sample_scalablelayout.R.drawable.ic_launcher);
//        mSL.addView(iv2, 220f, 160f, 50f, 50f);
//
//        TextView tv2 = new TextView(this);
//        tv2.setText("test2");
//        tv2.setBackgroundColor(Color.YELLOW);
//        mSL.addView(tv2, 40f, 170f, 100f, 30f);
//        mSL.setScale_TextSize(tv2, 20f);
//
//        TextView tv3 = new TextView(this);
//        tv3.setText("test3");
//        tv3.setBackgroundColor(Color.YELLOW);
//        mSL.addView(tv3, 0f, 210f, 60f, 30f);
//        mSL.setScale_TextSize(tv3, 20f);
//
//        TextView tv4 = new TextView(this);
//        tv4.setText("test4");
//        tv4.setBackgroundColor(Color.YELLOW);
//        mSL.addView(tv4, 150f, 250f, 100f, 30f);
//        mSL.setScale_TextSize(tv4, 20f);
//
//        TextView tv5 = new TextView(this);
//        tv5.setText("test5");
//        tv5.setBackgroundColor(Color.YELLOW);
//        mSL.addView(tv5, 350f, 300f, 100f, 30f);
//        mSL.setScale_TextSize(tv5, 20f);
//
//        TextView tv6 = new TextView(this);
//        tv6.setText("test6");
//        tv6.setBackgroundColor(Color.YELLOW);
//        mSL.addView(tv6, 50f, 300f, 120f, 40f);
//        mSL.setScale_TextSize(tv6, 20f);
//
////		mTV_Text = new TextView(this);
//////		mTV_Text.setText("test\n2233");
//////		mTV_Text.setText("test\n2233\n\n");
//////		mTV_Text.setText("test\n2233\n\n\n\nqwelkqjwkrjqw\n\naa");
//////		mTV_Text.setText("test\n2233\nqw\nel\nkqj\nwelkqwje\n\n\n\nwelkrjqw");
//////		mTV_Text.setText("test\n2233\nqw\nel\nkqj\nwelkqwje\n\n\n\nwelkrjqw"
//////					+"\n" +
//////					"ㅁㅈ디ㅏ거미더가\n" +
//////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
//////					"ㅁㅈ디ㅏ거미더가\n" +
//////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
//////					"ㅁㅈ디ㅏ거미더가\n" +
//////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
//////					"ㅁ기ㅏㅓㅎㅁ디가ㅓㅅㅎ미ㅓ\n"
//////);
////		mTV_Text.setText("test\n2233\nqw\nel\nkqj\nwelkqwje\n\n\n\nwelkrjqw"
////			+"\n" +
////			"1qwelkjlaske" +
////			"2aa409203kkl" +
//////			"3ㅁㅈ디ㅏ더가" +
//////			"4ㅁㅈ디ㅏ밎더ㅏ" +
//////			"5ㅁㅈ디ㅏ미더가" +
////			"6aaaaawww" +
////			"7zzzzzzz\n"
////			);
////		mTV_Text.setBackgroundColor(Color.RED);
////		mSL.addView(mTV_Text, 40f, 170f, 100f, 100f);
////		mSL.setScale_TextSize(mTV_Text, 30f);
////		mSL.setTextView_WrapContent(mTV_Text, TextView_WrapContent_Direction.Bottom, true);
//
//
//        mTV_Text = new TextView(this);
////		mTV_Text.setText("test\n2233\n\n\n\nqwelkqjwkrjqw\n\naa");
//        mTV_Text.setText("test");
//        mTV_Text.setBackgroundColor(Color.RED);
////		mSL.addView(mTV_Text, 40f, 170f, 100f, 100f);
//        mSL.addView(mTV_Text, 200f, 200f, 1f, 100f);
//        mSL.setScale_TextSize(mTV_Text, 30f);
//        mSL.setTextView_WrapContent(mTV_Text, ScalableLayout.TextView_WrapContent_Direction.Center_Horizontal, false);
//
////		Handler h = new Handler();
////		h.postDelayed(new Runnable() {
////			@Override
////			public void run() {
////				mTV_Text.setText(
////					mTV_Text.getText()
////					+"\n" +
////					"ㅁㅈ디ㅏ거미더가\n" +
////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
////					"ㅁㅈ디ㅏ거미더가\n" +
////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
////					"ㅁㅈ디ㅏ거미더가\n" +
////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
////					"ㅁㅈ디ㅏ거미더가\n" +
////					"ㅁㅈ디ㅏ거밎더ㅏ\n" +
////					"ㅁ기ㅏㅓㅎㅁ디가ㅓㅅㅎ미ㅓ\n"
////					);
////			}
////		}, 1000);
//
//        {
//            Button btn = new Button(this);
//            btn.setText("Refresh");
//            btn.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View pV) {
//                    mSL.requestLayout();
//                    mSL.forceLayout();
//                }
//            });
//            mSL.addView(btn, 205, 100, 200, 100);
//            mSL.setScale_TextSize(btn, 30);
//        }
//
////		{
////			Button btn = new Button(this);
////			btn.setText("늘리기");
////			btn.setOnClickListener(new View.OnClickListener() {
////				@Override
////				public void onClick(View pV) {
////					mTV_Text.setText(mTV_Text.getText()+"ㅁ디");
////				}
////			});
////			mSL.addView(btn, 305, 100, 100, 60);
////			mSL.setScale_TextSize(btn, 20);
////		}
////		{
////			Button btn = new Button(this);
////			btn.setText("줄이기");
////			btn.setOnClickListener(new View.OnClickListener() {
////				@Override
////				public void onClick(View pV) {
////					mTV_Text.setText(mTV_Text.getText().subSequence(0, mTV_Text.getText().length()/2));
////				}
////			});
////			mSL.addView(btn, 305, 170, 100, 60);
////			mSL.setScale_TextSize(btn, 20);
////		}

    }

//    @Override
//    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
//    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(com.ssomai.android.sample_scalablelayout.R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == com.ssomai.android.sample_scalablelayout.R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void onClick_SetMoreText(View view) {
        TextView tv = (TextView) findViewById(R.id.tv_moretext);
        tv.setText(tv.getText() + "커");
        tv.requestLayout();
    }

    public void onClick_SetLessText(View view) {
        TextView tv = (TextView) findViewById(R.id.tv_moretext);
        tv.setText(tv.getText().subSequence(1, tv.getText().length()));
        tv.requestLayout();
    }
}


package in.uncod.android.bypass;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.util.DisplayMetrics;
import android.util.Patterns;
import android.util.TypedValue;
import in.uncod.android.bypass.Element.Type;
import in.uncod.android.bypass.style.HorizontalLineSpan;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Bypass {
	static {
		System.loadLibrary("bypass");
	}

	private final Options mOptions;

	private final int mListItemIndent;
	private final int mBlockQuoteIndent;
	private final int mCodeBlockIndent;
	private final int mHruleSize;

	private final int mHruleTopBottomPadding;

	// Keeps track of the ordered list number for each LIST element.
	// We need to track multiple ordered lists at once because of nesting.
	private final Map<Element, Integer> mOrderedListNumber = new ConcurrentHashMap<Element, Integer>();

	/**
	 * @deprecated Use {@link #Bypass(android.content.Context)} instead.
	 */
	@Deprecated
	public Bypass() {
		// Default constructor for backwards-compatibility
		mOptions = new Options();
		mListItemIndent = 20;
		mBlockQuoteIndent = 10;
		mCodeBlockIndent = 10;
		mHruleSize = 2;
		mHruleTopBottomPadding = 20;
	}

	public Bypass(Context context) {
		this(context, new Options());
	}

	public Bypass(Context context, Options options) {
		mOptions = options;

		DisplayMetrics dm = context.getResources().getDisplayMetrics();

		mListItemIndent = (int) TypedValue.applyDimension(mOptions.mListItemIndentUnit,
			mOptions.mListItemIndentSize, dm);

		mBlockQuoteIndent = (int) TypedValue.applyDimension(mOptions.mBlockQuoteIndentUnit,
			mOptions.mBlockQuoteIndentSize, dm);

		mCodeBlockIndent = (int) TypedValue.applyDimension(mOptions.mCodeBlockIndentUnit,
			mOptions.mCodeBlockIndentSize, dm);

		mHruleSize = (int) TypedValue.applyDimension(mOptions.mHruleUnit,
			mOptions.mHruleSize, dm);

		mHruleTopBottomPadding = (int) dm.density * 10;
	}

	public CharSequence markdownToSpannable(String markdown) {
		return markdownToSpannable(markdown, null);
	}

	public CharSequence markdownToSpannable(String markdown, ImageGetter imageGetter) {
		Document document = processMarkdown(markdown);

		int size = document.getElementCount();
		CharSequence[] spans = new CharSequence[size];

		for (int i = 0; i < size; i++) {
			spans[i] = recurseElement(document.getElement(i), i, size, imageGetter);
		}

		return TextUtils.concat(spans);
	}

	private native Document processMarkdown(String markdown);

	// The 'numberOfSiblings' parameters refers to the number of siblings within the parent, including
	// the 'element' parameter, as in "How many siblings are you?" rather than "How many siblings do
	// you have?".
	private CharSequence recurseElement(Element element, int indexWithinParent, int numberOfSiblings,
			ImageGetter imageGetter) {

		Type type = element.getType();

		boolean isOrderedList = false;
		if (type == Type.LIST) {
			String flagsStr = element.getAttribute("flags");
			if (flagsStr != null) {
				int flags = Integer.parseInt(flagsStr);
				isOrderedList = (flags & Element.F_LIST_ORDERED) != 0;
				if (isOrderedList) {
					mOrderedListNumber.put(element, 1);
				}
			}
		}

		int size = element.size();
		CharSequence[] spans = new CharSequence[size];

		for (int i = 0; i < size; i++) {
			spans[i] = recurseElement(element.children[i], i, size, imageGetter);
		}

		// Clean up after we're done
		if (isOrderedList) {
			mOrderedListNumber.remove(this);
		}

		CharSequence concat = TextUtils.concat(spans);

		SpannableStringBuilder builder = new ReverseSpannableStringBuilder();

		String text = element.getText();
		if (element.size() == 0
			&& element.getParent() != null
			&& element.getParent().getType() != Type.BLOCK_CODE) {
			text = text.replace('\n', ' ');
		}

		// Retrieve the image now so we know whether we're going to have something to display later
		// If we don't, then show the alt text instead (if available).
		Drawable imageDrawable = null;
		if (type == Type.IMAGE && imageGetter != null && !TextUtils.isEmpty(element.getAttribute("link"))) {
			imageDrawable = imageGetter.getDrawable(element.getAttribute("link"));
		}

		switch (type) {
			case LIST:
				if (element.getParent() != null
					&& element.getParent().getType() == Type.LIST_ITEM) {
					builder.append("\n");
				}
				break;
			case LINEBREAK:
				builder.append("\n");
				break;
			case LIST_ITEM:
				builder.append(" ");
				if (mOrderedListNumber.containsKey(element.getParent())) {
					int number = mOrderedListNumber.get(element.getParent());
					builder.append(Integer.toString(number) + ".");
					mOrderedListNumber.put(element.getParent(), number + 1);
				}
				else {
					builder.append(mOptions.mUnorderedListItem);
				}
				builder.append("  ");
				break;
			case AUTOLINK:
				builder.append(element.getAttribute("link"));
				break;
			case HRULE:
				// This ultimately gets drawn over by the line span, but
				// we need something here or the span isn't even drawn.
				builder.append("-");
				break;
			case IMAGE:
				// Display alt text (or title text) if there is no image
				if (imageDrawable == null) {
					String show = element.getAttribute("alt");
					if (TextUtils.isEmpty(show)) {
						show = element.getAttribute("title");
					}
					if (!TextUtils.isEmpty(show)) {
						show = "[" + show + "]";
						builder.append(show);
					}
				}
				else {
					// Character to be replaced
					builder.append("\uFFFC");
				}
				break;
		}

		builder.append(text);
		builder.append(concat);

		// Don't auto-append whitespace after last item in document. The 'numberOfSiblings'
		// is the number of children the parent of the current element has (including the
		// element itself), hence subtracting a number from that count gives us the index
		// of the last child within the parent.
		if (element.getParent() != null || indexWithinParent < (numberOfSiblings - 1)) {
			if (type == Type.LIST_ITEM) {
				if (element.size() == 0 || !element.children[element.size() - 1].isBlockElement()) {
					builder.append("\n");
				}
			}
			else if (element.isBlockElement() && type != Type.BLOCK_QUOTE) {
				if (type == Type.LIST) {
					// If this is a nested list, don't include newlines
					if (element.getParent() == null || element.getParent().getType() != Type.LIST_ITEM) {
						builder.append("\n");
					}
				}
				else if (element.getParent() != null
					&& element.getParent().getType() == Type.LIST_ITEM) {
					// List items should never double-space their entries
					builder.append("\n");
				}
				else {
					builder.append("\n\n");
				}
			}
		}

		switch (type) {
			case HEADER:
				String levelStr = element.getAttribute("level");
				int level = Integer.parseInt(levelStr);
				setSpan(builder, new RelativeSizeSpan(mOptions.mHeaderSizes[level - 1]));
				setSpan(builder, new StyleSpan(Typeface.BOLD));
				break;
			case LIST:
				setBlockSpan(builder, new LeadingMarginSpan.Standard(mListItemIndent));
				break;
			case EMPHASIS:
				setSpan(builder, new StyleSpan(Typeface.ITALIC));
				break;
			case DOUBLE_EMPHASIS:
				setSpan(builder, new StyleSpan(Typeface.BOLD));
				break;
			case TRIPLE_EMPHASIS:
				setSpan(builder, new StyleSpan(Typeface.BOLD_ITALIC));
				break;
			case BLOCK_CODE:
				setSpan(builder, new LeadingMarginSpan.Standard(mCodeBlockIndent));
				setSpan(builder, new TypefaceSpan("monospace"));
				break;
			case CODE_SPAN:
				setSpan(builder, new TypefaceSpan("monospace"));
				break;
			case LINK:
			case AUTOLINK:
				String link = element.getAttribute("link");
				if (!TextUtils.isEmpty(link) && Patterns.EMAIL_ADDRESS.matcher(link).matches()) {
					link = "mailto:" + link;
				}
				setSpan(builder, new URLSpan(link));
				break;
			case BLOCK_QUOTE:
				// We add two leading margin spans so that when the order is reversed,
				// the QuoteSpan will always be in the same spot.
				setBlockSpan(builder, new LeadingMarginSpan.Standard(mBlockQuoteIndent));
				setBlockSpan(builder, new QuoteSpan(mOptions.mBlockQuoteColor));
				setBlockSpan(builder, new LeadingMarginSpan.Standard(mBlockQuoteIndent));
				setBlockSpan(builder, new StyleSpan(Typeface.ITALIC));
				break;
			case STRIKETHROUGH:
				setSpan(builder, new StrikethroughSpan());
				break;
			case HRULE:
				setSpan(builder, new HorizontalLineSpan(mOptions.mHruleColor, mHruleSize, mHruleTopBottomPadding));
				break;
			case IMAGE:
				if (imageDrawable != null) {
					setSpan(builder, new ImageSpan(imageDrawable));
				}
				break;
		}

		return builder;
	}

	private static void setSpan(SpannableStringBuilder builder, Object what) {
		builder.setSpan(what, 0, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
	}

	// These have trailing newlines that we want to avoid spanning
	private static void setBlockSpan(SpannableStringBuilder builder, Object what) {
		int length = Math.max(0, builder.length() - 1);
		builder.setSpan(what, 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
	}

	/**
	 * Configurable options for how Bypass renders certain elements.
	 */
	public static final class Options {
		private float[] mHeaderSizes;

		private String mUnorderedListItem;
		private int mListItemIndentUnit;
		private float mListItemIndentSize;

		private int mBlockQuoteColor;
		private int mBlockQuoteIndentUnit;
		private float mBlockQuoteIndentSize;

		private int mCodeBlockIndentUnit;
		private float mCodeBlockIndentSize;

		private int mHruleColor;
		private int mHruleUnit;
		private float mHruleSize;

		public Options() {
			mHeaderSizes = new float[] {
				1.5f, // h1
				1.4f, // h2
				1.3f, // h3
				1.2f, // h4
				1.1f, // h5
				1.0f, // h6
			};

			mUnorderedListItem = "\u2022";
			mListItemIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
			mListItemIndentSize = 10;

			mBlockQuoteColor = 0xff0000ff;
			mBlockQuoteIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
			mBlockQuoteIndentSize = 10;

			mCodeBlockIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
			mCodeBlockIndentSize = 10;

			mHruleColor = Color.GRAY;
			mHruleUnit = TypedValue.COMPLEX_UNIT_DIP;
			mHruleSize = 1;
		}

		public Options setHeaderSizes(float[] headerSizes) {
			if (headerSizes == null) {
				throw new IllegalArgumentException("headerSizes must not be null");
			}
			else if (headerSizes.length != 6) {
				throw new IllegalArgumentException("headerSizes must have 6 elements (h1 through h6)");
			}

			mHeaderSizes = headerSizes;

			return this;
		}

		public Options setUnorderedListItem(String unorderedListItem) {
			mUnorderedListItem = unorderedListItem;
			return this;
		}

		public Options setListItemIndentSize(int unit, float size) {
			mListItemIndentUnit = unit;
			mListItemIndentSize = size;
			return this;
		}

		public Options setBlockQuoteColor(int color) {
			mBlockQuoteColor = color;
			return this;
		}

		public Options setBlockQuoteIndentSize(int unit, float size) {
			mBlockQuoteIndentUnit = unit;
			mBlockQuoteIndentSize = size;
			return this;
		}

		public Options setCodeBlockIndentSize(int unit, float size) {
			mCodeBlockIndentUnit = unit;
			mCodeBlockIndentSize = size;
			return this;
		}

		public Options setHruleColor(int color) {
			mHruleColor = color;
			return this;
		}

		public Options setHruleSize(int unit, float size) {
			mHruleUnit = unit;
			mHruleSize = size;
			return this;
		}
	}

	/**
	 * Retrieves images for markdown images.
	 */
	public static interface ImageGetter {

		/**
		 * This method is called when the parser encounters an image tag.
		 */
		public Drawable getDrawable(String source);

	}
}


package com.github.javaparser.symbolsolver.resolution.typeinference;

import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;

import java.util.LinkedList;
import java.util.List;

/**
 * @author Federico Tomassetti
 */
public class ConstraintFormulaSet {
    private List<ConstraintFormula> constraintFormulas;

    public ConstraintFormulaSet withConstraint(ConstraintFormula constraintFormula) {
        ConstraintFormulaSet newInstance = new ConstraintFormulaSet();
        newInstance.constraintFormulas.addAll(this.constraintFormulas);
        newInstance.constraintFormulas.add(constraintFormula);
        return newInstance;
    }

    private static final ConstraintFormulaSet EMPTY = new ConstraintFormulaSet();

    public static ConstraintFormulaSet empty() {
        return EMPTY;
    }

    private ConstraintFormulaSet() {
        constraintFormulas = new LinkedList<>();
    }

    /**
     * Takes a compatibility assertion about an expression or type, called a constraint formula, and reduces it to a
     * set of bounds on inference variables. Often, a constraint formula reduces to other constraint formulas,
     * which must be recursively reduced. A procedure is followed to identify these additional constraint formulas and,
     * ultimately, to express via a bound set the conditions under which the choices for inferred types would render
     * each constraint formula true.
     */
    public BoundSet reduce(TypeSolver typeSolver) {
        List<ConstraintFormula> constraints = new LinkedList<>(constraintFormulas);
        BoundSet boundSet = BoundSet.empty();
        while (constraints.size() > 0) {
            ConstraintFormula constraintFormula = constraints.remove(0);
            ConstraintFormula.ReductionResult reductionResult = constraintFormula.reduce(boundSet);
            constraints.addAll(reductionResult.getConstraintFormulas());
            boundSet.incorporate(reductionResult.getBoundSet(), typeSolver);
        }
        return boundSet;
    }

    public boolean isEmpty() {
        return constraintFormulas.isEmpty();
    }
}