/*******************************************************************************
 * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     Oracle - initial API and implementation from Oracle TopLink
 ******************************************************************************/  
package org.eclipse.persistence.internal.platform.database.oracle;

import java.io.StringWriter;
import java.sql.*;
import java.util.*;
import oracle.sql.*;
import org.eclipse.persistence.internal.helper.Helper;

/**
 * Used as a helper class for TIMESTAMP, TIMESTAMPTZ and TIMESTAMPLTZ in oracle9.
 */
public class TIMESTAMPHelper {

    /**
     * This conversion required the use of the literal string to get the same
     * functionality as the native SQL to_timestamp() approach.
     */
    public static TIMESTAMPTZ buildTIMESTAMPTZ(Calendar cal, Connection con, boolean shouldPrintCalendar) throws SQLException {
        //Bug5614674.  It used to be a driver bug and Helper.printCalendar(cal, false) was used to make it work.  It has been fixed in 11.  Separate the newer version from the old ones.
        if (shouldPrintCalendar) {
            return new TIMESTAMPTZ(con, Helper.printCalendar(cal, false), cal);
        } else {
            return new TIMESTAMPTZ(con, new Timestamp(cal.getTimeInMillis()), cal);
        }
    }

    /**
     * Build a calendar from TIMESTAMPTZWrapper. 
     */
    public static Calendar buildCalendar(TIMESTAMPTZWrapper timestampTZ) throws SQLException{
        Timestamp ts = timestampTZ.getTimestamp();
        TimeZone tz = timestampTZ.getTimeZone();

        Calendar gCal;
        if(timestampTZ.isTimestampInGmt()) {
            gCal = Calendar.getInstance(tz);
            gCal.setTime(ts);
        } else {
            gCal = Calendar.getInstance();
            gCal.setTime(ts);
            gCal.getTimeZone().setID(tz.getID());
            gCal.getTimeZone().setRawOffset(tz.getRawOffset());
        }
        return gCal;
    }

   /**
    * Build a calendar from TIMESTAMPLTZWrapper. 
    */
    public static Calendar buildCalendar(TIMESTAMPLTZWrapper timestampLTZ) throws SQLException{
        Calendar gCal;
        if (timestampLTZ.getZoneId() != null) {
            gCal = Calendar.getInstance(TimeZone.getTimeZone(timestampLTZ.getZoneId()));
        } else {
            gCal = Calendar.getInstance();
        }

        //This is the only way to set time in Calendar.  Passing Timestamp directly to the new 
        //calendar does not work because the GMT time is wrong.
        if(timestampLTZ.isLtzTimestampInGmt()) {
            gCal.setTimeInMillis(timestampLTZ.getTimestamp().getTime());
        } else {
            Calendar localCalendar = Helper.allocateCalendar();
            localCalendar.setTime(timestampLTZ.getTimestamp());
            gCal.set(localCalendar.get(Calendar.YEAR), localCalendar.get(Calendar.MONTH), localCalendar.get(Calendar.DATE), localCalendar.get(Calendar.HOUR_OF_DAY), localCalendar.get(Calendar.MINUTE), localCalendar.get(Calendar.SECOND));
            Helper.releaseCalendar(localCalendar);
        }
        gCal.set(Calendar.MILLISECOND, timestampLTZ.getTimestamp().getNanos() / 1000000);

        return gCal;
    }


    /**
     * Build a calendar string based on the calendar fields.
     * If the daylight savings time should be printed and the zone is in daylight savings time, 
     * print the short representation of daylight savings from the calendar's timezone data. 
     */
    public static String printCalendar(Calendar calendar) {
        if (calendar == null) {
            return "null";
        }
        StringWriter writer = new StringWriter();
        writer.write(Helper.printCalendar(calendar, false));
        writer.write(" ");
        writer.write(calendar.getTimeZone().getID());
        // If we should print daylight savings and the zone is reported to be using daylight time, 
        // write the short representation of the daylight time in the writer.
        if (shouldAppendDaylightTime(calendar)) {
            writer.write(" ");
            writer.write(calendar.getTimeZone().getDisplayName(true, TimeZone.SHORT));
        }
        return writer.toString();
    }
    
    /**
     * Return true if the calendar supports and is in daylight time
     * (according to its timezone), false otherwise
     */
    public static boolean shouldAppendDaylightTime(Calendar calendar) {
        if (calendar == null) {
            return false;
        }
        TimeZone zone = calendar.getTimeZone();
        return zone.useDaylightTime() && zone.inDaylightTime(calendar.getTime());
    }

    /**
     * Extract TimeZone from TIMESTAMPTZ.
     */
    public static TimeZone extractTimeZone(byte[] bytes) {
        String regionName = null;
        if ((bytes[11] & -128) != 0) {
            int regionCode = (bytes[11] & 127) << 6;
            regionCode += ((bytes[12] & 252) >> 2);
            regionName = new String(ZONEIDMAP.getRegion(regionCode));
        } else {
            int hourOffset = bytes[11] - 20;
            int minuteOffset = bytes[12] - 60;
            String offset = Helper.buildZeroPrefix(hourOffset, 2) + ":" + Helper.buildZeroPrefixWithoutSign(minuteOffset, 2);
            regionName = "GMT" + offset;
        }
        return TimeZone.getTimeZone(regionName);
    }
}
