/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Signal.java
 *
 * Copyright (c) 2009, Static Free Software. All rights reserved.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) 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/>.
 */
package com.sun.electric.tool.simulation;

import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.geometry.btree.unboxed.LatticeOperation;
import com.sun.electric.database.geometry.btree.unboxed.Unboxed;
import com.sun.electric.database.geometry.btree.unboxed.UnboxedHalfDouble;
import com.sun.electric.tool.user.waveform.Panel;
import com.sun.electric.tool.user.waveform.WaveSignal;
import com.sun.electric.tool.user.waveform.Panel.WaveSelection;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
import java.util.List;

/**
 *  An implementation of Sample for complex data.  Holds 
 *  two doubles internally: real and imaginary.
 */
public class ComplexSample extends ScalarSample implements Sample
{
    private double real;
    private double imag;
    private double hypot;

    public ComplexSample(double real, double imag)
    {
        super(Math.hypot(real, imag));
    	hypot = Math.hypot(real, imag);
        this.real = real;
        this.imag = imag;
    }

    public double getReal() { return real; }
    public double getImag() { return imag; }

    public boolean equals(Object o)
    {
        if (o==null || !(o instanceof ComplexSample)) return false;
        ComplexSample cs = (ComplexSample)o;
        return cs.real==real && cs.imag==imag;
    }

    public int hashCode()
    {
        long l = Double.doubleToLongBits(real) ^ Double.doubleToLongBits(imag);
        return ((int)(l & 0xffffffff)) ^ ((int)((l >> 32) & 0xffffffff));
    }

    public boolean isLogicX() { return false; }
    public boolean isLogicZ() { return false; }

    public Sample lub(Sample s)
    {
        if (!(s instanceof ComplexSample))
            throw new RuntimeException("tried to call ComplexSample.lub("+s.getClass().getName()+")");
        ComplexSample cs = (ComplexSample)s;
        if (cs.real >= real && cs.imag >= imag) return cs;
        if (cs.real <= real && cs.imag <= imag) return this;
        return new ComplexSample(Math.max(real, cs.real), Math.max(imag, cs.imag));
    }

    public Sample glb(Sample s)
    {
        if (!(s instanceof ComplexSample))
            throw new RuntimeException("tried to call ComplexSample.glb("+s.getClass().getName()+")");
        ComplexSample cs = (ComplexSample)s;
        if (cs.real >= real && cs.imag >= imag) return this;
        if (cs.real <= real && cs.imag <= imag) return cs;
        return new ComplexSample(Math.min(real, cs.real), Math.min(imag, cs.imag));
    }

    public double getMinValue() { return hypot; /*Math.min(real, imag);*/ }

    public double getMaxValue() { return hypot; /*Math.max(real, imag);*/ }

    public static final Unboxed<ComplexSample> unboxer = new Unboxed<ComplexSample>()
    {
        public int getSize() { return UnboxedHalfDouble.instance.getSize()*2; }
        public ComplexSample deserialize(byte[] buf, int ofs)
        {
            return new ComplexSample(UnboxedHalfDouble.instance.deserialize(buf, ofs),
                                     UnboxedHalfDouble.instance.deserialize(buf, ofs+UnboxedHalfDouble.instance.getSize()));
        }
        public void serialize(ComplexSample v, byte[] buf, int ofs)
        {
            UnboxedHalfDouble.instance.serialize(v.real, buf, ofs);
            UnboxedHalfDouble.instance.serialize(v.imag, buf, ofs+UnboxedHalfDouble.instance.getSize());
        }
    };

    private static final LatticeOperation<ComplexSample> latticeOp =
        new LatticeOperation<ComplexSample>(unboxer)
    {
        private final int sz = UnboxedHalfDouble.instance.getSize();
        public void glb(byte[] buf1, int ofs1, byte[] buf2, int ofs2, byte[] dest, int dest_ofs)
        {
            ScalarSample.latticeOp.glb(buf1, ofs1,    buf2, ofs2,    dest, dest_ofs);
            ScalarSample.latticeOp.glb(buf1, ofs1+sz, buf2, ofs2+sz, dest, dest_ofs+sz);
        }
        public void lub(byte[] buf1, int ofs1, byte[] buf2, int ofs2, byte[] dest, int dest_ofs)
        {
            ScalarSample.latticeOp.lub(buf1, ofs1,    buf2, ofs2,    dest, dest_ofs);
            ScalarSample.latticeOp.lub(buf1, ofs1+sz, buf2, ofs2+sz, dest, dest_ofs+sz);
        }
    };

    public static Signal<ComplexSample> createComplexSignal(SignalCollection sc, Stimuli sd, String signalName, String signalContext)
    {
    	/**
    	 *  Adam says: This class is an _anonymous_ inner class for a reason.  Although XXXSample.createSignal() returns a
    	 *  Signal<XXXSample>, it is important that other code does not assume this is the only way such signals might
    	 *  arise.  Phrased differently, if there were an inner class XXXSample.SignalOfXXX extends Signal<XXXSample>,
    	 *  you might see other code write "if (x instanceof XXXSample.SignalOfXXX) { ... }" -- and we don't want people
    	 *  to do this.  So, by making the class anonymous, we intentionally deprive other code of the ability to do these
    	 *  instanceof checks.
    	 */
        Signal<ComplexSample> ret =
            new BTreeSignal<ComplexSample>(sc, sd, signalName, signalContext, false, BTreeSignal.getTree(unboxer, latticeOp, sd))
        {
            public void plot(Panel panel, Graphics g, WaveSignal ws, Color light, List<PolyBase> forPs,
            	Rectangle2D bounds, List<WaveSelection> selectedObjects, Signal<?> xAxisSignal)
            {
            	ScalarSample.plotSig(this, panel, g, ws, light, forPs, bounds, selectedObjects, xAxisSignal);
            }
        };
        return ret;
    }

}
