/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: RtfTableRow.java 1805173 2017-08-16 10:50:04Z ssteiner $ */

package org.apache.fop.render.rtf.rtflib.rtfdoc;

/*
 * This file is part of the RTF library of the FOP project, which was originally
 * created by Bertrand Delacretaz bdelacretaz@codeconsult.ch and by other
 * contributors to the jfor project (www.jfor.org), who agreed to donate jfor to
 * the FOP project.
 */

import java.io.IOException;
import java.io.Writer;

import org.apache.fop.apps.FOPException;

/**
 * <p>Container for RtfTableCell elements.</p>
 *
 * <p>This work was authored by Bertrand Delacretaz (bdelacretaz@codeconsult.ch),
 * Andreas Putz (a.putz@skynamics.com), and
 * Roberto Marra (roberto@link-u.com).</p>
 */

public class RtfTableRow extends RtfContainer implements ITableAttributes {
    private RtfTableCell cell;
//    private RtfExtraRowSet extraRowSet;
    private int id;
    private int highestCell;


    /** Create an RTF element as a child of given container */
    RtfTableRow(RtfTable parent, Writer w, int idNum) throws IOException {
        super(parent, w);
        id = idNum;
    }

    /** Create an RTF element as a child of given container */
    RtfTableRow(RtfTable parent, Writer w, RtfAttributes attrs, int idNum) throws IOException {
        super(parent, w, attrs);
        id = idNum;
    }

    /**
     * Close current cell if any and start a new one
     * @param cellWidth width of new cell
     * @return new RtfTableCell
     * @throws IOException for I/O problems
     */
    public RtfTableCell newTableCell(int cellWidth) throws IOException {
        highestCell++;
        cell = new RtfTableCell(this, writer, cellWidth, highestCell);
        return cell;
    }

    /**
     * Close current cell if any and start a new one
     * @param attrs attributes of new cell
     * @param cellWidth width of new cell
     * @return new RtfTableCell
     * @throws IOException for I/O problems
     */
    public RtfTableCell newTableCell(int cellWidth, RtfAttributes attrs) throws IOException {
        highestCell++;
        cell = new RtfTableCell(this, writer, cellWidth, attrs, highestCell);
        return cell;
    }

    /**
     * Added by Boris POUDEROUS on 07/02/2002
     * in order to add an empty cell that is merged with the cell above.
     * This cell is placed before or after the nested table.
     * @param attrs attributes of new cell
     * @param cellWidth width of new cell
     * @return new RtfTableCell
     * @throws IOException for I/O problems
     */
    public RtfTableCell newTableCellMergedVertically(int cellWidth,
           RtfAttributes attrs) throws IOException {
        highestCell++;
        cell = new RtfTableCell(this, writer, cellWidth, attrs, highestCell);
        cell.setVMerge(RtfTableCell.MERGE_WITH_PREVIOUS);
        return cell;
    }

    /**
     * Added by Boris POUDEROUS on 07/02/2002
     * in order to add an empty cell that is merged with the previous cell.
     * @param attrs attributes of new cell
     * @param cellWidth width of new cell
     * @return new RtfTableCell
     * @throws IOException for I/O problems
     * @throws FOPException if attributes cannot be cloned
     */
    public RtfTableCell newTableCellMergedHorizontally(int cellWidth,
           RtfAttributes attrs) throws IOException, FOPException {
        highestCell++;
        // Added by Normand Masse
        // Inherit attributes from base cell for merge
        RtfAttributes wAttributes = null;
        if (attrs != null) {
            try {
                wAttributes = (RtfAttributes)attrs.clone();
            } catch (CloneNotSupportedException e) {
                throw new FOPException(e);
            }
        }

        cell = new RtfTableCell(this, writer, cellWidth, wAttributes, highestCell);
        cell.setHMerge(RtfTableCell.MERGE_WITH_PREVIOUS);
        return cell;
    }

    /**
     * @throws IOException for I/O problems
     */
    protected void writeRtfPrefix() throws IOException {
        newLine();
        writeGroupMark(true);
    }

    /**
     * Overridden to write trowd and cell definitions before writing our cells
     * @throws IOException for I/O problems
     */
    protected void writeRtfContent() throws IOException {
        if (getTable().isNestedTable()) {
            //nested table
            writeControlWord("intbl");
            //itap is the depth (level) of the current nested table
            writeControlWord("itap" + getTable().getNestedTableDepth());
        } else {
            //normal (not nested) table
            writeRowAndCellsDefintions();
        }
        // now children can write themselves, we have the correct RTF prefix code
        super.writeRtfContent();
    }

