/**
 *  CompositeTextLine.java
 *
Copyright (c) 2018, Innovatics Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and / or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
 *  This class was designed and implemented by Jon T. Swanson, Ph.D.
 *
 *  Refactored and integrated into the project by Eugene Dragoev - 2nd June 2012.
 */


package com.pdfjet;

import java.util.*;


/**
 *  Used to create composite text line objects.
 *
 *
 */
public class CompositeTextLine implements Drawable {

    private static final int X = 0;
    private static final int Y = 1;

    private List<TextLine> textLines = new ArrayList<TextLine>();

    private float[] position = new float[2];
    private float[] current  = new float[2];

    // Subscript and Superscript size factors
    private float subscript_size_factor    = 0.583f;
    private float superscript_size_factor  = 0.583f;

    // Subscript and Superscript positions in relation to the base font
    private float superscript_position = 0.350f;
    private float subscript_position   = 0.141f;

    private float fontSize = 0f;


    public CompositeTextLine(float x, float y) {
        position[X] = x;
        position[Y] = y;
        current[X]  = x;
        current[Y]  = y;
    }


    /**
     *  Sets the font size.
     *
     *  @param fontSize the font size.
     */
    public void setFontSize(float fontSize) {
        this.fontSize = fontSize;
    }


    /**
     *  Gets the font size.
     *
     *  @return fontSize the font size.
     */
    public float getFontSize() {
        return fontSize;
    }


    /**
     *  Sets the superscript factor for this composite text line.
     *
     *  @param superscript the superscript size factor.
     */
    public void setSuperscriptFactor(float superscript) {
        this.superscript_size_factor = superscript;
    }


    /**
     *  Gets the superscript factor for this text line.
     *
     *  @return superscript the superscript size factor.
     */
    public float getSuperscriptFactor() {
        return superscript_size_factor;
    }


    /**
     *  Sets the subscript factor for this composite text line.
     *
     *  @param subscript the subscript size factor.
     */
    public void setSubscriptFactor(float subscript) {
        this.subscript_size_factor = subscript;
    }


    /**
     *  Gets the subscript factor for this text line.
     *
     *  @return subscript the subscript size factor.
     */
    public float getSubscriptFactor() {
        return subscript_size_factor;
    }


    /**
     *  Sets the superscript position for this composite text line.
     *
     *  @param superscript_position the superscript position.
     */
    public void setSuperscriptPosition(float superscript_position) {
        this.superscript_position = superscript_position;
    }


    /**
     *  Gets the superscript position for this text line.
     *
     *  @return superscript_position the superscript position.
     */
    public float getSuperscriptPosition() {
        return superscript_position;
    }


    /**
     *  Sets the subscript position for this composite text line.
     *
     *  @param subscript_position the subscript position.
     */
    public void setSubscriptPosition(float subscript_position) {
        this.subscript_position = subscript_position;
    }


    /**
     *  Gets the subscript position for this text line.
     *
     *  @return subscript_position the subscript position.
     */
    public float getSubscriptPosition() {
        return subscript_position;
    }


    /**
     *  Add a new text line.
     *
     *  Find the current font, current size and effects (normal, super or subscript)
     *  Set the position of the component to the starting stored as current position
     *  Set the size and offset based on effects
     *  Set the new current position
     *
     *  @param component the component.
     */
    public void addComponent(TextLine component) {
        if (component.getTextEffect() == Effect.SUPERSCRIPT) {
            if (fontSize > 0f) {
                component.getFont().setSize(fontSize * superscript_size_factor);
            }
            component.setLocation(
                    current[X],
                    current[Y] - fontSize * superscript_position);
        }
        else if (component.getTextEffect() == Effect.SUBSCRIPT) {
            if (fontSize > 0f) {
                component.getFont().setSize(fontSize * subscript_size_factor);
            }
            component.setLocation(
                    current[X],
                    current[Y] + fontSize * subscript_position);
        }
        else {
            if (fontSize > 0f) {
                component.getFont().setSize(fontSize);
            }
            component.setLocation(current[X], current[Y]);
        }
        current[X] += component.getWidth();
        textLines.add(component);
    }


