/****************************************************************
 * 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.                                           *
 ****************************************************************/


/**
 * RFC2822 date parser.
 *
 * Created 9/28/2004
 * by Joe Cheng <code@joecheng.com>
 */

options {
	STATIC=false;
	LOOKAHEAD=1;
	JDK_VERSION = "1.5";
	OUTPUT_DIRECTORY = "../../../../../../../../../target/generated-sources/javacc";
	//DEBUG_PARSER=true;
	//DEBUG_TOKEN_MANAGER=true;
}

PARSER_BEGIN(DateTimeParser)
/****************************************************************
 * 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.                                           *
 ****************************************************************/
package org.apache.james.mime4j.field.datetime.parser;

import org.apache.james.mime4j.dom.datetime.DateTime;

public class DateTimeParser {
    private static final boolean ignoreMilitaryZoneOffset = true;

    public static void main(String args[]) throws ParseException {
		while (true) {
		    try {
				DateTimeParser parser = new DateTimeParser(System.in);
		    	parser.parseLine();
		    } catch (Exception x) {
				x.printStackTrace();
				return;
		    }
		}
    }

    public DateTime parseAll() throws ParseException {
		try {
			return doParseAll();
		} catch (TokenMgrError e) {
			throw new ParseException(e);
		}
    }

    public DateTime parseLine() throws ParseException {
		try {
			return doParseLine();
		} catch (TokenMgrError e) {
			throw new ParseException(e);
		}
    }

    private static int parseDigits(Token token) {
        return Integer.parseInt(token.image, 10);
    }

    private static int getMilitaryZoneOffset(char c) {
        if (ignoreMilitaryZoneOffset)
            return 0;

        c = Character.toUpperCase(c);

        switch (c) {
            case 'A': return 1;
            case 'B': return 2;
            case 'C': return 3;
            case 'D': return 4;
            case 'E': return 5;
            case 'F': return 6;
            case 'G': return 7;
            case 'H': return 8;
            case 'I': return 9;
            case 'K': return 10;
            case 'L': return 11;
            case 'M': return 12;

            case 'N': return -1;
            case 'O': return -2;
            case 'P': return -3;
            case 'Q': return -4;
            case 'R': return -5;
            case 'S': return -6;
            case 'T': return -7;
            case 'U': return -8;
            case 'V': return -9;
            case 'W': return -10;
            case 'X': return -11;
            case 'Y': return -12;

            case 'Z': return 0;
            default: return 0;
        }
    }

    private static class Time {
        private int hour;
        private int minute;
        private int second;
        private int zone;

        public Time(int hour, int minute, int second, int zone) {
            this.hour = hour;
            this.minute = minute;
            this.second = second;
            this.zone = zone;
        }

        public int getHour() { return hour; }
        public int getMinute() { return minute; }
        public int getSecond() { return second; }
        public int getZone() { return zone; }
    }

    private static class Date {
        private String year;
        private int month;
        private int day;

        public Date(String year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }

        public String getYear() { return year; }
        public int getMonth() { return month; }
        public int getDay() { return day; }
    }
}

PARSER_END(DateTimeParser)

private DateTime doParseLine() :
{DateTime dt;}
{
	dt=date_time() ["\r"] "\n"
	{ return dt; }
}

private DateTime doParseAll() :
{DateTime dt;}
{
	dt=date_time() <EOF>
	{ return dt; }
}

DateTime date_time() :
{Date d; Time t;}
{
	[ day_of_week() "," ]
	d=date()
	t=time()
	{
	    return new DateTime(
	            d.getYear(),
	            d.getMonth(),
	            d.getDay(),
	            t.getHour(),
	            t.getMinute(),
	            t.getSecond(),
	            t.getZone());    // time zone offset
	}
}

String day_of_week() :
{}
{
(    <MON> | <TUE> | <WED> | <THU> | <FRI> | <SAT> | <SUN>
)
    { return token.image; }
}

Date date() :
{int d, m; String y;}
{
    d=day() m=month() y=year()
    { return new Date(y, m, d); }
}

int day() :
{Token t;}
{
    t=<DIGITS> { return parseDigits(t); }
}

