/*
 * This file is part of artnet4j.
 * 
 * Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
 * 
 * artnet4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * artnet4j is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
 */

package de.ctdo.bunti.artnet.packets;

import de.ctdo.bunti.dmx.DMX;

public class ArtDmxPacket extends ArtNetPacket {
    private static final int DMX_PACKET_MAX_LENGTH = 530;
    private static final int DMX_PACKET_HEADER_LENGTH = 18;
    private static final int OFFSET_SEQUENCE = 12;
    private static final int OFFSET_PHYSICAL = 13;
    private static final int OFFSET_UNIVERSE = 14;
    private static final int OFFSET_DMX_LENGTH = 16;
    private static final int LOWER_NIBBLE = 0x0f;
    private static final int HALF_BYTE = 4;
    private static final int SEQUENCE_ID_MAX = 0xff;

    private int numChannels;
    private int sequenceID;
    private int subnetID;
    private int universeID;

    public ArtDmxPacket() {
        super(PacketType.ART_OUTPUT, DMX_PACKET_MAX_LENGTH);
        getData().setInt8(0, OFFSET_PHYSICAL);
        setUniverse(0, 0);
    }

    /**
     * @return the actual packet size used. If an odd number DMX channels is
     *         used, the packet size is made even automatically.
     * 
     * @see ArtNetPacket#getLength()
     */
    @Override
    public final int getLength() {
        return DMX_PACKET_HEADER_LENGTH + (1 == numChannels % 2 ? numChannels + 1 : numChannels);
    }

    /**
     * @return the number of DMX channels
     */
    public final int getNumChannels() {
        return numChannels;
    }

    /**
     * @return the sequenceID
     */
    public final int getSequenceID() {
        return sequenceID;
    }

    /**
     * @return the subnetID
     */
    public final int getSubnetID() {
        return subnetID;
    }

    /**
     * @return the universeID
     */
    public final int getUniverseID() {
        return universeID;
    }

    public final void setDMX(byte[] dmxData, int numChannels) {
        setNumChannels(numChannels);
        getData().setByteChunk(dmxData, DMX_PACKET_HEADER_LENGTH, numChannels);
        getData().setInt16((1 == numChannels % 2 ? numChannels + 1 : numChannels), OFFSET_DMX_LENGTH);
    }

    /**
     * @param numChannels
     *            the number of DMX channels to set
     */
    public final void setNumChannels(int numChannels) {
        this.numChannels = numChannels > DMX.DMX_CHANNEL_INDEX_MAX ? DMX.DMX_CHANNEL_INDEX_MAX : numChannels;
    }

    public final void setSequenceID(int id) {
        sequenceID = id % SEQUENCE_ID_MAX;
        getData().setInt8(sequenceID, OFFSET_SEQUENCE);
    }

    /**
     * @param subnetID the subnetID to set
     */
    public final void setSubnetID(int subnetID) {
        this.subnetID = subnetID & LOWER_NIBBLE;
        setUniverse(this.subnetID, this.universeID);
    }

    /**
     * @param universeID the universeID to set
     */
    public final void setUniverseID(int universeID) {
        this.universeID = universeID & LOWER_NIBBLE;
        setUniverse(this.subnetID, this.universeID);
    }

    /**
     * Sets universe and subnet into data array. Needed because subnet and universe are both 4 bit and share one byte
     * @param subnetID
     * @param universeID
     */
    private void setUniverse(int subnetID, int universeID) {
        getData().setInt16LE(subnetID << HALF_BYTE | universeID, OFFSET_UNIVERSE);
    }
}