/*********************************************************************
 *
 *      Copyright (C) 2006 Andrew Khan
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package org.xbib.datastructures.xslx.jxl.biff.drawing;

import java.io.FileInputStream;
import java.io.IOException;
import org.xbib.datastructures.xslx.jxl.common.Assert;
import org.xbib.datastructures.xslx.jxl.common.Logger;
import org.xbib.datastructures.xslx.jxl.write.biff.File;


/**
 * Contains the various biff records used to insert a drawing into a
 * worksheet.  This type of image does not have an associated object
 * record
 */
public class Drawing2 implements DrawingGroupObject {
    /**
     * The logger
     */
    private static final Logger logger = Logger.getLogger(Drawing.class);

    /**
     * The spContainer that was read in
     */
    private EscherContainer readSpContainer;

    /**
     * The MsoDrawingRecord associated with the drawing
     */
    private MsoDrawingRecord msoDrawingRecord;

    /**
     * Initialized flag
     */
    private boolean initialized = false;

    /**
     * The file containing the image
     */
    private java.io.File imageFile;

    /**
     * The raw image data, used instead of an image file
     */
    private byte[] imageData;

    /**
     * The object id, assigned by the drawing group
     */
    private int objectId;

    /**
     * The blip id
     */
    private int blipId;

    /**
     * The column position of the image
     */
    private double x;

    /**
     * The row position of the image
     */
    private double y;

    /**
     * The width of the image in cells
     */
    private double width;

    /**
     * The height of the image in cells
     */
    private double height;

    /**
     * The number of places this drawing is referenced
     */
    private int referenceCount;

    /**
     * The top level escher container
     */
    private EscherContainer escherData;

    /**
     * Where this image came from (read, written or a copy)
     */
    private Origin origin;

    /**
     * The drawing group for all the images
     */
    private DrawingGroup drawingGroup;

    /**
     * The drawing data
     */
    private DrawingData drawingData;

    /**
     * The type of this drawing object
     */
    private ShapeType type;

    /**
     * The shape id
     */
    private int shapeId;

    /**
     * The drawing position on the sheet
     */
    private int drawingNumber;


    /**
     * Constructor used when reading images
     *
     * @param mso the drawing record
     * @param dd  the drawing data for all drawings on this sheet
     * @param dg  the drawing group
     */
    public Drawing2(MsoDrawingRecord mso,
                    DrawingData dd,
                    DrawingGroup dg) {
        drawingGroup = dg;
        msoDrawingRecord = mso;
        drawingData = dd;
        initialized = false;
        origin = Origin.READ;
        // there is no drawing number associated with this drawing
        drawingData.addRawData(msoDrawingRecord.getData());
        drawingGroup.addDrawing(this);

        Assert.verify(mso != null);

        initialize();
    }

    /**
     * Copy constructor used to copy drawings from read to write
     *
     * @param dgo the drawing group object
     * @param dg  the drawing group
     */
    protected Drawing2(DrawingGroupObject dgo, DrawingGroup dg) {
        Drawing2 d = (Drawing2) dgo;
        Assert.verify(d.origin == Origin.READ);
        msoDrawingRecord = d.msoDrawingRecord;
        initialized = false;
        origin = Origin.READ;
        drawingData = d.drawingData;
        drawingGroup = dg;
        drawingNumber = d.drawingNumber;
        drawingGroup.addDrawing(this);
    }

    /**
     * Constructor invoked when writing the images
     *
     * @param x     the column
     * @param y     the row
     * @param w     the width in cells
     * @param h     the height in cells
     * @param image the image file
     */
    public Drawing2(double x,
                    double y,
                    double w,
                    double h,
                    java.io.File image) {
        imageFile = image;
        initialized = true;
        origin = Origin.WRITE;
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
        referenceCount = 1;
        type = ShapeType.PICTURE_FRAME;
    }

    /**
     * Constructor invoked when writing the images
     *
     * @param x     the column
     * @param y     the row
     * @param w     the width in cells
     * @param h     the height in cells
     * @param image the image data
     */
    public Drawing2(double x,
                    double y,
                    double w,
                    double h,
                    byte[] image) {
        imageData = image;
        initialized = true;
        origin = Origin.WRITE;
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
        referenceCount = 1;
        type = ShapeType.PICTURE_FRAME;
    }

    /**
     * Initializes the member variables from the Escher stream data
     */
    private void initialize() {
        initialized = true;
    }

    /**
     * Sets the object id.  Invoked by the drawing group when the object is
     * added to id
     *
     * @param objid the object id
     * @param bip   the blip id
     * @param sid   the shape id
     */
    public final void setObjectId(int objid, int bip, int sid) {
        objectId = objid;
        blipId = bip;
        shapeId = sid;

        if (origin == Origin.READ) {
            origin = Origin.READ_WRITE;
        }
    }

    /**
     * Accessor for the object id
     *
     * @return the object id
     */
    public final int getObjectId() {
        if (!initialized) {
            initialize();
        }

        return objectId;
    }

    /**
     * Accessor for the shape id
     *
     * @return the shape id
     */
    public int getShapeId() {
        if (!initialized) {
            initialize();
        }

        return shapeId;
    }

    /**
     * Accessor for the blip id
     *
     * @return the blip id
     */
    public final int getBlipId() {
        if (!initialized) {
            initialize();
        }

        return blipId;
    }

    /**
     * Gets the drawing record which was read in
     *
     * @return the drawing record
     */
    public MsoDrawingRecord getMsoDrawingRecord() {
        return msoDrawingRecord;
    }

    /**
     * Creates the main Sp container for the drawing
     *
     * @return the SP container
     */
    public EscherContainer getSpContainer() {
        if (!initialized) {
            initialize();
        }

        Assert.verify(origin == Origin.READ);

        return getReadSpContainer();
    }

