package com.nifty.cloud.ncs.service.parser;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.nifty.cloud.ncs.NiftyCloudResponse;
import com.nifty.cloud.ncs.http.HttpResponse;
import com.nifty.cloud.ncs.service.NcsObjectContents;
import com.nifty.cloud.ncs.service.ObjectListing;
import com.nifty.cloud.ncs.service.Owner;
import com.nifty.cloud.ncs.util.DateUtils;

/**
 * ListObjectsのXML応答XML解析用クラスです。
 *
 */
public class ListObjectsParser extends AbstractStorageResponseParser<ObjectListing>
{
	public ListObjectsParser()
	{
		super();
	}

	@Override
	public NiftyCloudResponse<ObjectListing> parse(HttpResponse response) throws Exception
	{
		NiftyCloudResponse<ObjectListing> ncsResponse = super.parseMetaData(response);
		ListObjectsUnmarshaller unmarshaller = new ListObjectsUnmarshaller();
		parseXml(unmarshaller, response.getContent());
		ObjectListing objectListing = unmarshaller.getObjectListing();

		ncsResponse.setResult(objectListing);

		return (ncsResponse);
	}

    /**
     * Unmarshaller for the ListObjects XML response.
     */
	private class ListObjectsUnmarshaller extends DefaultHandler
	{
		private NcsObjectContents ncsObjectContents = null;
		private Owner owner = null;
		private ObjectListing objectListing = new ObjectListing();
		private StringBuffer text = null;
		private boolean withCommonPrefixes = false;
		private List<String> commonPrefixes = new ArrayList<String>();

		private String bucketName = null;
		private String requestPrefix = null;
		private String requestMarker = null;
		private int requestMaxKeys = 0;
		private String requestDelimiter = null;
		private boolean isTruncated = false;
		private String nextMarker = null;

		public ListObjectsUnmarshaller()
		{
			super();
			text = new StringBuffer();
		}

		public ObjectListing getObjectListing()
		{
			objectListing.setBucketName(bucketName);
			objectListing.setCommonPrefixes(commonPrefixes);
			objectListing.setDelimiter(requestDelimiter);
			objectListing.setMarker(requestMarker);
			objectListing.setMaxKeys(requestMaxKeys);
			objectListing.setPrefix(requestPrefix);
			objectListing.setTruncated(isTruncated);

			if (nextMarker != null) {
				objectListing.setNextMarker(nextMarker);
			} else if (isTruncated == true) {
				String nextMarker = objectListing.getObjectList().get(objectListing.getObjectList().size() - 1).getName();
				objectListing.setNextMarker(nextMarker);
			}

			return (objectListing);
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
		 */
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException
		{
			text.append(ch, start, length);
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#startDocument()
		 */
		@Override
		public void startDocument() throws SAXException
		{
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#endDocument()
		 */
		@Override
		public void endDocument() throws SAXException
		{
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
		 */
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
		{
			if (localName.equals("Contents")) {
				ncsObjectContents = new NcsObjectContents();
				ncsObjectContents.setBucketName(bucketName);
			} else if (localName.equals("Owner")) {
				owner = new Owner();
				ncsObjectContents.setOwner(owner);
			} else if (localName.equals("CommonPrefixes")) {
				withCommonPrefixes = true;
			}
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
		 */
		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException
		{
			String element = text.toString();

			if (localName.equals("Name")) {
				bucketName = element;
				if (log.isDebugEnabled()) {
					log.debug("Examining listing for bucket: " + bucketName);
				}
			} else if (withCommonPrefixes == false && localName.equals("Prefix")) {
				requestPrefix = checkForEmptyString(element);
			} else if (localName.equals("Marker")) {
				requestMarker = checkForEmptyString(element);
			} else if (localName.equals("NextMarker")) {
				nextMarker = element;
			} else if (localName.equals("MaxKeys")) {
				requestMaxKeys = parseInt(element);
			} else if (localName.equals("Delimiter")) {
				requestDelimiter = checkForEmptyString(element);
			} else if (localName.equals("IsTruncated")) {
				String str = element.toLowerCase(Locale.getDefault());
				if (str.startsWith("false")) {
					isTruncated = false;
				} else if (str.startsWith("true")) {
					isTruncated = true;
				} else {
					throw (new RuntimeException("Invalid value for IsTruncated field: " + str));
				}
			} else if (localName.equals("Contents")) {
				objectListing.addObject(ncsObjectContents);
				if (log.isDebugEnabled()) {
					log.debug("Created new NcsObject from listimg: " + ncsObjectContents);
				}
			} else if (localName.equals("Key")) {
				ncsObjectContents.setName(element);
			} else if (localName.equals("LastModified")) {
				try {
					ncsObjectContents.setLastModified(DateUtils.parseIso8601Date(element));
				} catch (ParseException e) {
					throw (new RuntimeException("Non-ISO8601 date for LastModified in output: " + element, e));
				}
			} else if (localName.equals("ETag")) {
				ncsObjectContents.setETag(removeQuotes(element));
			} else if (localName.equals("Size")) {
				ncsObjectContents.setSize(parseLong(element));
			} else if (localName.equals("ID")) {
				if (owner == null) {
					owner = new Owner();
					ncsObjectContents.setOwner(owner);
				}
				owner.setUserId(element);
			} else if (localName.equals("DisplayName")) {
				owner.setDisplayName(element);
			} else if (withCommonPrefixes == true && localName.equals("Prefix")) {
				commonPrefixes.add(element);
			} else if (localName.equals("CommonPrefixes")) {
				withCommonPrefixes = false;
			}

			text = new StringBuffer();
		}
	}

}
