/*********************************************************************
 *
 *      Copyright (C) 2002 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.read.biff;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.xbib.datastructures.xslx.jxl.CellFeatures;
import org.xbib.datastructures.xslx.jxl.CellType;
import org.xbib.datastructures.xslx.jxl.DateCell;
import org.xbib.datastructures.xslx.jxl.NumberCell;
import org.xbib.datastructures.xslx.jxl.biff.FormattingRecords;
import org.xbib.datastructures.xslx.jxl.common.Assert;
import org.xbib.datastructures.xslx.jxl.common.Logger;
import org.xbib.datastructures.xslx.jxl.format.CellFormat;

/**
 * A date which is stored in the cell
 */
class DateRecord implements DateCell, CellFeaturesAccessor {
    // The default formats used when returning the date as a string
    private static final SimpleDateFormat dateFormat =
            new SimpleDateFormat("dd MMM yyyy");
    private static final SimpleDateFormat timeFormat =
            new SimpleDateFormat("HH:mm:ss");
    // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks
    // the day before this was 29th Feb 1900, but it was 28th Feb 1900.
    // I guess the programmers thought nobody would notice that they
    // couldn't be bothered to program this dating anomaly properly
    private static final int nonLeapDay = 61;
    private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT");
    // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives
    // the UTC offset
    private static final int utcOffsetDays = 25569;
    // The number of days between 01 Jan 1904 and 01 Jan 1970 - this gives
    // the UTC offset using the 1904 date system
    private static final int utcOffsetDays1904 = 24107;
    // The number of milliseconds in  a day
    private static final long secondsInADay = 24 * 60 * 60;
    private static final long msInASecond = 1000;
    private static final long msInADay = secondsInADay * msInASecond;
    /**
     * The logger
     */
    private static final Logger logger = Logger.getLogger(DateRecord.class);
    /**
     * The date represented within this cell
     */
    private final Date date;
    /**
     * The row number of this cell record
     */
    private final int row;
    /**
     * The column number of this cell record
     */
    private final int column;
    /**
     * Indicates whether this is a full date, or merely a time
     */
    private final boolean time;
    /**
     * The format to use when displaying this cell's contents as a string
     */
    private DateFormat format;
    /**
     * The raw cell format
     */
    private CellFormat cellFormat;
    /**
     * The index to the XF Record
     */
    private final int xfIndex;
    /**
     * A handle to the formatting records
     */
    private final FormattingRecords formattingRecords;
    /**
     * A handle to the sheet
     */
    private final SheetImpl sheet;
    /**
     * The cell features
     */
    private CellFeatures features;
    /**
     * A flag to indicate whether this objects formatting things have
     * been initialized
     */
    private boolean initialized;


    /**
     * Constructs this object from the raw data
     *
     * @param num the numerical representation of this
     * @param xfi the java equivalent of the excel date format
     * @param fr  the formatting records
     * @param nf  flag indicating whether we are using the 1904 date system
     * @param si  the sheet
     */
    public DateRecord(NumberCell num,
                      int xfi, FormattingRecords fr,
                      boolean nf, SheetImpl si) {
        row = num.getRow();
        column = num.getColumn();
        xfIndex = xfi;
        formattingRecords = fr;
        sheet = si;
        initialized = false;

        format = formattingRecords.getDateFormat(xfIndex);

        // This value represents the number of days since 01 Jan 1900
        double numValue = num.getValue();

        if (Math.abs(numValue) < 1) {
            if (format == null) {
                format = timeFormat;
            }
            time = true;
        } else {
            if (format == null) {
                format = dateFormat;
            }
            time = false;
        }

        // Work round a bug in excel.  Excel seems to think there is a date
        // called the 29th Feb, 1900 - but in actual fact this was not a leap year.
        // Therefore for values less than 61 in the 1900 date system,
        // add one to the numeric value
        if (!nf && !time && numValue < nonLeapDay) {
            numValue += 1;
        }

        // Get rid of any timezone adjustments - we are not interested
        // in automatic adjustments
        format.setTimeZone(gmtZone);

        // Convert this to the number of days since 01 Jan 1970
        int offsetDays = nf ? utcOffsetDays1904 : utcOffsetDays;
        double utcDays = numValue - offsetDays;

        // Convert this into utc by multiplying by the number of milliseconds
        // in a day.  Use the round function prior to ms conversion due
        // to a rounding feature of Excel (contributed by Jurgen
        long utcValue = Math.round(utcDays * secondsInADay) * msInASecond;

        date = new Date(utcValue);
    }

    /**
     * Interface method which returns the row number of this cell
     *
     * @return the zero base row number
     */
    public final int getRow() {
        return row;
    }

    /**
     * Interface method which returns the column number of this cell
     *
     * @return the zero based column number
     */
    public final int getColumn() {
        return column;
    }

    /**
     * Gets the date
     *
     * @return the date
     */
    public Date getDate() {
        return date;
    }

    /**
     * Gets the cell contents as a string.  This method will use the java
     * equivalent of the excel formatting string
     *
     * @return the label
     */
    public String getContents() {
        return format.format(date);
    }

    /**
     * Accessor for the cell type
     *
     * @return the cell type
     */
    public CellType getType() {
        return CellType.DATE;
    }

    /**
     * Indicates whether the date value contained in this cell refers to a date,
     * or merely a time
     *
     * @return TRUE if the value refers to a time
     */
    public boolean isTime() {
        return time;
    }

    /**
     * Gets the DateFormat used to format the cell.  This will normally be
     * the format specified in the excel spreadsheet, but in the event of any
     * difficulty parsing this, it will revert to the default date/time format.
     *
     * @return the DateFormat object used to format the date in the original
     * excel cell
     */
    public DateFormat getDateFormat() {
        Assert.verify(format != null);

        return format;
    }

    /**
     * Gets the CellFormat object for this cell.  Used by the WritableWorkbook
     * API
     *
     * @return the CellFormat used for this cell
     */
    public CellFormat getCellFormat() {
        if (!initialized) {
            cellFormat = formattingRecords.getXFRecord(xfIndex);
            initialized = true;
        }

        return cellFormat;
    }

    /**
     * Determines whether or not this cell has been hidden
     *
     * @return TRUE if this cell has been hidden, FALSE otherwise
     */
    public boolean isHidden() {
        ColumnInfoRecord cir = sheet.getColumnInfo(column);

        if (cir != null && cir.getWidth() == 0) {
            return true;
        }

        RowRecord rr = sheet.getRowInfo(row);

        return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed());
    }

    /**
     * Accessor for the sheet
     *
     * @return the containing sheet
     */
    protected final SheetImpl getSheet() {
        return sheet;
    }

    /**
     * Accessor for the cell features
     *
     * @return the cell features or NULL if this cell doesn't have any
     */
    public CellFeatures getCellFeatures() {
        return features;
    }

    /**
     * Sets the cell features
     *
     * @param cf the cell features
     */
    public void setCellFeatures(CellFeatures cf) {
        features = cf;
    }

}





