/*
 * Copyright 2007 Sun Microsystems, Inc.
 *
 * This file is part of jVoiceBridge.
 *
 * jVoiceBridge is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation and distributed hereunder 
 * to you.
 *
 * jVoiceBridge 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Sun designates this particular file as subject to the "Classpath"
 * exception as provided by Sun in the License file that accompanied this 
 * code. 
 */

package com.sun.voip.server;

import com.sun.voip.Logger;
import com.sun.voip.MixDataSource;

/*
 * There is a MixDescriptor for each MixDataSource whose volume needs to be
 * adjusted.  A MixDataSource's volume needs to be adjusted if the 
 * MixDataSource is member in a whisper group or if a member has a 
 * custom mix volume for another member.
 */
class MixDescriptor {
    private static final String ZERO_VOLUME_PROPERTY =
	"com.sun.voip.server.ZERO_VOLUME";

    private static double zeroVolume = .0001;

    private MixDataSource mixDataSource;
    private double attenuation;

    /*
     * The spatial values consist of 4 doubles.
     * The first is frontBack with -1 being all the way back an +1 
     * all the way to the front.
     * The second double is leftRight with -1 all the way to the left
     * and +1 all the way to the right.
     * The third double is upDown with -1 all the way down and +1
     * all the way to the top.
     * The fourth double is the non-negative volume level.
     */
    private double[] spatialValues;

    private boolean isMuted;

    static {
	String s = System.getProperty(ZERO_VOLUME_PROPERTY);

	if (s != null) {
	    try {
		zeroVolume = Double.parseDouble(s);

		if (zeroVolume < 0) {
		    zeroVolume = 0;
		}
	    } catch (NumberFormatException e) {
		Logger.println("Invalid zero volume:  " + s);
	    }
	}
    }

    public MixDescriptor(MixDataSource mixDataSource, double attenuation) {
	this(mixDataSource, attenuation, null);
    }

    public MixDescriptor(MixDataSource mixDataSource, double attenuation,
	    double[] spatialValues) {

	this.mixDataSource = mixDataSource;
	this.attenuation = attenuation;

	if (spatialValues != null) {
	    this.spatialValues = new double[4];

	    this.spatialValues[0] = spatialValues[0];
	    this.spatialValues[1] = spatialValues[1];
	    this.spatialValues[2] = spatialValues[2];
	    this.spatialValues[3] = spatialValues[3];
	} else {
	    this.spatialValues = null;
	}
    }

    public void setMixDataSource(MixDataSource mixDataSource) {
	this.mixDataSource = mixDataSource;
    }

    public MixDataSource getMixDataSource() {
	return mixDataSource;
    }

    public void setAttenuation(double attenuation) {
	this.attenuation = attenuation;
    }

    public double getAttenuation() {
	return attenuation;
    }

    //public void adjustSpatialVolume(double volume) {
    //	spatialValues[3] += volume;
    //}

    public boolean isMuted() {
	return isMuted;
    }

    public double getEffectiveVolume() {
	if (isMuted()) {
	   return 0;
	}

	if (spatialValues != null) {
	    return spatialValues[3] * attenuation;
	}

	return attenuation;
    }

    public void setMuted(boolean isMuted) {
	this.isMuted = isMuted;
    }

    public void setSpatialValues(double[] spatialValues) {
	this.spatialValues = spatialValues;
    }

    public boolean isPrivateMix() {
	return spatialValues != null;
    }

    public static boolean isZeroVolume(double volume) {
	return volume <= zeroVolume;
    }

    public boolean isNop() {
	return isNop(spatialValues, attenuation);
    }

    public static boolean isNop(double[] spatialValues, double attenuation) {
	if (spatialValues == null) {
	    return attenuation == 1;
	}

	return isSpatiallyNeutral(spatialValues) && spatialValues[3] == 1
	    && attenuation == 1;
    }

    public boolean isSpatiallyNeutral() {
	return isSpatiallyNeutral(spatialValues);
    }

    public static boolean isSpatiallyNeutral(double[] spatialValues) {
	if (spatialValues == null) {
	    return true;
	}

	return spatialValues[0] == 0 &&
	    spatialValues[1] == 0 &&
	    spatialValues[2] == 0;
    }

    public double[] getSpatialValues() {
	return spatialValues;
    }

    public boolean equals(MixDataSource mixDataSource, 
	    double[] spatialValues) {

	if (this.mixDataSource != mixDataSource) {
	    return false;
	}

	if (this.spatialValues[3] == 0 && spatialValues[3] == 0) {
	    return true;
	}

	return this.spatialValues[0] == spatialValues[0] &&
	    this.spatialValues[1] == spatialValues[1] &&
	    this.spatialValues[2] == spatialValues[2] &&
	    this.spatialValues[3] == spatialValues[3];
    }

    public Object clone() {
	MixDescriptor mixDescriptor = new MixDescriptor(
	    mixDataSource, attenuation, spatialValues);

	mixDescriptor.setMuted(isMuted);
	return mixDescriptor;
    }

    public String toString() {
	String s = "";

	if (mixDataSource != null) {
	    s += mixDataSource;
	}

	s += values();

	return s;
    }

    public String toAbbreviatedString() {
        String s = "";

	if (mixDataSource != null) {
            s += mixDataSource.toAbbreviatedString();
	}

	s += values();
	
	return s;
    }

    private String values() {
	double frontBack = 1;
	double leftRight = 0;
	double upDown = 0;
	double volume = 1;

	if (spatialValues != null) {
	    frontBack = spatialValues[0];
	    leftRight = spatialValues[1];
	    upDown = spatialValues[2];
	    volume = spatialValues[3];
	}
	    
	String s = "  attenuation = " + attenuation;

	s += ", frontBack = " + (Math.round(frontBack * 1000) / 1000.);

	s += ", leftRight = " + (Math.round(leftRight * 10000) / 10000.);

	s += ", upDown = " + (Math.round(upDown * 10000) / 10000.);

	s += ", volume = " + (Math.round(volume * 1000) / 1000.);

	s += ", effectiveVolume = " 
	    + (Math.round(attenuation * volume * 1000) / 1000.);

	if (isMuted) {
	    s += ", MUTED";
	}

	return s;
    }

}
