/*
 * 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.xpath.types;

import javax.xml.transform.TransformerException;

import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.regex.Matcher;
import org.apache.xpath.regex.Pattern;

import xml.xpath31.processor.types.XSAnyAtomicType;
import xml.xpath31.processor.types.XSDate;
import xml.xpath31.processor.types.XSDateTime;

/**
 * Implementation of XML Schema data type xs:gYear.
 * 
 * @author : Mukul Gandhi <mukulg@apache.org>
 * 
 * @xsl.usage general
 */
public class XSGYear extends XSAnyAtomicType {
	
	private static final long serialVersionUID = 4500652479945465195L;

	/**
	 * Class fields to represent components of XML Schema type xs:gYear.  
	 */
	
    private int m_year;
	
	private boolean m_isNegative;
	
	private boolean m_isTimeZoneNegative;
	
	private boolean m_isTimeZoneUtc;
	
	private int m_timezone_hrs;
	
	private int m_timezone_min;
	
	private boolean m_isTimeZoned;
	
	private String m_gYearStrValue;
	
	private static final String XS_GYEAR = "xs:gYear";
	
	/**
	 * Default constructor.
	 */
	public XSGYear() {
		// no op
	}
	
	/**
	 * Class constructor.
	 */
	public XSGYear(String gYearStrValue) throws TransformerException {
		
		boolean isXsDate = true;
		
		try {
		   // Constructing xs:gYear value, from supplied xs:date lexical value
		   XSDate xsDate = XSDate.parseDate(gYearStrValue);
		   String timeZoneStr = DateTimeUtil.getTimeZoneStrFromXsDateValue(xsDate);
		   
		   int year = xsDate.year();
		   String str1 = String.valueOf(year);
		   
		   if (timeZoneStr != null) {
			  str1 += timeZoneStr; 
		   }
		   
		   parse(str1);
		   
		   m_gYearStrValue = str1;
		}
		catch (TransformerException ex) {
			isXsDate = false;
		}
		
		if (!isXsDate) {
		   try {
			  // Constructing xs:gYear value, from supplied xs:dateTime lexical value 
		      XSDateTime xsDateTime = XSDateTime.parseDateTime(gYearStrValue);		      
		      String timeZoneStr = DateTimeUtil.getTimeZoneStrFromXsDateTimeValue(xsDateTime);
		      
		      int year = xsDateTime.year();
		      String str1 = String.valueOf(year);
		      
		      if (timeZoneStr != null) {
		    	 str1 += timeZoneStr;   
		      }
			   
			  parse(str1);
			   
			  m_gYearStrValue = str1;
		   }
		   catch (TransformerException ex) {
		      // no op	   
		   }
		}
		
		if (m_gYearStrValue == null) {
		   // Constructing xs:gYear value, from supplied xs:gYear lexical value	
		   parse(gYearStrValue);
		   
		   m_gYearStrValue = gYearStrValue;
		}
	}

	@Override
	public String stringType() {
		return XS_GYEAR;
	}
	
	public ResultSequence constructor(ResultSequence seq) {
		ResultSequence result = new ResultSequence();
		
		XObject xObj = seq.item(0);
		String strVal = XslTransformEvaluationHelper.getStrVal(xObj);
		try {
			XSGYear xsGYear = new XSGYear(strVal);
			result.add(xsGYear);
		} catch (TransformerException ex) {
			// no op
		}
		
		return result;
	}

	@Override
	public String stringValue() {
		return m_gYearStrValue;
	}
	
	public int getType() {
		return CLASS_GYEAR;
	}
	
	/**
	 * Implementation of operation equals, for the type xs:gYear.
	 */
	public boolean eq(XSGYear obj2) {
		boolean result = false;

		result = ((m_year == obj2.getYear()) && (m_isNegative == obj2.isNegative()) && 
				  (m_isTimeZoneNegative == obj2.isTimeZoneNegative()) && (m_isTimeZoneUtc == obj2.isTimeZoneUtc()) && 
				  (m_timezone_hrs == obj2.getTimezoneHrs()) && (m_timezone_min == obj2.getTimezoneMin()) && 
				  (m_isTimeZoned == obj2.isTimeZoned()));

		return result;
	}
	
	/**
	 * Implementation of operation not equals, for the type xs:gYear.
	 */
	public boolean ne(XSGYear obj2) {
        boolean result = false;
		
		result = !eq(obj2);
		
		return result;
	}

	@Override
	public String typeName() {
		return "gYear";
	}
	
