/* UnderOverAtom.java
 * =========================================================================
 * This file is part of the JMathTeX Library - http://jmathtex.sourceforge.net
 *
 * Copyright (C) 2004-2007 Universiteit Gent
 *
 * This program 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 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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.
 *
 * A copy of the GNU General Public License can be found in the file
 * LICENSE.txt provided with the source distribution of this program (see
 * the META-INF directory in the source jar). This license can also be
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 *
 * If you did not receive a copy of the GNU General Public License along
 * with this program, contact the lead developer, or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 */

package be.ugent.caagt.jmathtex;

/**
 * An atom representing another atom with an atom above it (if not null) seperated
 * by a kern and in a smaller size depending on "overScriptSize" and/or an atom under
 * it (if not null) seperated by a kern and in a smaller size depending on "underScriptSize"
 */
class UnderOverAtom extends Atom {
    
    // base, underscript and overscript
    private final Atom base;
    private final Atom under;
    private final Atom over;
    
    // kern between base and under- and overscript
    private final float underSpace;
    private final float overSpace;
    
    // units for the kerns
    private final int underUnit; // NOPMD
     // TODO: seems never to be used?
    private final int overUnit;
    
    // whether the under- and overscript should be drawn in a smaller size
    private final boolean underScriptSize;
    private final boolean overScriptSize;
    
    public UnderOverAtom(Atom base, Atom underOver, int underOverUnit,
            float underOverSpace, boolean underOverScriptSize, boolean over) {
        // check if unit is valid
        SpaceAtom.checkUnit(underOverUnit);
        
        // units valid
        this.base = base;
        // TODO: split into two different classes?
        
        if (over) {
            this.under = null;
            this.underSpace = 0.0f;
            this.underUnit = 0;
            this.underScriptSize = false;
            this.over = underOver;
            this.overUnit = underOverUnit;
            this.overSpace = underOverSpace;
            this.overScriptSize = underOverScriptSize;
        } else {
            this.under = underOver;
            this.underUnit = underOverUnit;
            this.underSpace = underOverSpace;
            this.underScriptSize = underOverScriptSize;
            this.overSpace = 0.0f;
            this.over = null;
            this.overUnit = 0;
            this.overScriptSize = false;
        }
    }
    
    public UnderOverAtom(Atom base, Atom under, int underUnit, float underSpace,
            boolean underScriptSize, Atom over, int overUnit, float overSpace,
            boolean overScriptSize) throws InvalidUnitException {
        // check if units are valid
        SpaceAtom.checkUnit(underUnit);
        SpaceAtom.checkUnit(overUnit);
        
        // units valid
        this.base = base;
        this.under = under;
        this.underUnit = underUnit;
        this.underSpace = underSpace;
        this.underScriptSize = underScriptSize;
        this.over = over;
        this.overUnit = overUnit;
        this.overSpace = overSpace;
        this.overScriptSize = overScriptSize;
    }
    
    public Box createBox(TeXEnvironment env) {
        
        // create boxes in right style and calculate maximum width
        Box b = (base == null ? new StrutBox(0, 0, 0, 0) : base.createBox(env));
        Box o = null, u = null;
        float max = b.getWidth();
        if (over != null) {
            o = over.createBox(overScriptSize ? env.subStyle() : env);
            max = Math.max(max, o.getWidth());
        }
        if (under != null) {
            u = under.createBox(underScriptSize ? env.subStyle() : env);
            max = Math.max(max, u.getWidth());
        }
        
        // create vertical box
        VerticalBox vBox = new VerticalBox();
        
        // last font used by the base (for Mspace atoms following)
        env.setLastFontId(b.getLastFontId());
        
        // overscript + space
        if (over != null) {
            vBox.add(changeWidth(o, max));
            // unit will be valid (checked in constructor)
            vBox.add(new SpaceAtom(overUnit, 0, overSpace, 0).createBox(env));
        }
        
        // base
        vBox.add(changeWidth(b, max));
        
        // calculate future height of the vertical box (to make sure that the base
        // stays on the baseline!)
        float h = vBox.getHeight() + vBox.getDepth() - b.getDepth();
        
        // underscript + space
        if (under != null) {
            // unit will be valid (checked in constructor)
            vBox.add(new SpaceAtom(overUnit, 0, underSpace, 0).createBox(env));
            vBox.add(changeWidth(u, max));
        }
        
        // set height and depth
        vBox.setDepth(vBox.getHeight() + vBox.getDepth() - h);
        vBox.setHeight(h);
        
        return vBox;
    }
    
    private static Box changeWidth(Box b, float maxWidth) {
        if (b != null && Math.abs(maxWidth - b.getWidth()) > TeXFormula.PREC)
            return new HorizontalBox(b, maxWidth, TeXConstants.ALIGN_CENTER);
        else
            return b;
    }
}
