package com.mikosik.jsolid.d3;

import static com.mikosik.jsolid.JSolid.vx;
import static com.mikosik.jsolid.JSolid.vy;
import static com.mikosik.jsolid.JSolid.vz;
import static java.util.stream.Collectors.toList;

import java.util.List;

import eu.mihosoft.vrl.v3d.CSG;
import eu.mihosoft.vrl.v3d.Plane;
import eu.mihosoft.vrl.v3d.Transform;
import eu.mihosoft.vrl.v3d.Vector3d;

public abstract class AbstractSolid implements Solid {

  public List<Vector3d> vertexes() {
    return toCsg().getPolygons().stream()
        .flatMap(x -> x.vertices.stream())
        .map(x -> x.position)
        .collect(toList());
  }

  public Solid plus(Solid solid) {
    CSG thisCsg = toCsg();
    if (thisCsg.getPolygons().size() == 0) {
      return solid;
    }
    CSG thatCsg = solid.toCsg();
    if (thatCsg.getPolygons().size() == 0) {
      return this;
    }

    return new CsgSolid(toCsg().union(solid.toCsg()));
  }

  public Solid plus(Solid solid, Alignment<?> alignment) {
    return plus(alignment.align(this, solid));
  }

  public Solid minus(Solid solid) {
    CSG thisCsg = toCsg();
    if (thisCsg.getPolygons().size() == 0) {
      return this;
    }
    CSG thatCsg = solid.toCsg();
    if (thatCsg.getPolygons().size() == 0) {
      return this;
    }
    return new CsgSolid(thisCsg.difference(thatCsg));
  }

  public Solid minus(Solid solid, Alignment<?> alignment) {
    return minus(alignment.align(this, solid));
  }

  public Solid intersect(Solid solid) {
    CSG thisCsg = toCsg();
    if (thisCsg.getPolygons().size() == 0) {
      return this;
    }
    CSG thatCsg = solid.toCsg();
    if (thatCsg.getPolygons().size() == 0) {
      return solid;
    }
    return new CsgSolid(toCsg().intersect(solid.toCsg()));
  }

  public Solid move(Vector3d position) {
    return translate(position);
  }

  public Solid move(Anchor<?> anchor, double value) {
    return move(anchor.axis.v(value).minus(anchor.vectorIn(this)));
  }

  private CsgSolid translate(Vector3d vector) {
    return new CsgSolid(toCsg().transformed(Transform.unity().translate(vector)));
  }

  public Solid rotate(Vector3d direction, double angle) {
    return new CsgSolid(toCsg().transformed(rotationTransform(direction, angle)));
  }

  private Transform rotationTransform(Vector3d axis, double angle) {
    if (axis.equals(vx(1))) {
      return Transform.unity().rotX(angle);
    } else if (axis.equals(vy(1))) {
      return Transform.unity().rotY(angle);
    } else if (axis.equals(vz(1))) {
      return Transform.unity().rotZ(angle);
    } else {
      throw new IllegalArgumentException("Axis must be one of vx(1), vy(1), vz(1).");
    }
  }

  public Solid mirror(Vector3d planeNormal) {
    return new CsgSolid(toCsg().transformed(Transform.unity().mirror(plane(planeNormal))));
  }

  private Plane plane(Vector3d planeNormal) {
    if (planeNormal.equals(vx(1))) {
      return Plane.YZ_PLANE;
    } else if (planeNormal.equals(vy(1))) {
      return Plane.XZ_PLANE;
    } else if (planeNormal.equals(vz(1))) {
      return Plane.XY_PLANE;
    } else {
      throw new IllegalArgumentException("planeNormal must be one of vx(1), vy(1), vz(1).");
    }
  }

  public Solid scale(Vector3d factor) {
    return new CsgSolid(toCsg().transformed(Transform.unity().scale(factor)));
  }

  public Solid convexHull() {
    return new CsgSolid(toCsg().hull());
  }

  public String toStl() {
    return toCsg().toStlString();
  }
}

@@ -607,32 +607,142 @@
 toList());%0A  %7D%0A%0A
+  public Solid transform(Transform transform) %7B%0A    return new CsgSolid(toCsg().transformed(transform));%0A  %7D%0A%0A
   public Solid p
@@ -1898,16 +1898,44 @@
  return 
+transform(Transform.unity().
 translat
@@ -1945,16 +1945,17 @@
 osition)
+)
 ;%0A  %7D%0A%0A 
@@ -2084,144 +2084,8 @@
  %7D%0A%0A
-  private CsgSolid translate(Vector3d vector) %7B%0A    return new CsgSolid(toCsg().transformed(Transform.unity().translate(vector)));%0A  %7D%0A%0A
   pu
@@ -2149,37 +2149,16 @@
  return 
-new CsgSolid(toCsg().
 transfor
@@ -2158,18 +2158,16 @@
 ransform
-ed
 (rotatio
@@ -2195,17 +2195,16 @@
  angle))
-)
 ;%0A  %7D%0A%0A 
@@ -2212,16 +2212,23 @@
 private 
+static 
 Transfor
@@ -2681,37 +2681,16 @@
  return 
-new CsgSolid(toCsg().
 transfor
@@ -2682,34 +2682,32 @@
 return transform
-ed
 (Transform.unity
@@ -2736,17 +2736,16 @@
 ormal)))
-)
 ;%0A  %7D%0A%0A 
@@ -3169,37 +3169,16 @@
  return 
-new CsgSolid(toCsg().
 transfor
@@ -3178,18 +3178,16 @@
 ransform
-ed
 (Transfo
@@ -3211,17 +3211,16 @@
 factor))
-)
 ;%0A  %7D%0A%0A 

refactored out AbstractSolid.transform(Transform)