// Path: udder/src/main/java/com/coillighting/udder/model/Device.java
// public class Device extends Object {
// 
//     /** This Device's address in some arbitrary address space. For the dairy,
//      * this address is in the space of a single OPC channel.
//      *
//      * FUTURE Multiple channels/universes.
//      *
//      * Not public because no effects should be computed on the basis of a
//      * Device's address.
//      */
//     protected int addr = 0;
// 
//     /** A dirt simple grouping mechanism. Each Device belongs to exactly one
//      *  group (for now). For the Dairy installation, this will indicate gate
//      *  0 or gate 1, in case an Animator cares which group the Device is in.
//      *
//      * Public because device group computations happen in the hottest inner loop.
//      */
//     public int group=0;
// 
//     /** Position in model space. Public because device positional
//      *  computations happen in the hottest inner loop.
//      */
//     public double x = 0.0;
//     public double y = 0.0;
//     public double z = 0.0;
// 
//     public Device(int addr, int group, double x, double y, double z) {
//         if(addr < 0) {
//             throw new IllegalArgumentException("Invalid Device address: " + addr);
//         } else if(group < 0) {
//             throw new IllegalArgumentException("Negative group index: " + addr);
//         }
//         this.addr = addr;
//         this.group = group;
//         this.x = x;
//         this.y = y;
//         this.z = z;
//     }
// 
//     public String toString() {
//         return "Device @"+addr+" ["+group+"] ("+x+","+y+","+z+")";
//     }
// 
//     public int getAddr() {
//         return this.addr;
//     }
// 
//     public int getGroup() {
//         return this.group;
//     }
// 
//     // TODO refactor x, y and z as a double[] for consistency with other classes? or refactor as a Point3D (see below). We should standardize here.
//     public double[] getPoint() {
//         return new double[]{x, y, z};
//     }
// 
//     public Point3D getPoint3D() {
//         return new Point3D(x, y, z);
//     }
// 
//     /** Return the 3D bounding box for the given devices. */
//     public static BoundingCube getDeviceBoundingCube(Device[] devices) {
//         if(devices==null) {
//             return null;
//         } else {
//             // FIXME: determine the real min and max values that will compare properly.
//             // A seed value of Double.MAX_VALUE did not work with all comparisons here. WTF?
//             // For now you must place your devices within this cube, sorry:
//             final double MAX_VALUE = 999999999.0;
//             final double MIN_VALUE = -999999999.0;
// 
//             double minx=MAX_VALUE;
//             double maxx=MIN_VALUE;
//             double miny=MAX_VALUE;
//             double maxy=MIN_VALUE;
//             double minz=MAX_VALUE;
//             double maxz=MIN_VALUE;
// 
//             for(Device d: devices) {
//                 double x = d.x;
//                 double y = d.y;
//                 double z = d.z;
//                 if(x < minx) minx = x;
//                 if(x > maxx) maxx = x;
//                 if(y < miny) miny = y;
//                 if(y > maxy) maxy = y;
//                 if(z < minz) minz = z;
//                 if(z > maxz) maxz = z;
//             }
//             return new BoundingCube(minx, miny, minz,
//                 maxx-minx, maxy-miny, maxz-minz);
//         }
//     }
// }


import com.coillighting.udder.model.Device;
import java.util.ArrayList;
import java.util.List;
package com.coillighting.udder.infrastructure;



/** Data structure representing a 3D point in an Open Pixel Control layout
 *  file. We will serialize this as an element in a JSON array. For details
 *  on this dirt simple file format, see the following URL:
 *
 *      https://github.com/zestyping/openpixelcontrol/tree/master/layouts
 *
 *  Example output consisting of three JSON-serialized OpcLayoutPoints:
 *
 *      [
 *          {"point": [1.32, 0.00, 1.32]},
 *          {"point": [1.32, 0.00, 1.21]},
 *          {"point": [1.32, 0.00, 1.10]}
 *      ]
 *
 *  Like Command and PatchElement, this class is structured for compatibility
 *  with the Boon JsonFactory, which automatically binds JSON bidirectionally
 *  to Java classes which adhere to its preferred (bean-style) layout.
 *
 *  After you serialize your JSON layout, you can use it to run the barebones
 *  OpenGL server that comes with Open Pixel Control. Invoke it like this:
 *
 *      $ path/to/bin/gl_server path/to/udder/udder/conf/opc_layout.json
 *
 *  For the Dairy project, we recommend trying Eric Miller's visualizer:
 *
 *      https://github.com/patternleaf/archway
 */