    /**
     * Accessor for the drawing group
     *
     * @return the drawing group
     */
    public DrawingGroup getDrawingGroup() {
        return drawingGroup;
    }

    /**
     * Sets the drawing group for this drawing.  Called by the drawing group
     * when this drawing is added to it
     *
     * @param dg the drawing group
     */
    public void setDrawingGroup(DrawingGroup dg) {
        drawingGroup = dg;
    }

    /**
     * Gets the origin of this drawing
     *
     * @return where this drawing came from
     */
    public Origin getOrigin() {
        return origin;
    }

    /**
     * Accessor for the reference count on this drawing
     *
     * @return the reference count
     */
    public int getReferenceCount() {
        return referenceCount;
    }

    /**
     * Sets the new reference count on the drawing
     *
     * @param r the new reference count
     */
    public void setReferenceCount(int r) {
        referenceCount = r;
    }

    /**
     * Accessor for the column of this drawing
     *
     * @return the column
     */
    public double getX() {
        if (!initialized) {
            initialize();
        }
        return x;
    }

    /**
     * Sets the column position of this drawing
     *
     * @param x the column
     */
    public void setX(double x) {
        if (origin == Origin.READ) {
            if (!initialized) {
                initialize();
            }
            origin = Origin.READ_WRITE;
        }

        this.x = x;
    }

    /**
     * Accessor for the row of this drawing
     *
     * @return the row
     */
    public double getY() {
        if (!initialized) {
            initialize();
        }

        return y;
    }

    /**
     * Accessor for the row of the drawing
     *
     * @param y the row
     */
    public void setY(double y) {
        if (origin == Origin.READ) {
            if (!initialized) {
                initialize();
            }
            origin = Origin.READ_WRITE;
        }

        this.y = y;
    }


    /**
     * Accessor for the width of this drawing
     *
     * @return the number of columns spanned by this image
     */
    public double getWidth() {
        if (!initialized) {
            initialize();
        }

        return width;
    }

    /**
     * Accessor for the width
     *
     * @param w the number of columns to span
     */
    public void setWidth(double w) {
        if (origin == Origin.READ) {
            if (!initialized) {
                initialize();
            }
            origin = Origin.READ_WRITE;
        }

        width = w;
    }

    /**
     * Accessor for the height of this drawing
     *
     * @return the number of rows spanned by this image
     */
    public double getHeight() {
        if (!initialized) {
            initialize();
        }

        return height;
    }

    /**
     * Accessor for the height of this drawing
     *
     * @param h the number of rows spanned by this image
     */
    public void setHeight(double h) {
        if (origin == Origin.READ) {
            if (!initialized) {
                initialize();
            }
            origin = Origin.READ_WRITE;
        }

        height = h;
    }


    /**
     * Gets the SpContainer that was read in
     *
     * @return the read sp container
     */
    private EscherContainer getReadSpContainer() {
        if (!initialized) {
            initialize();
        }

        return readSpContainer;
    }

    /**
     * Accessor for the image data
     *
     * @return the image data
     */
    public byte[] getImageData() {
        Assert.verify(false);
        Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);

        if (!initialized) {
            initialize();
        }

        return drawingGroup.getImageData(blipId);
    }

    /**
     * Accessor for the image data
     *
     * @return the image data
     */
    public byte[] getImageBytes() throws IOException {
        Assert.verify(false);
        if (origin == Origin.READ || origin == Origin.READ_WRITE) {
            return getImageData();
        }

        Assert.verify(origin == Origin.WRITE);

        if (imageFile == null) {
            Assert.verify(imageData != null);
            return imageData;
        }

        byte[] data = new byte[(int) imageFile.length()];
        FileInputStream fis = new FileInputStream(imageFile);
        fis.read(data, 0, data.length);
        fis.close();
        return data;
    }

    /**
     * Accessor for the type
     *
     * @return the type
     */
    public ShapeType getType() {
        return type;
    }

    /**
     * Writes any other records associated with this drawing group object
     *
     * @param outputFile the output file
     * @throws IOException
     */
    public void writeAdditionalRecords(File outputFile) throws IOException {
        // no records to write
    }

    /**
     * Writes any records that need to be written after all the drawing group
     * objects have been written
     * Does nothing here
     *
     * @param outputFile the output file
     * @throws IOException
     */
    public void writeTailRecords(File outputFile) throws IOException {
        // does nothing
    }

    /**
     * Interface method
     *
     * @return the column number at which the image is positioned
     */
    public double getColumn() {
        return getX();
    }

    /**
     * Interface method
     *
     * @return the row number at which the image is positions
     */
    public double getRow() {
        return getY();
    }

    /**
     * Accessor for the first drawing on the sheet.  This is used when
     * copying unmodified sheets to indicate that this drawing contains
     * the first time Escher gubbins
     *
     * @return TRUE if this MSORecord is the first drawing on the sheet
     */
    public boolean isFirst() {
        return msoDrawingRecord.isFirst();
    }

    /**
     * Queries whether this object is a form object.  Form objects have their
     * drawings records spread over TXO and CONTINUE records and
     * require special handling
     *
     * @return TRUE if this is a form object, FALSE otherwise
     */
    public boolean isFormObject() {
        return false;
    }

    /**
     * Removes a row
     *
     * @param r the row to be removed
     */
    public void removeRow(int r) {
        if (y > r) {
            setY(r);
        }
    }

    /**
     * Accessor for the image file path.  Normally this is the absolute path
     * of a file on the directory system, but if this drawing was constructed
     * using an byte[] then the blip id is returned
     *
     * @return the image file path, or the blip id
     */
    public String getImageFilePath() {
        Assert.verify(false);
        return null;
    }
}



