/*
 * Copyright 2000-2004 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.tools.javazic;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * ZoneRec hold information of time zone corresponding to each text
 * line of the "Zone" part.
 *
 * @since 1.4
 */
class ZoneRec {
    private int gmtOffset;
    private String ruleName;
    private int directSave;
    private Rule ruleRef;
    private String format;
    private boolean hasUntil;
    private int untilYear;
    private Month untilMonth;
    private RuleDay untilDay;
    private Time untilTime;
    private long untilInMillis;
    private String line;

    /**
     * @return the "UNTIL" value in milliseconds
     */
    Time getUntilTime() {
	return untilTime;
    }

    /**
     * @return the GMT offset value in milliseconds
     */
    int getGmtOffset() {
	return gmtOffset;
    }

    /**
     * @return the rule name to which this zone record refers
     */
    String getRuleName() {
	return ruleName;
    }

    /**
     * @return the amount of saving time directly defined in the
     * "RULES/SAVE" field.
     */
    int getDirectSave() {
	return directSave;
    }

    /**
     * @return true if this zone record has a reference to a rule
     */
    boolean hasRuleReference() {
	return ruleRef != null;
    }

    /**
     * Returns the "FORMAT" field string of this zone record. This 
     * @return the "FORMAT" field
     */
    String getFormat() {
	return format;
    }

    /**
     * @return the year in the "UNTIL" field
     */
    int getUntilYear() {
	return untilYear;
    }

    /**
     * Returns the "UNTIL" field value in milliseconds from Janurary
     * 1, 1970 0:00 GMT.
     * @param currentSave the amount of daylight saving in
     * milliseconds that is used to adjust wall-clock time.
     * @return the milliseconds value of the "UNTIL" field
     */
    long getUntilTime(int currentSave) {
	if (untilTime.isWall()) {
	    return untilInMillis - currentSave;
	}
	return untilInMillis;
    }

    /**
     * Returns the "UNTIL" time in milliseconds without adjusting GMT
     * offsets or daylight saving.
     * @return local "UNTIL" time in milliseconds
     */
    long getLocalUntilTime() {
	return Time.getLocalTime(untilYear,
				 untilMonth,
				 untilDay,
				 untilTime.getTime());
    }

    /**
     * Returns the "UNTIL" time in milliseconds with adjusting GMT offsets and daylight saving.
     * @return the "UNTIL" time after the adjustment
     */
    long getLocalUntilTime(int save, int gmtOffset) {
	return Time.getLocalTime(untilYear,
				 untilMonth,
				 untilDay,
				 save,
				 gmtOffset,
				 untilTime);
    }

    /**
     * @return the text line of this zone record
     */
    String getLine() {
	return line;
    }

    /**
     * Sets the specified text line to this zone record
     */
    void setLine(String line) {
	this.line = line;
    }

    /**
     * @return true if this zone record has the "UNTIL" field
     */
    boolean hasUntil() {
	return this.hasUntil;
    }

    /**
     * Adjusts the "UNTIL" time to GMT offset if this zone record has
     * it.  <code>untilTime</code> is not adjusted to daylight saving
     * in this method.
     */
    void adjustTime() {
	if (!hasUntil()) {
	    return;
	}
	if (untilTime.isSTD() || untilTime.isWall()) {
	    // adjust to gmt offset only here.  adjust to real
	    // wall-clock time when tracking rules
	    untilInMillis -= gmtOffset;
	}
    }

    /**
     * @return the reference to the Rule object
     */
    Rule getRuleRef() {
	return ruleRef;
    }

    /**
     * Resolves the reference to a Rule and adjusts its "UNTIL" time
     * to GMT offset.
     */
    void resolve(Zoneinfo zi) {
	if (ruleName != null && (!"-".equals(ruleName))) {
		ruleRef = zi.getRule(ruleName);
	}
	adjustTime();
    }

    /**
     * Parses a Zone text line that is described by a StringTokenizer.
     * @param tokens represents tokens of a Zone text line
     * @return the zone record produced by parsing the text
     */
    static ZoneRec parse(StringTokenizer tokens) {
	ZoneRec rec = new ZoneRec();
	try {
	    rec.gmtOffset = (int) Time.parse(tokens.nextToken()).getTime();
	    String token = tokens.nextToken();
	    char c = token.charAt(0);
	    if (c >= '0' && c <= '9') {
		rec.directSave = (int) Time.parse(token).getTime();
	    } else {
		rec.ruleName = token;
	    }
	    rec.format = tokens.nextToken();
	    if (tokens.hasMoreTokens()) {
		rec.hasUntil = true;
		rec.untilYear = Integer.parseInt(tokens.nextToken());
		if (tokens.hasMoreTokens()) {
		    rec.untilMonth = Month.parse(tokens.nextToken());
		} else {
		    rec.untilMonth = Month.JANUARY;
		}
		if (tokens.hasMoreTokens()) {
		    rec.untilDay = RuleDay.parse(tokens.nextToken());
		} else {
		    rec.untilDay = new RuleDay(1);
		}
		if (tokens.hasMoreTokens()) {
		    rec.untilTime = Time.parse(tokens.nextToken());
		} else {
		    rec.untilTime = Time.parse("0:00");
		}
		rec.untilInMillis = rec.getLocalUntilTime();
	    }
	} catch (Exception e) {
	    // TODO: error reporting
	    e.printStackTrace();
	}
	return rec;
    }

    private static void panic(String msg) {
	Main.panic(msg);
    }
}