    /**
     *
     * @throws IOException In case of a IO-problem
     */
    public void writeRowAndCellsDefintions() throws IOException {
        // render the row and cells definitions
        writeControlWord("trowd");

        if (!getTable().isNestedTable()) {
            writeControlWord("itap0");
        }

        //check for keep-together
        if (attrib.isSet(ITableAttributes.ROW_KEEP_TOGETHER)) {
            writeControlWord(ROW_KEEP_TOGETHER);
        }

        writePaddingAttributes();

        final RtfTable parentTable = (RtfTable) parent;
        adjustBorderProperties(parentTable);

        writeAttributes(attrib, new String[]{ITableAttributes.ATTR_HEADER});
        writeAttributes(attrib, ITableAttributes.ROW_BORDER);
        writeAttributes(attrib, ITableAttributes.CELL_BORDER);
        writeAttributes(attrib, IBorderAttributes.BORDERS);

        if (attrib.isSet(ITableAttributes.ROW_HEIGHT)) {
            writeOneAttribute(
                    ITableAttributes.ROW_HEIGHT,
                    attrib.getValue(ITableAttributes.ROW_HEIGHT));
        }

        // write X positions of our cells
        int xPos = 0;

        final Object leftIndent = attrib.getValue(ITableAttributes.ATTR_ROW_LEFT_INDENT);
        if (leftIndent != null) {
            xPos = (Integer) leftIndent;
        }

        RtfAttributes tableBorderAttributes = getTable().getBorderAttributes();

        int index = 0;
        for (Object o : getChildren()) {
            final RtfElement e = (RtfElement) o;
            if (e instanceof RtfTableCell) {

                RtfTableCell rtfcell = (RtfTableCell) e;

                // Adjust the cell's display attributes so the table's/row's borders
                // are drawn properly.

                if (tableBorderAttributes != null) {
                    // get border attributes from table
                    if (index == 0) {
                        String border = ITableAttributes.CELL_BORDER_LEFT;
                        if (!rtfcell.getRtfAttributes().isSet(border)) {
                            rtfcell.getRtfAttributes().set(border,
                                    (RtfAttributes) tableBorderAttributes.getValue(border));
                        }
                    }

                    if (index == this.getChildCount() - 1) {
                        String border = ITableAttributes.CELL_BORDER_RIGHT;
                        if (!rtfcell.getRtfAttributes().isSet(border)) {
                            rtfcell.getRtfAttributes().set(border,
                                    (RtfAttributes) tableBorderAttributes.getValue(border));
                        }
                    }

                    if (isFirstRow()) {
                        String border = ITableAttributes.CELL_BORDER_TOP;
                        if (!rtfcell.getRtfAttributes().isSet(border)) {
                            rtfcell.getRtfAttributes().set(border,
                                    (RtfAttributes) tableBorderAttributes.getValue(border));
                        }
                    }

                    if ((parentTable != null) && (parentTable.isHighestRow(id))) {
                        String border = ITableAttributes.CELL_BORDER_BOTTOM;
                        if (!rtfcell.getRtfAttributes().isSet(border)) {
                            rtfcell.getRtfAttributes().set(border,
                                    (RtfAttributes) tableBorderAttributes.getValue(border));
                        }
                    }
                }

                // get border attributes from row
                if (index == 0) {
                    if (!rtfcell.getRtfAttributes().isSet(ITableAttributes.CELL_BORDER_LEFT)) {
                        rtfcell.getRtfAttributes().set(ITableAttributes.CELL_BORDER_LEFT,
                                (String) attrib.getValue(ITableAttributes.ROW_BORDER_LEFT));
                    }
                }

                if (index == this.getChildCount() - 1) {
                    if (!rtfcell.getRtfAttributes().isSet(ITableAttributes.CELL_BORDER_RIGHT)) {
                        rtfcell.getRtfAttributes().set(ITableAttributes.CELL_BORDER_RIGHT,
                                (String) attrib.getValue(ITableAttributes.ROW_BORDER_RIGHT));
                    }
                }

                if (isFirstRow()) {
                    if (!rtfcell.getRtfAttributes().isSet(ITableAttributes.CELL_BORDER_TOP)) {
                        rtfcell.getRtfAttributes().set(ITableAttributes.CELL_BORDER_TOP,
                                (String) attrib.getValue(ITableAttributes.ROW_BORDER_TOP));
                    }
                }

                if ((parentTable != null) && (parentTable.isHighestRow(id))) {
                    if (!rtfcell.getRtfAttributes().isSet(ITableAttributes.CELL_BORDER_BOTTOM)) {
                        rtfcell.getRtfAttributes().set(ITableAttributes.CELL_BORDER_BOTTOM,
                                (String) attrib.getValue(ITableAttributes.ROW_BORDER_BOTTOM));
                    }
                }

                // write cell's definition
                xPos = rtfcell.writeCellDef(xPos);
            }
            index++; // Added by Boris POUDEROUS on 2002/07/02
        }

        newLine();
    }