int month() :
{}
{
    <JAN> { return 1; }
|   <FEB> { return 2; }
|   <MAR> { return 3; }
|   <APR> { return 4; }
|   <MAY> { return 5; }
|   <JUN> { return 6; }
|   <JUL> { return 7; }
|   <AUG> { return 8; }
|   <SEP> { return 9; }
|   <OCT> { return 10; }
|   <NOV> { return 11; }
|   <DEC> { return 12; }
}

String year() :
{Token t;}
{
    t=<DIGITS> 
    {
      String year = (String) t.image;
      if (year.length() == 2) 
      {
        if (year.charAt(0) > '6')
        {
          return "19" + year;
        }
        return "20" + year;
      }
      return year;
    }
}

Time time() :
{int h, m, s=0, z;}
{
    h=hour() ":" m=minute() [ ":" s=second() ] z=zone()
    { return new Time(h, m, s, z); }
}

int hour() :
{Token t;}
{
    t=<DIGITS> { return parseDigits(t); }
}

int minute() :
{Token t;}
{
    t=<DIGITS> { return parseDigits(t); }
}

int second() :
{Token t;}
{
    t=<DIGITS> { return parseDigits(t); }
}

int zone() :
{ Token t, u; int z; }
{
(    t=< OFFSETDIR: ["+", "-"] > u=<DIGITS> { z=parseDigits(u)*(t.image.equals("-") ? -1 : 1); }
|   z=obs_zone()
)
    { return z; }
}

int obs_zone() :
{Token t; int z;}
{
(   "UT"  { z=0; }
|   "GMT" { z=0; }
|   "EST" { z=-5; }
|   "EDT" { z=-4; }
|   "CST" { z=-6; }
|   "CDT" { z=-5; }
|   "MST" { z=-7; }
|   "MDT" { z=-6; }
|   "PST" { z=-8; }
|   "PDT" { z=-7; }
|   t=< MILITARY_ZONE: ["A"-"I","a"-"i","K"-"Z","k"-"z"] > { z=getMilitaryZoneOffset(t.image.charAt(0)); }
)
    { return z * 100; }
}

SPECIAL_TOKEN :
{
 	< WS: ( [" ", "\t"] )+ >
}

TOKEN_MGR_DECLS :
{
	// Keeps track of how many levels of comment nesting
	// we've encountered.  This is only used when the 2nd
	// level is reached, for example ((this)), not (this).
	// This is because the outermost level must be treated
	// specially anyway, because the outermost ")" has a
	// different token type than inner ")" instances.
	static int commentNest;
}

MORE :
{
	// starts a comment
	"(" : INCOMMENT
}

<INCOMMENT>
SKIP :
{
	// ends a comment
	< COMMENT: ")" > : DEFAULT
	// if this is ever changed to not be a SKIP, need
	// to make sure matchedToken.token = token.toString()
	// is called.
}

<INCOMMENT>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	"(" { commentNest = 1; } : NESTED_COMMENT
|	< <ANY>>
}

<NESTED_COMMENT>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	"(" { ++commentNest; }
|	")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }
|	< <ANY>>
}

TOKEN :
{
    < DIGITS: ( ["0"-"9"] )+ >
}

TOKEN [IGNORE_CASE] :
{
    < JAN: "Jan" >
|   < FEB: "Feb" >
|   < MAR: "Mar" >
|   < APR: "Apr" >
|   < MAY: "May" >
|   < JUN: "Jun" >
|   < JUL: "Jul" >
|   < AUG: "Aug" >
|   < SEP: "Sep" >
|   < OCT: "Oct" >
|   < NOV: "Nov" >
|   < DEC: "Dec" >
}

TOKEN [IGNORE_CASE] :
{
    < MON: "Mon" >
|   < TUE: "Tue" >
|   < WED: "Wed" >
|   < THU: "Thu" >
|   < FRI: "Fri" >
|   < SAT: "Sat" >
|   < SUN: "Sun" >
}

// GLOBALS

<*>
TOKEN :
{
	< #QUOTEDPAIR: "\\" <ANY> >
|	< #ANY: ~[] >
}
