package no.agens.depth.lib;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
import com.mdroid.repo.depth_lib_android_.R;

public class DepthRendrer extends RelativeLayout {

  android.graphics.Matrix matrix = new android.graphics.Matrix();
  private Paint shadowPaint = new Paint();
  private NinePatchDrawable softShadow;
  private Drawable roundSoftShadow;
  private Path edgePath = new Path();
  private float shadowAlpha = 0.3f;

  public DepthRendrer(Context context) {
    super(context);
    setup();
  }

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

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

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public DepthRendrer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    setup();
  }

  public float getTopEdgeLength(DepthLayout dl) {
    return getDistance(dl.getTopLeftBack(), dl.getTopRightBack());
  }

  float getDistance(PointF p1, PointF p2) {
    return (float) Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
  }

  public float getShadowAlpha() {
    return shadowAlpha;
  }

  public void setShadowAlpha(float shadowAlpha) {
    this.shadowAlpha = Math.min(1f, Math.max(0, shadowAlpha));
  }

  void setup() {
    getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
      @Override public boolean onPreDraw() {
        for (int i = 0; i < getChildCount(); i++) {
          View child = getChildAt(i);
          if (child instanceof DepthLayout) {
            boolean hasChangedBounds = ((DepthLayout) child).calculateBounds();
            if (hasChangedBounds) invalidate();
          }
        }
        return true;
      }
    });

    shadowPaint.setColor(Color.BLACK);
    shadowPaint.setAntiAlias(true);
    softShadow = (NinePatchDrawable) getResources().getDrawable(R.drawable.shadow, null);
    roundSoftShadow = getResources().getDrawable(R.drawable.round_soft_shadow, null);
  }

  @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if (child instanceof DepthLayout && !isInEditMode()) {
      DepthLayout dl = (DepthLayout) child;

      float[] src =
          new float[] { 0, 0, dl.getWidth(), 0, dl.getWidth(), dl.getHeight(), 0, dl.getHeight() };
      if (dl.isCircle()) {
        dl.getCustomShadow().drawShadow(canvas, dl, roundSoftShadow);
        if (Math.abs(dl.getRotationX()) > 1 || Math.abs(dl.getRotationY()) > 1) {
          drawCornerBaseShape(dl, canvas, src);
        }
      } else {
        dl.getCustomShadow().drawShadow(canvas, dl, softShadow);
        if (dl.getRotationX() != 0 || dl.getRotationY() != 0) {
          if (getLongestHorizontalEdge(dl) > getLongestVerticalEdge(dl)) {
            drawVerticalFirst(dl, canvas, src);
          } else {
            drawHorizontalFist(dl, canvas, src);
          }
        }
      }
    }
    return super.drawChild(canvas, child, drawingTime);
  }

  private void drawCornerBaseShape(DepthLayout dl, Canvas canvas, float[] src) {
    float[] dst = new float[] {
        dl.getTopLeftBack().x, dl.getTopLeftBack().y, dl.getTopRightBack().x,
        dl.getTopRightBack().y, dl.getBottomRightBack().x, dl.getBottomRightBack().y,
        dl.getBottomLeftBack().x, dl.getBottomLeftBack().y
    };
    int count = canvas.save();
    matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
    canvas.concat(matrix);
    edgePath.reset();
    edgePath.addRoundRect(0, 0, dl.getWidth(), dl.getHeight(), dl.getWidth() / 2f,
        dl.getHeight() / 2f, Path.Direction.CCW);

    canvas.drawPath(edgePath, dl.getEdgePaint());
    shadowPaint.setAlpha((int) (shadowAlpha * 0.5f * 255));
    canvas.drawPath(edgePath, shadowPaint);

    canvas.restoreToCount(count);
  }

  private void drawHorizontalFist(DepthLayout dl, Canvas canvas, float[] src) {
    if (getLeftEdgeLength(dl) <= getRightEdgeLength(dl)) {
      drawLeftEdge(dl, canvas, src);
    } else {
      drawRightEdge(dl, canvas, src);
    }

    drawTopEdge(dl, canvas, src);
    drawBottomEdge(dl, canvas, src);

    if (getLeftEdgeLength(dl) >= getRightEdgeLength(dl)) {
      drawLeftEdge(dl, canvas, src);
    } else {
      drawRightEdge(dl, canvas, src);
    }
  }

  private void drawVerticalFirst(DepthLayout dl, Canvas canvas, float[] src) {

    if (getTopEdgeLength(dl) <= getBottomEdgeLength(dl)) {
      drawTopEdge(dl, canvas, src);
    } else {
      drawBottomEdge(dl, canvas, src);
    }

    drawLeftEdge(dl, canvas, src);
    drawRightEdge(dl, canvas, src);

    if (getTopEdgeLength(dl) >= getBottomEdgeLength(dl)) {
      drawTopEdge(dl, canvas, src);
    } else {
      drawBottomEdge(dl, canvas, src);
    }
  }

  float getLongestHorizontalEdge(DepthLayout dl) {
    float topEdgeLength = getTopEdgeLength(dl);
    float bottomEdgeLength = getBottomEdgeLength(dl);
    if (topEdgeLength > bottomEdgeLength) {
      return topEdgeLength;
    } else {
      return bottomEdgeLength;
    }
  }

  float getLongestVerticalEdge(DepthLayout dl) {
    float leftEdgeLength = getLeftEdgeLength(dl);
    float rightEdgeLength = getRightEdgeLength(dl);
    if (leftEdgeLength > rightEdgeLength) {
      return leftEdgeLength;
    } else {
      return rightEdgeLength;
    }
  }

  private float getRightEdgeLength(DepthLayout dl) {
    return getDistance(dl.getTopRightBack(), dl.getBottomRightBack());
  }

  private float getLeftEdgeLength(DepthLayout dl) {
    return getDistance(dl.getTopLeftBack(), dl.getBottomLeftBack());
  }

  private float getBottomEdgeLength(DepthLayout dl) {
    return getDistance(dl.getBottomLeftBack(), dl.getBottomRightBack());
  }

  void drawShadow(PointF point1, PointF point2, float correctionValue, Canvas canvas,
      DepthLayout dl) {
    float angle = Math.abs(Math.abs(getAngle(point1, point2)) + correctionValue);
    float alpha = angle / 180f;
    shadowPaint.setAlpha((int) (alpha * 255f * shadowAlpha));

    canvas.drawRect(0, 0, dl.getWidth(), dl.getHeight(), shadowPaint);
  }

  private void drawRectancle(DepthLayout dl, Canvas canvas) {
    canvas.drawRect(0, 0, dl.getWidth(), dl.getHeight(), dl.getEdgePaint());
  }

  public float getAngle(PointF point1, PointF point2) {
    float angle = (float) Math.toDegrees(Math.atan2(point1.y - point2.y, point1.x - point2.x));

    return angle;
  }

  private void drawLeftEdge(DepthLayout dl, Canvas canvas, float[] src) {
    float[] dst = new float[] {
        dl.getTopLeft().x, dl.getTopLeft().y, dl.getTopLeftBack().x, dl.getTopLeftBack().y,
        dl.getBottomLeftBack().x, dl.getBottomLeftBack().y, dl.getBottomLeft().x,
        dl.getBottomLeft().y
    };
    int count = canvas.save();
    matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
    canvas.concat(matrix);
    drawRectancle(dl, canvas);
    drawShadow(dl.getTopLeft(), dl.getBottomLeft(), 0, canvas, dl);

    canvas.restoreToCount(count);
  }

  private void drawRightEdge(DepthLayout dl, Canvas canvas, float[] src) {
    float[] dst = new float[] {
        dl.getTopRight().x, dl.getTopRight().y, dl.getTopRightBack().x, dl.getTopRightBack().y,
        dl.getBottomRightBack().x, dl.getBottomRightBack().y, dl.getBottomRight().x,
        dl.getBottomRight().y
    };
    int count = canvas.save();
    matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
    canvas.concat(matrix);
    drawRectancle(dl, canvas);
    drawShadow(dl.getTopRight(), dl.getBottomRight(), -180f, canvas, dl);
    canvas.restoreToCount(count);
  }

  private void drawTopEdge(DepthLayout dl, Canvas canvas, float[] src) {

    float[] dst = new float[] {
        dl.getTopLeft().x, dl.getTopLeft().y, dl.getTopRight().x, dl.getTopRight().y,
        dl.getTopRightBack().x, dl.getTopRightBack().y, dl.getTopLeftBack().x, dl.getTopLeftBack().y
    };
    int count = canvas.save();
    matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
    canvas.concat(matrix);
    drawRectancle(dl, canvas);
    drawShadow(dl.getTopLeft(), dl.getTopRight(), -180f, canvas, dl);
    canvas.restoreToCount(count);
  }

  private void drawBottomEdge(DepthLayout dl, Canvas canvas, float[] src) {

    float[] dst = new float[] {
        dl.getBottomLeft().x, dl.getBottomLeft().y, dl.getBottomRight().x, dl.getBottomRight().y,
        dl.getBottomRightBack().x, dl.getBottomRightBack().y, dl.getBottomLeftBack().x,
        dl.getBottomLeftBack().y
    };
    int count = canvas.save();
    matrix.setPolyToPoly(src, 0, dst, 0, dst.length >> 1);
    canvas.concat(matrix);
    drawRectancle(dl, canvas);
    drawShadow(dl.getBottomLeft(), dl.getBottomRight(), 0, canvas, dl);
    canvas.restoreToCount(count);
  }
}