	/**
	 * Method definition to parse a string value to an XML Schema type 
	 * xs:gYear's component parts. 
	 * 
	 * @param gYearMonthStrValue						A string value that needs to be parsed 
	 *                                                  to a xs:gYear value. 
	 * @throws TransformerException
	 */
	private void parse(String gYearStrValue) throws TransformerException {		
		try {
			int strLength = gYearStrValue.length();			
			if (strLength > 0) {
				if (gYearStrValue.charAt(0) == '-') {
					m_isNegative = true;
					String suffixValue = gYearStrValue.substring(1);
					if (suffixValue.endsWith("Z")) {
						m_isTimeZoneUtc = true;						
						int zIdx = suffixValue.indexOf('Z');
						String str2 = suffixValue.substring(0, zIdx);
						m_year = Integer.valueOf(str2); 
					}
					else {
						int plusIdx = suffixValue.indexOf('+');
						if (plusIdx > -1) {
							String str2 = suffixValue.substring(0, plusIdx);
							m_year = Integer.valueOf(str2);
							String timeZoneStr = suffixValue.substring(plusIdx + 1);
							if (isTimeZoneStrCorrectlyFormatted(timeZoneStr)) {
								String[] timeZoneStrParts = timeZoneStr.split(":");
								m_timezone_hrs = Integer.valueOf(timeZoneStrParts[0]); 
								m_timezone_min = Integer.valueOf(timeZoneStrParts[1]);
								if ((m_timezone_hrs == 0) && (m_timezone_min == 0)) {
									m_isTimeZoneUtc = true;  
								}
								m_isTimeZoned = true;
							}
							else {
								throw new TransformerException("FORG0001 : A string value " + gYearStrValue + " cannot be parsed to a value of type xs:gYear.");
							}
						}
						else {
							String[] strParts = suffixValue.split("-");
							int strArrLength = strParts.length;
							if (strArrLength == 1) {
								m_year = Integer.valueOf(strParts[0]);
							}
							else if (strArrLength == 2) {
								m_year = Integer.valueOf(strParts[0]);
								if (isTimeZoneStrCorrectlyFormatted(strParts[1])) {
									String[] timeZoneStrParts = (strParts[1]).split(":");
									m_timezone_hrs = Integer.valueOf(timeZoneStrParts[0]); 
									m_timezone_min = Integer.valueOf(timeZoneStrParts[1]);
									if ((m_timezone_hrs == 0) && (m_timezone_min == 0)) {
										m_isTimeZoneUtc = true;  
									}
									m_isTimeZoned = true;
									m_isTimeZoneNegative = true;
								}
								else {
									throw new TransformerException("FORG0001 : A string value " + gYearStrValue + " cannot be parsed to a value of type xs:gYear.");
								}
							}
						}
					}
				}
				else if (gYearStrValue.endsWith("Z")) {
					m_isTimeZoneUtc = true;						
					int zIdx = gYearStrValue.indexOf('Z');
					String str2 = gYearStrValue.substring(0, zIdx);
					m_year = Integer.valueOf(str2);
					m_isTimeZoned = true;
				}
				else {
					int plusIdx = gYearStrValue.indexOf('+');
					if (plusIdx > -1) {
						String str2 = gYearStrValue.substring(0, plusIdx);
						m_year = Integer.valueOf(str2);
						String timeZoneStr = gYearStrValue.substring(plusIdx + 1);
						String[] timeZoneStrParts = timeZoneStr.split(":");
						m_timezone_hrs = Integer.valueOf(timeZoneStrParts[0]); 
						m_timezone_min = Integer.valueOf(timeZoneStrParts[1]);
						if ((m_timezone_hrs == 0) && (m_timezone_min == 0)) {
							m_isTimeZoneUtc = true;  
						}
						m_isTimeZoned = true;
					}
					else {
						String[] strParts = gYearStrValue.split("-"); 
						int strArrLength = strParts.length;
						if (strArrLength == 1) {
							m_year = Integer.valueOf(strParts[0]);
						}
						else if (strArrLength == 2) {
							m_year = Integer.valueOf(strParts[0]);
							String[] timeZoneStrParts = (strParts[1]).split(":");
							m_timezone_hrs = Integer.valueOf(timeZoneStrParts[0]); 
							m_timezone_min = Integer.valueOf(timeZoneStrParts[1]);
							if ((m_timezone_hrs == 0) && (m_timezone_min == 0)) {
								m_isTimeZoneUtc = true;  
							}
							m_isTimeZoned = true;
							m_isTimeZoneNegative = true;
						}
					}
				}
			}
			else {
				throw new TransformerException("FORG0001 : A zero length string was supplied to construct a value of type xs:gYear.");	
			}
	    }
	    catch (Exception ex) {
	    	throw new TransformerException("FORG0001 : A string value " + gYearStrValue + " cannot be parsed to a value of type xs:gYear.");	
	    }
	}

	/**
	 * Getter method definitions for various components of an XML Schema type xs:gYear. 
	 */
	
	public int getYear() {
		return m_year;
	}
	
	public boolean isNegative() {
		return m_isNegative;
	}

	public boolean isTimeZoneNegative() {
		return m_isTimeZoneNegative;
	}

	public boolean isTimeZoneUtc() {
		return m_isTimeZoneUtc;
	}

	public int getTimezoneHrs() {
		return m_timezone_hrs;
	}

	public int getTimezoneMin() {
		return m_timezone_min;
	}

	public boolean isTimeZoned() {
		return m_isTimeZoned;
	}
	
	/**
	 * Method definition to check the string format of a timezone string.
	 * 
	 * (The regex for timezone's string value is specified within XML Schema 
	 *  datatypes specification)
	 * 
	 * @param strValue					The supplied string value of timezone
	 * @return							Boolean true or false
	 */
	private boolean isTimeZoneStrCorrectlyFormatted(String strValue) {
		boolean result = false;
		
		Pattern pattern = Pattern.compile("((0[0-9]|1[0-3]):[0-5][0-9]|14:00)");
		Matcher matcher = pattern.matcher(strValue);
		result = matcher.matches();  
		
		return result;
	}

}
