package ru.nikitenkogleb.myapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.v7.widget.AppCompatTextView;
import android.text.BoringLayout;
import android.text.Layout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.Gravity;

import static android.text.BoringLayout.isBoring;
import static android.text.Layout.Alignment.ALIGN_OPPOSITE;

/**
 * @author Nikitenko Gleb
 * @since 1.0, 30/11/2017
 */
@SuppressWarnings("unused")
public final class TwoAlignTextView extends AppCompatTextView {

  /** Splitter symbol. */
  private static final String SPLITTER = "|";

  /** Opposite text. */
  private CharSequence mText;

  private BoringLayout mOppositeLayout;

  public TwoAlignTextView(Context context) {super(context);}

  public TwoAlignTextView(Context context, AttributeSet attrs) {super(context, attrs);}

  public TwoAlignTextView(Context context, AttributeSet attrs, int defStyleAttr)
  {super(context, attrs, defStyleAttr);}

  @Override
  public void setText(CharSequence text, BufferType type) {
    if (text != null) {
      final int index = text.toString().indexOf(SPLITTER);
      if (index != -1) {
        super.setText(text.subSequence(0, index), type);
        mText = text.subSequence(index + 1, text.length());
      } else {super.setText(text, type); mText = "";}
    } else {super.setText(null, type); mText = "";}
    invalidateOppositeLayout();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    invalidateOppositeLayout();
  }

  @Override
  public void setGravity(int gravity) {
    super.setGravity(gravity);
    invalidateOppositeLayout();
  }

  private void invalidateOppositeLayout() {
    mText = mText == null ? "" : mText;
    final TextPaint paint = getPaint();
    final BoringLayout.Metrics metrics =
        isBoring(mText, paint);
    final int width = getMeasuredWidth();
    final Layout.Alignment align = ALIGN_OPPOSITE;
    final float mult = getLineSpacingMultiplier();
    final float add = getLineSpacingExtra();
    final boolean pad = false;
    mOppositeLayout = mOppositeLayout == null ?
        BoringLayout.make
            (mText, paint, width, align, mult, add, metrics, pad) :
        mOppositeLayout.replaceOrMake
            (mText, paint, width, align, mult, add, metrics, pad);
  }

  @Override
  protected void onDraw(@NonNull Canvas canvas) {
    super.onDraw(canvas); if (mOppositeLayout == null) return;
    canvas.save(); prepareCanvas(canvas, mOppositeLayout.getHeight());
    mOppositeLayout.draw(canvas); canvas.restore();
  }

  private void prepareCanvas(@NonNull Canvas canvas, int height) {

    final int
        mTop = getTop(), mBottom = getBottom(),
        mGravity = getGravity();

    final float
        mShadowRadius = getShadowRadius(),
        mShadowDx = getShadowDx(),
        mShadowDy = getShadowDy();

    final int compoundPaddingLeft = getCompoundPaddingLeft();
    final int compoundPaddingTop = getCompoundPaddingTop();
    final int compoundPaddingRight = getCompoundPaddingRight();
    final int compoundPaddingBottom = getCompoundPaddingBottom();
    final int scrollX = getScrollX();
    final int scrollY = getScrollY();
    final int right = getRight();
    final int left = getLeft();
    final int bottom = getBottom();
    final int top = getTop();


    int extendedPaddingTop = getExtendedPaddingTop();
    int extendedPaddingBottom = getExtendedPaddingBottom();

    final int vSpace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
    final int maxScrollY = height - vSpace;

    float clipLeft = compoundPaddingLeft + scrollX;
    float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
    float clipRight = right - left - getCompoundPaddingRight() + scrollX;
    float clipBottom = bottom - top + scrollY -
        ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);

    if (mShadowRadius != 0) {
      clipLeft += Math.min(0, mShadowDx - mShadowRadius);
      clipRight += Math.max(0, mShadowDx + mShadowRadius);

      clipTop += Math.min(0, mShadowDy - mShadowRadius);
      clipBottom += Math.max(0, mShadowDy + mShadowRadius);
    }

    canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);

    final float dy = extendedPaddingTop +
        (mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP ?
            getVerticalOffset(mGravity, height,
                extendedPaddingTop, extendedPaddingBottom) : 0;

    canvas.translate(-compoundPaddingRight, dy);
  }

  /**
   * @param gravity the text-gravity
   * @param textHeight the text height
   * @param extendedPaddingTop top pad
   * @param extendedPaddingBottom bottom pad
   * @return vertical offset
   */
  private int getVerticalOffset(int gravity, int textHeight,
      int extendedPaddingTop, int extendedPaddingBottom) {
    gravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
    if (gravity != Gravity.TOP) {
      final int boxHeight = getMeasuredHeight() -
          extendedPaddingTop + extendedPaddingBottom;
      if (textHeight < boxHeight) {
        final int delta = boxHeight - textHeight;
        return gravity == Gravity.BOTTOM ? delta :
            Math.round(delta / 2.0f);
      }
    }
    return 0;
  }

}