    private void adjustBorderProperties(RtfTable parentTable) {
        // if we have attributes, manipulate border properties
        if (attrib != null && parentTable != null) {

            //if table is only one row long
            if (isFirstRow() && parentTable.isHighestRow(id)) {
                attrib.unset(ITableAttributes.ROW_BORDER_HORIZONTAL);
            //or if row is the first row
            } else if (isFirstRow()) {
                attrib.unset(ITableAttributes.ROW_BORDER_BOTTOM);
            //or if row is the last row
            } else if (parentTable.isHighestRow(id)) {
                attrib.unset(ITableAttributes.ROW_BORDER_TOP);
            //else the row is an inside row
            } else {
                attrib.unset(ITableAttributes.ROW_BORDER_BOTTOM);
                attrib.unset(ITableAttributes.ROW_BORDER_TOP);
            }
        }
    }

    /**
     * Overridden to write RTF suffix code, what comes after our children
     * @throws IOException for I/O problems
     */
    protected void writeRtfSuffix() throws IOException {
        if (getTable().isNestedTable()) {
            //nested table
            writeGroupMark(true);
            writeStarControlWord("nesttableprops");
            writeRowAndCellsDefintions();
            writeControlWordNS("nestrow");
            writeGroupMark(false);

            writeGroupMark(true);
            writeControlWord("nonesttables");
            writeControlWord("par");
            writeGroupMark(false);
        } else {
            writeControlWord("row");
        }

        writeGroupMark(false);
    }

//    RtfExtraRowSet getExtraRowSet() {
//        return extraRowSet;
//    }

    private void writePaddingAttributes()
    throws IOException {
        // Row padding attributes generated in the converter package
        // use RTF 1.6 definitions - try to compute a reasonable RTF 1.5 value
        // out of them if present
        // how to do vertical padding with RTF 1.5?
        if (attrib != null && !attrib.isSet(ATTR_RTF_15_TRGAPH)) {
            int gaph = -1;
            try {
                // set (RTF 1.5) gaph to the average of the (RTF 1.6) left and right padding values
                final Integer leftPadStr = (Integer)attrib.getValue(ATTR_ROW_PADDING_LEFT);
                if (leftPadStr != null) {
                    gaph = leftPadStr;
                }
                final Integer rightPadStr = (Integer)attrib.getValue(ATTR_ROW_PADDING_RIGHT);
                if (rightPadStr != null) {
                    gaph = (gaph + rightPadStr) / 2;
                }
            } catch (Exception e) {
                final String msg = "RtfTableRow.writePaddingAttributes: " + e.toString();
//                getRtfFile().getLog().logWarning(msg);
            }
            if (gaph >= 0) {
                attrib.set(ATTR_RTF_15_TRGAPH, gaph);
            }
        }

        // write all padding attributes
        writeAttributes(attrib, ATTRIB_ROW_PADDING);
    }

    /**
     * @return true if the row is the first in the table
     */
    public boolean isFirstRow() {
        return (id == 1);
    }

    /**
     * @param cellId cell id to check
     * @return true if the cell is the highest cell
     */
    public boolean isHighestCell(int cellId) {
        return (highestCell == cellId) ? true : false;
    }

    /**
     *
     * @return Parent table of the row.
     */
    public RtfTable getTable() {
        RtfElement e = this;
        while (e.parent != null) {
            if (e.parent instanceof RtfTable) {
                return (RtfTable) e.parent;
            }

            e = e.parent;
        }

        return null;
    }
}