    /**
     *  Loop through all the text lines and reset their position based on
     *  the new position set here.
     *
     *  @param x the x coordinate.
     *  @param y the y coordinate.
     */
    public void setPosition(double x, double y) {
        setPosition((float) x, (float) y);
    }


    /**
     *  Loop through all the text lines and reset their position based on
     *  the new position set here.
     *
     *  @param x the x coordinate.
     *  @param y the y coordinate.
     */
    public void setPosition(float x, float y) {
        setLocation(x, y);
    }


    /**
     *  Loop through all the text lines and reset their location based on
     *  the new location set here.
     *
     *  @param x the x coordinate.
     *  @param y the y coordinate.
     */
    public void setLocation(float x, float y) {
        position[X] = x;
        position[Y] = y;
        current[X]  = x;
        current[Y]  = y;

        if (textLines == null || textLines.size() == 0) {
            return;
        }

        for (TextLine component : textLines) {
            if (component.getTextEffect() == Effect.SUPERSCRIPT) {
                component.setLocation(
                        current[X],
                        current[Y] - fontSize * superscript_position);
            }
            else if (component.getTextEffect() == Effect.SUBSCRIPT) {
                component.setLocation(
                        current[X],
                        current[Y] + fontSize * subscript_position);
            }
            else {
                component.setLocation(current[X], current[Y]);
            }
            current[X] += component.getWidth();
        }
    }


    /**
     *  Return the position of this composite text line.
     *
     *  @return the position of this composite text line.
     */
    public float[] getPosition() {
        return position;
    }


    /**
     *  Return the nth entry in the TextLine array.
     *
     *  @param index the index of the nth element.
     *  @return the text line at the specified index.
     */
    public TextLine getTextLine(int index) {
        if (textLines == null || textLines.size() == 0) {
            return null;
        }
        if (index < 0 || index > textLines.size() - 1) {
            return null;
        }
        return textLines.get(index);
    }


    /**
     *  Returns the number of text lines.
     *
     *  @return the number of text lines.
     */
    public int getNumberOfTextLines() {
       return textLines.size();
    }


    /**
     *  Returns the vertical coordinates of the top left and bottom right corners
     *  of the bounding box of this composite text line.
     *
     *  @return the an array containing the vertical coordinates.
     */
    public float[] getMinMax() {
        float min = position[Y];
        float max = position[Y];
        float cur;

        for (TextLine component : textLines) {
            if (component.getTextEffect() == Effect.SUPERSCRIPT) {
                cur = (position[Y] - component.getFont().ascent) - fontSize * superscript_position;
                if (cur < min)
                    min = cur;
            }
            else if (component.getTextEffect() == Effect.SUBSCRIPT) {
                cur = (position[Y] - component.getFont().descent) + fontSize * subscript_position;
                if (cur > max)
                    max = cur;
            }
            else {
                cur = position[Y] - component.getFont().ascent;
                if (cur < min)
                    min = cur;
                cur = position[Y] - component.getFont().descent;
                if (cur > max)
                    max = cur;
            }
        }

        return new float[] {min, max};
    }


    /**
     *  Returns the height of this CompositeTextLine.
     *
     *  @return the height.
     */
    public float getHeight() {
        float[] yy = getMinMax();
        return yy[1] - yy[0];
    }


    /**
     *  Returns the width of this CompositeTextLine.
     *
     *  @return the width.
     */
    public float getWidth() {
        return (current[X] - position[X]);
    }


    /**
     *  Draws this line on the specified page.
     *
     *  @param page the page to draw this line on.
     *  @return x and y coordinates of the bottom right corner of this component.
     *  @throws Exception
     */
    public float[] drawOn(Page page) throws Exception {
        float x_max = 0f;
        float y_max = 0f;
        // Loop through all the text lines and draw them on the page
        for (TextLine textLine : textLines) {
            float[] xy = textLine.drawOn(page);
            x_max = Math.max(x_max, xy[0]);
            y_max = Math.max(y_max, xy[1]);
        }
        return new float[] {x_max, y_max};
    }

}