public class OpcLayoutPoint {

    private double[] point;

    public OpcLayoutPoint(double[] point) {
        if(point == null) {
            throw new NullPointerException("point is null");
        } else if(point.length != 3) {
            throw new IllegalArgumentException("invalid point length " + point.length);
        }
        this.point = point;
    }

    public double[] getPoint() {
        return this.point;
    }

    public void scale(double factor) {
        for(int i=0; i<point.length; i++) {
            point[i] *= factor;
        }
    }

    /** Return a list associating OPC address with spatial coordinates.
     *  The Open Pixel Control's gl_server consumes this list and presents
     *  a very simple 3D view of your show, which you can use as a monitor.
     *  See https://github.com/patternleaf/archway for a fine example of how to
     *  create a much snazzier 3D monitor in your browser.
     */
    public static List<OpcLayoutPoint> createOpcLayoutPoints(PatchSheet patchSheet) {
        // We occasionally skip scaling when debugging.
        final boolean autoscale = true;

        // Shrink the layout, which at the Dairy arrived in inches, to fit the
        // limited viewport of the OPC gl model.
        // TODO: automatically compute a decent scale, since the GL viewport
        // lacks auto zoom -- or implement autozoom for gl_server.
        final double glViewportScale = 3.0;

        final double [] origin = {0.0, 0.0, 0.0};

// Path: udder/src/main/java/com/coillighting/udder/model/Device.java
// public class Device extends Object {
// 
//     /** This Device's address in some arbitrary address space. For the dairy,
//      * this address is in the space of a single OPC channel.
//      *
//      * FUTURE Multiple channels/universes.
//      *
//      * Not public because no effects should be computed on the basis of a
//      * Device's address.
//      */
//     protected int addr = 0;
// 
//     /** A dirt simple grouping mechanism. Each Device belongs to exactly one
//      *  group (for now). For the Dairy installation, this will indicate gate
//      *  0 or gate 1, in case an Animator cares which group the Device is in.
//      *
//      * Public because device group computations happen in the hottest inner loop.
//      */
//     public int group=0;
// 
//     /** Position in model space. Public because device positional
//      *  computations happen in the hottest inner loop.
//      */
//     public double x = 0.0;
//     public double y = 0.0;
//     public double z = 0.0;
// 
//     public Device(int addr, int group, double x, double y, double z) {
//         if(addr < 0) {
//             throw new IllegalArgumentException("Invalid Device address: " + addr);
//         } else if(group < 0) {
//             throw new IllegalArgumentException("Negative group index: " + addr);
//         }
//         this.addr = addr;
//         this.group = group;
//         this.x = x;
//         this.y = y;
//         this.z = z;
//     }
// 
//     public String toString() {
//         return "Device @"+addr+" ["+group+"] ("+x+","+y+","+z+")";
//     }
// 
//     public int getAddr() {
//         return this.addr;
//     }
// 
//     public int getGroup() {
//         return this.group;
//     }
// 
//     // TODO refactor x, y and z as a double[] for consistency with other classes? or refactor as a Point3D (see below). We should standardize here.
//     public double[] getPoint() {
//         return new double[]{x, y, z};
//     }
// 
//     public Point3D getPoint3D() {
//         return new Point3D(x, y, z);
//     }
// 
//     /** Return the 3D bounding box for the given devices. */
//     public static BoundingCube getDeviceBoundingCube(Device[] devices) {
//         if(devices==null) {
//             return null;
//         } else {
//             // FIXME: determine the real min and max values that will compare properly.
//             // A seed value of Double.MAX_VALUE did not work with all comparisons here. WTF?
//             // For now you must place your devices within this cube, sorry:
//             final double MAX_VALUE = 999999999.0;
//             final double MIN_VALUE = -999999999.0;
// 
//             double minx=MAX_VALUE;
//             double maxx=MIN_VALUE;
//             double miny=MAX_VALUE;
//             double maxy=MIN_VALUE;
//             double minz=MAX_VALUE;
//             double maxz=MIN_VALUE;
// 
//             for(Device d: devices) {
//                 double x = d.x;
//                 double y = d.y;
//                 double z = d.z;
//                 if(x < minx) minx = x;
//                 if(x > maxx) maxx = x;
//                 if(y < miny) miny = y;
//                 if(y > maxy) maxy = y;
//                 if(z < minz) minz = z;
//                 if(z > maxz) maxz = z;
//             }
//             return new BoundingCube(minx, miny, minz,
//                 maxx-minx, maxy-miny, maxz-minz);
//         }
//     }
// }


// Path: udder/src/main/java/com/coillighting/udder/infrastructure/OpcLayoutPoint.java
import com.coillighting.udder.model.Device;
import java.util.ArrayList;
import java.util.List;

package com.coillighting.udder.infrastructure;



/** Data structure representing a 3D point in an Open Pixel Control layout
 *  file. We will serialize this as an element in a JSON array. For details
 *  on this dirt simple file format, see the following URL:
 *
 *      https://github.com/zestyping/openpixelcontrol/tree/master/layouts
 *
 *  Example output consisting of three JSON-serialized OpcLayoutPoints:
 *
 *      [
 *          {"point": [1.32, 0.00, 1.32]},
 *          {"point": [1.32, 0.00, 1.21]},
 *          {"point": [1.32, 0.00, 1.10]}
 *      ]
 *
 *  Like Command and PatchElement, this class is structured for compatibility
 *  with the Boon JsonFactory, which automatically binds JSON bidirectionally
 *  to Java classes which adhere to its preferred (bean-style) layout.
 *
 *  After you serialize your JSON layout, you can use it to run the barebones
 *  OpenGL server that comes with Open Pixel Control. Invoke it like this:
 *
 *      $ path/to/bin/gl_server path/to/udder/udder/conf/opc_layout.json
 *
 *  For the Dairy project, we recommend trying Eric Miller's visualizer:
 *
 *      https://github.com/patternleaf/archway
 */
public class OpcLayoutPoint {

    private double[] point;

    public OpcLayoutPoint(double[] point) {
        if(point == null) {
            throw new NullPointerException("point is null");
        } else if(point.length != 3) {
            throw new IllegalArgumentException("invalid point length " + point.length);
        }
        this.point = point;
    }

    public double[] getPoint() {
        return this.point;
    }

    public void scale(double factor) {
        for(int i=0; i<point.length; i++) {
            point[i] *= factor;
        }
    }

    /** Return a list associating OPC address with spatial coordinates.
     *  The Open Pixel Control's gl_server consumes this list and presents
     *  a very simple 3D view of your show, which you can use as a monitor.
     *  See https://github.com/patternleaf/archway for a fine example of how to
     *  create a much snazzier 3D monitor in your browser.
     */
    public static List<OpcLayoutPoint> createOpcLayoutPoints(PatchSheet patchSheet) {
        // We occasionally skip scaling when debugging.
        final boolean autoscale = true;

        // Shrink the layout, which at the Dairy arrived in inches, to fit the
        // limited viewport of the OPC gl model.
        // TODO: automatically compute a decent scale, since the GL viewport
        // lacks auto zoom -- or implement autozoom for gl_server.
        final double glViewportScale = 3.0;

        final double [] origin = {0.0, 0.0, 0.0};

        final Device[] devices = patchSheet.getModelSpaceDevices();