// 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;
package com.coillighting.udder.infrastructure;



/** This class is used as a JSON schema spec and output datatype for the Boon
 *  JsonFactory when it deserializes JSON patch sheets exported from Eric's
 *  visualizer. This is just an intermediate representation. We immediately
 *  convert these PatchElements to Devices after parsing.
 *
 *  Example input consisting of three JSON-serialized PatchElements:
 *
 *      [{
 *          "point": [-111, 92.33984355642154, -78.20986874321204],
 *          "group": 0,
 *          "address": 57
 *      }, {
 *          "point": [-111, 93.58520914575311, -78.05527879900943],
 *          "group": 0,
 *          "address": 56
 *      }, {
 *          "point": [-111, 94.70067095432645, -77.91681404627441],
 *          "group": 0,
 *          "address": 55
 *      }]
 *
 *  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.
 */
public class PatchElement {

    private double[] point;
    private int group;
    private int address;

    public PatchElement(double[] point, int group, int address) {
        if(address < 0) throw new IllegalArgumentException("Negative device address: " + address);
        if(group < 0) throw new IllegalArgumentException("Negative group index: " + group);
        this.point = point;
        this.group = group;
        this.address = address;
        this.getZ(); // validates point is between 1 and 3 dimensions
    }

    /* A 1D layout is mandatory. 2D is optional. Return y=0 if unspecified. */
    private double getY() {
        double y=0.0;
        if(this.point == null) {
            throw new IllegalArgumentException(
                "You must provide a point in space for each output device in the patch sheet.");
        } else if(this.point.length >= 2) {
            y = this.point[1];
        } else {
            throw new IllegalArgumentException("Udder does not support "
                + this.point.length + "-dimensional shows.");
        }
        return y;
    }

    /* A 1D layout is mandatory. 3D is optional. Return z=0 if unspecified. */
    private double getZ() {
        double z=0.0;
        if(this.point == null) {
            throw new IllegalArgumentException(
                "You must provide a point in space for each output device in the patch sheet.");
        } else if(this.point.length == 3) {
            z = this.point[2];
        } else {
            throw new IllegalArgumentException("Udder does not support "
                + this.point.length + "-dimensional shows.");
        }
        return z;
    }

    /** Convert this intermediate representation into a full-fledged 3D Udder
     *  Device.
     */
// 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/PatchElement.java
import com.coillighting.udder.model.Device;

package com.coillighting.udder.infrastructure;



/** This class is used as a JSON schema spec and output datatype for the Boon
 *  JsonFactory when it deserializes JSON patch sheets exported from Eric's
 *  visualizer. This is just an intermediate representation. We immediately
 *  convert these PatchElements to Devices after parsing.
 *
 *  Example input consisting of three JSON-serialized PatchElements:
 *
 *      [{
 *          "point": [-111, 92.33984355642154, -78.20986874321204],
 *          "group": 0,
 *          "address": 57
 *      }, {
 *          "point": [-111, 93.58520914575311, -78.05527879900943],
 *          "group": 0,
 *          "address": 56
 *      }, {
 *          "point": [-111, 94.70067095432645, -77.91681404627441],
 *          "group": 0,
 *          "address": 55
 *      }]
 *
 *  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.
 */
public class PatchElement {

    private double[] point;
    private int group;
    private int address;

    public PatchElement(double[] point, int group, int address) {
        if(address < 0) throw new IllegalArgumentException("Negative device address: " + address);
        if(group < 0) throw new IllegalArgumentException("Negative group index: " + group);
        this.point = point;
        this.group = group;
        this.address = address;
        this.getZ(); // validates point is between 1 and 3 dimensions
    }

    /* A 1D layout is mandatory. 2D is optional. Return y=0 if unspecified. */
    private double getY() {
        double y=0.0;
        if(this.point == null) {
            throw new IllegalArgumentException(
                "You must provide a point in space for each output device in the patch sheet.");
        } else if(this.point.length >= 2) {
            y = this.point[1];
        } else {
            throw new IllegalArgumentException("Udder does not support "
                + this.point.length + "-dimensional shows.");
        }
        return y;
    }

    /* A 1D layout is mandatory. 3D is optional. Return z=0 if unspecified. */
    private double getZ() {
        double z=0.0;
        if(this.point == null) {
            throw new IllegalArgumentException(
                "You must provide a point in space for each output device in the patch sheet.");
        } else if(this.point.length == 3) {
            z = this.point[2];
        } else {
            throw new IllegalArgumentException("Udder does not support "
                + this.point.length + "-dimensional shows.");
        }
        return z;
    }

    /** Convert this intermediate representation into a full-fledged 3D Udder
     *  Device.
     */
    public Device toDevice() {