package com.airbnb.lottie.animation.content;

import android.annotation.TargetApi;
import android.graphics.Path;
import android.os.Build;

import com.airbnb.lottie.model.content.MergePaths;

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

@TargetApi(Build.VERSION_CODES.KITKAT)
public class MergePathsContent implements PathContent, GreedyContent {
  private final Path firstPath = new Path();
  private final Path remainderPath = new Path();
  private final Path path = new Path();

  private final String name;
  private final List<PathContent> pathContents = new ArrayList<>();
  private final MergePaths mergePaths;

  public MergePathsContent(MergePaths mergePaths) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
      throw new IllegalStateException("Merge paths are not supported pre-KitKat.");
    }
    name = mergePaths.getName();
    this.mergePaths = mergePaths;
  }

  @Override public void absorbContent(ListIterator<Content> contents) {
    // Fast forward the iterator until after this content.
    //noinspection StatementWithEmptyBody
    while (contents.hasPrevious() && contents.previous() != this) {}
    while (contents.hasPrevious()) {
      Content content = contents.previous();
      if (content instanceof PathContent) {
        pathContents.add((PathContent) content);
        contents.remove();
      }
    }
  }

  @Override public void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
    for (int i = 0; i < pathContents.size(); i++) {
      pathContents.get(i).setContents(contentsBefore, contentsAfter);
    }
  }

  @Override public Path getPath() {
    path.reset();

    if (mergePaths.isHidden()) {
      return path;
    }

    switch (mergePaths.getMode()) {
      case MERGE:
        addPaths();
        break;
      case ADD:
        opFirstPathWithRest(Path.Op.UNION);
        break;
      case SUBTRACT:
        opFirstPathWithRest(Path.Op.REVERSE_DIFFERENCE);
        break;
      case INTERSECT:
        opFirstPathWithRest(Path.Op.INTERSECT);
        break;
      case EXCLUDE_INTERSECTIONS:
        opFirstPathWithRest(Path.Op.XOR);
        break;
    }

    return path;
  }

  @Override public String getName() {
    return name;
  }

  private void addPaths() {
    for (int i = 0; i < pathContents.size(); i++) {
      path.addPath(pathContents.get(i).getPath());
    }
  }

  @TargetApi(Build.VERSION_CODES.KITKAT)
  private void opFirstPathWithRest(Path.Op op) {
    remainderPath.reset();
    firstPath.reset();

    for (int i = pathContents.size() - 1; i >= 1; i--) {
      PathContent content = pathContents.get(i);

      if (content instanceof ContentGroup) {
        List<PathContent> pathList = ((ContentGroup) content).getPathList();
        for (int j = pathList.size() - 1; j >= 0; j--) {
          Path path = pathList.get(j).getPath();
          path.transform(((ContentGroup) content).getTransformationMatrix());
          this.remainderPath.addPath(path);
        }
      } else {
        remainderPath.addPath(content.getPath());
      }
    }

    PathContent lastContent = pathContents.get(0);
    if (lastContent instanceof ContentGroup) {
      List<PathContent> pathList = ((ContentGroup) lastContent).getPathList();
      for (int j = 0; j < pathList.size(); j++) {
        Path path = pathList.get(j).getPath();
        path.transform(((ContentGroup) lastContent).getTransformationMatrix());
        this.firstPath.addPath(path);
      }
    } else {
      firstPath.set(lastContent.getPath());
    }

    path.op(firstPath, remainderPath, op);
  }
}
