package com.airbnb.lottie.model.content;

import android.graphics.PointF;
import androidx.annotation.FloatRange;

import com.airbnb.lottie.model.CubicCurveData;
import com.airbnb.lottie.utils.Logger;
import com.airbnb.lottie.utils.MiscUtils;

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

public class ShapeData {
  private final List<CubicCurveData> curves;
  private PointF initialPoint;
  private boolean closed;

  public ShapeData(PointF initialPoint, boolean closed, List<CubicCurveData> curves) {
    this.initialPoint = initialPoint;
    this.closed = closed;
    this.curves = new ArrayList<>(curves);
  }

  public ShapeData() {
    curves = new ArrayList<>();
  }

  private void setInitialPoint(float x, float y) {
    if (initialPoint == null) {
      initialPoint = new PointF();
    }
    initialPoint.set(x, y);
  }

  public PointF getInitialPoint() {
    return initialPoint;
  }

  public boolean isClosed() {
    return closed;
  }

  public List<CubicCurveData> getCurves() {
    return curves;
  }

  public void interpolateBetween(ShapeData shapeData1, ShapeData shapeData2,
      @FloatRange(from = 0f, to = 1f) float percentage) {
    if (initialPoint == null) {
      initialPoint = new PointF();
    }
    closed = shapeData1.isClosed() || shapeData2.isClosed();


    if (shapeData1.getCurves().size() != shapeData2.getCurves().size()) {
      Logger.warning("Curves must have the same number of control points. Shape 1: " +
          shapeData1.getCurves().size() + "\tShape 2: " + shapeData2.getCurves().size());
    }
    
    int points = Math.min(shapeData1.getCurves().size(), shapeData2.getCurves().size());
    if (curves.size() < points) {
      for (int i = curves.size(); i < points; i++) {
        curves.add(new CubicCurveData());
      }
    } else if (curves.size() > points) {
      for (int i = curves.size() - 1; i >= points; i--) {
        curves.remove(curves.size() - 1);
      }
    }

    PointF initialPoint1 = shapeData1.getInitialPoint();
    PointF initialPoint2 = shapeData2.getInitialPoint();

    setInitialPoint(MiscUtils.lerp(initialPoint1.x, initialPoint2.x, percentage),
        MiscUtils.lerp(initialPoint1.y, initialPoint2.y, percentage));

    for (int i = curves.size() - 1; i >= 0; i--) {
      CubicCurveData curve1 = shapeData1.getCurves().get(i);
      CubicCurveData curve2 = shapeData2.getCurves().get(i);

      PointF cp11 = curve1.getControlPoint1();
      PointF cp21 = curve1.getControlPoint2();
      PointF vertex1 = curve1.getVertex();

      PointF cp12 = curve2.getControlPoint1();
      PointF cp22 = curve2.getControlPoint2();
      PointF vertex2 = curve2.getVertex();

      curves.get(i).setControlPoint1(
          MiscUtils.lerp(cp11.x, cp12.x, percentage), MiscUtils.lerp(cp11.y, cp12.y,
              percentage));
      curves.get(i).setControlPoint2(
          MiscUtils.lerp(cp21.x, cp22.x, percentage), MiscUtils.lerp(cp21.y, cp22.y,
              percentage));
      curves.get(i).setVertex(
          MiscUtils.lerp(vertex1.x, vertex2.x, percentage), MiscUtils.lerp(vertex1.y, vertex2.y,
              percentage));
    }
  }

  @Override public String toString() {
    return "ShapeData{" + "numCurves=" + curves.size() +
        "closed=" + closed +
        '}';
  }
}
