/*******************************************************************************
 * Copyright (c) 2004, 2010 Tasktop Technologies and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Tasktop Technologies - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.bugzilla.core;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.eclipse.mylyn.tasks.core.IRepositoryPerson;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
import org.eclipse.mylyn.tasks.core.data.TaskCommentMapper;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskDataCollector;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parser for xml bugzilla reports.
 * 
 * @author Rob Elves
 * @author Hiroyuki Inaba (internationalization)
 */
public class SaxMultiBugReportContentHandler extends DefaultHandler {

	private static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$

	private static final String ID_STRING_BEGIN = " (id="; //$NON-NLS-1$

	private static final String ID_STRING_END = ")"; //$NON-NLS-1$

	private StringBuffer characters;

	private TaskComment taskComment;

	private Map<String, TaskCommentMapper> attachIdToComment = new HashMap<String, TaskCommentMapper>();

	private int commentNum = 0;

	private BugzillaAttachmentMapper attachment;

	private final Map<String, TaskData> taskDataMap;

	private TaskData repositoryTaskData;

	private List<TaskComment> longDescs;

	private String errorMessage = null;

	private final List<BugzillaCustomField> customFields;

	private final TaskDataCollector collector;

	private boolean isDeprecated = false;

	private boolean isPatch = false;

	private String token;

	private String exporter;

	private TaskAttribute attachmentAttribute;

	private boolean bugParseErrorOccurred;

	private final BugzillaRepositoryConnector connector;

	private boolean useIsPrivate;

	private final TaskAttributeMapper mapper;

	private final SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); //$NON-NLS-1$

	private final SimpleDateFormat simpleFormatter_deltaTS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$

	private String bugIDValue;

	public SaxMultiBugReportContentHandler(TaskAttributeMapper mapper, TaskDataCollector collector,
			Map<String, TaskData> taskDataMap, List<BugzillaCustomField> customFields,
			BugzillaRepositoryConnector connector) {
		this.mapper = mapper;
		this.taskDataMap = taskDataMap;
		this.customFields = customFields;
		this.collector = collector;
		this.connector = connector;
		TaskRepository taskRepository = mapper.getTaskRepository();
		String useParam = taskRepository.getProperty(IBugzillaConstants.BUGZILLA_INSIDER_GROUP);
		useIsPrivate = (useParam == null || (useParam != null && useParam.equals("true"))); //$NON-NLS-1$
	}

	public boolean errorOccurred() {
		return errorMessage != null;
	}

	public String getErrorMessage() {
		return errorMessage;
	}

	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		characters.append(ch, start, length);
		//System.err.println(String.copyValueOf(ch, start, length));
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		characters = new StringBuffer();
		BugzillaAttribute tag = BugzillaAttribute.UNKNOWN;
		if (localName.startsWith(BugzillaCustomField.CUSTOM_FIELD_PREFIX)) {
			return;
		}
		try {
			tag = BugzillaAttribute.valueOf(localName.trim().toUpperCase(Locale.ENGLISH));
		} catch (RuntimeException e) {
			if (e instanceof IllegalArgumentException) {
				// ignore unrecognized tags
				return;
			}
			throw e;
		}
		switch (tag) {
		case BUGZILLA:
			// Note: here we can get the bugzilla version if necessary
//			String version = attributes.getValue("version");
//			urlbase = attributes.getValue("urlbase"); //$NON-NLS-1$
//			String maintainer = attributes.getValue("maintainer");
			exporter = attributes.getValue("exporter"); //$NON-NLS-1$
			break;
		case BUG:
			if (attributes != null && (attributes.getValue("error") != null)) { //$NON-NLS-1$
				errorMessage = attributes.getValue("error"); //$NON-NLS-1$
				bugParseErrorOccurred = true;
				repositoryTaskData = null;
			}
			attachIdToComment = new HashMap<String, TaskCommentMapper>();
			commentNum = 0;
			taskComment = null;
			longDescs = new ArrayList<TaskComment>();
			token = null;
			bugIDValue = null;
			break;
		case LONG_DESC:
			String is_private = attributes.getValue("isprivate"); //$NON-NLS-1$
			taskComment = new TaskComment(is_private);
			break;
		case WHO:
			if (taskComment != null) {
				if (attributes != null && attributes.getLength() > 0) {
					String name = attributes.getValue(ATTRIBUTE_NAME);
					if (name != null && name.length() > 0) {
						taskComment.authorName = name;
					}
				}
			}
			break;
		case REPORTER:
			if (attributes != null && attributes.getLength() > 0) {
				String name = attributes.getValue(ATTRIBUTE_NAME);
				if (name != null) {
					BugzillaTaskDataHandler.createAttribute(repositoryTaskData, BugzillaAttribute.REPORTER_NAME)
							.setValue(name);
				}
			}
			break;
		case QA_CONTACT:
			if (attributes != null && attributes.getLength() > 0) {
				String name = attributes.getValue(ATTRIBUTE_NAME);
				if (name != null) {
					BugzillaTaskDataHandler.createAttribute(repositoryTaskData, BugzillaAttribute.QA_CONTACT_NAME)
							.setValue(name);
				}
			}
			break;
		case ASSIGNED_TO:
			if (attributes != null && attributes.getLength() > 0) {
				String name = attributes.getValue(ATTRIBUTE_NAME);
				if (name != null) {
					BugzillaTaskDataHandler.createAttribute(repositoryTaskData, BugzillaAttribute.ASSIGNED_TO_NAME)
							.setValue(name);
				}
			}
			break;
		case ATTACHMENT:
			if (attributes != null) {
				isDeprecated = "1".equals(attributes.getValue(BugzillaAttribute.IS_OBSOLETE.getKey())); //$NON-NLS-1$
				isPatch = "1".equals(attributes.getValue(BugzillaAttribute.IS_PATCH.getKey())); //$NON-NLS-1$
			}
			break;
		case FLAG:
			if (attributes != null && attributes.getLength() > 0) {
				String name = attributes.getValue(ATTRIBUTE_NAME);
				if (name != null) {
					BugzillaFlagMapper mapper = new BugzillaFlagMapper(connector);
					String requestee = attributes.getValue("requestee"); //$NON-NLS-1$
					mapper.setRequestee(requestee);
					String setter = attributes.getValue("setter"); //$NON-NLS-1$
					mapper.setSetter(setter);
					String status = attributes.getValue("status"); //$NON-NLS-1$
					mapper.setState(status);
					mapper.setFlagId(name);
					String id = attributes.getValue("id"); //$NON-NLS-1$
					if (id != null && !id.equals("")) { //$NON-NLS-1$
						/*
						 * for version 3.2rc1 and 3.2.rc2 the id was not defined so we ignore
						 * the definition
						 */
						try {
							mapper.setNumber(Integer.valueOf(id));
							TaskAttribute attribute;
							if (attachmentAttribute != null) {
								attribute = attachmentAttribute.createAttribute(BugzillaAttribute.KIND_FLAG + id);
							} else {
								attribute = repositoryTaskData.getRoot().createAttribute(
										BugzillaAttribute.KIND_FLAG + id);
							}
							mapper.applyTo(attribute);
						} catch (NumberFormatException e) {
							// ignore
						}
					}
				}
			}
			break;
		}

	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {

		//remove whitespaces from the end of the parsed Text
		while (characters.length() > 0 && Character.isWhitespace(characters.charAt(characters.length() - 1))) {
			characters.setLength(characters.length() - 1);
		}

		String parsedText = characters.toString();

		if (localName.startsWith(BugzillaCustomField.CUSTOM_FIELD_PREFIX)) {
			TaskAttribute endAttribute = repositoryTaskData.getRoot().getAttribute(localName);
			if (endAttribute == null) {
				String desc = "???"; //$NON-NLS-1$
				BugzillaCustomField customField = null;
				for (BugzillaCustomField bugzillaCustomField : customFields) {
					if (localName.equals(bugzillaCustomField.getName())) {
						customField = bugzillaCustomField;
						break;
					}
				}
				if (customField != null) {
					TaskAttribute atr = repositoryTaskData.getRoot().createAttribute(localName);
					desc = customField.getDescription();
					atr.getMetaData().defaults().setLabel(desc).setReadOnly(false);
					atr.getMetaData().setKind(TaskAttribute.KIND_DEFAULT);
					atr.getMetaData().setType(TaskAttribute.TYPE_SHORT_TEXT);
					switch (customField.getFieldType()) {
					case FreeText:
						atr.getMetaData().setType(TaskAttribute.TYPE_SHORT_TEXT);
						break;
					case DropDown:
						atr.getMetaData().setType(TaskAttribute.TYPE_SINGLE_SELECT);
						break;
					case MultipleSelection:
						atr.getMetaData().setType(TaskAttribute.TYPE_MULTI_SELECT);
						break;
					case LargeText:
						atr.getMetaData().setType(TaskAttribute.TYPE_LONG_TEXT);
						break;
					case DateTime:
						atr.getMetaData().setType(TaskAttribute.TYPE_DATETIME);
						break;

					default:
						List<String> options = customField.getOptions();
						if (options.size() > 0) {
							atr.getMetaData().setType(TaskAttribute.TYPE_SINGLE_SELECT);
						} else {
							atr.getMetaData().setType(TaskAttribute.TYPE_SHORT_TEXT);
						}
					}
					atr.getMetaData().setReadOnly(false);
					atr.setValue(parsedText);
				}
			} else {
				endAttribute.addValue(parsedText);
			}
		}

		BugzillaAttribute tag = BugzillaAttribute.UNKNOWN;
		try {
			tag = BugzillaAttribute.valueOf(localName.trim().toUpperCase(Locale.ENGLISH));
		} catch (RuntimeException e) {
			if (e instanceof IllegalArgumentException) {
				// ignore unrecognized tags
				return;
			}
			throw e;
		}
		switch (tag) {
		case BUG_ID: {
			bugIDValue = parsedText.trim();
			repositoryTaskData = taskDataMap.get(parsedText.trim());
			if (repositoryTaskData == null) {
				errorMessage = parsedText + Messages.SaxMultiBugReportContentHandler_id_not_found;
				bugParseErrorOccurred = true;
				break;
			}

			TaskAttribute attr = repositoryTaskData.getRoot().getMappedAttribute(tag.getKey());
			if (attr == null) {
				attr = BugzillaTaskDataHandler.createAttribute(repositoryTaskData, tag);
			}
			attr.setValue(parsedText);

			if (exporter != null) {
				createAttrribute(exporter, BugzillaAttribute.EXPORTER_NAME);
			} else {
				createAttrribute("", BugzillaAttribute.EXPORTER_NAME); //$NON-NLS-1$
			}

			break;
		}

		// Comment attributes
		case WHO:
			if (taskComment != null) {
				taskComment.author = parsedText;
			}
			break;
		case BUG_WHEN:
			if (taskComment != null) {
				taskComment.createdTimeStamp = parsedText;
			}
			break;
		case WORK_TIME:
			if (taskComment != null) {
				taskComment.timeWorked = parsedText;
			}
			break;
		case THETEXT:
			if (taskComment != null) {
				taskComment.commentText = parsedText;
			}
			break;
		case LONG_DESC:
			if (taskComment != null) {
				longDescs.add(taskComment);
			}
			break;

		// Attachment attributes
		case ATTACHID:
			attachmentAttribute = repositoryTaskData.getRoot().createAttribute(
					TaskAttribute.PREFIX_ATTACHMENT + parsedText);
			attachment = BugzillaAttachmentMapper.createFrom(attachmentAttribute);
			attachment.setLength(new Long(-1));
			attachment.setAttachmentId(parsedText);
			attachment.setPatch(isPatch);
			attachment.setDeprecated(isDeprecated);
			attachment.setToken(null);
			break;
		case DATE:
			if (attachment != null) {
				try {
					attachment.setCreationDate(simpleFormatter.parse(parsedText));
					break;
				} catch (ParseException e) {
				} catch (NumberFormatException e) {
				}
			}
			break;
		case DESC:
			if (attachment != null) {
				attachment.setDescription(parsedText);
			}
			break;
		case FILENAME:
			if (attachment != null) {
				attachment.setFileName(parsedText);
			}
			break;
		case CTYPE:
		case TYPE:
			if (attachment != null) {
				attachment.setContentType(parsedText);
			}
			break;
		case DELTA_TS:
			if (attachment != null) {
				try {
					if (parsedText != null) {
						try {
							attachment.setDeltaDate(simpleFormatter_deltaTS.parse(parsedText));
						} catch (ParseException e) {
						}
					}
				} catch (NumberFormatException e) {
					// ignore
				}
			} else {
				createAttrribute(parsedText, tag);
			}
			break;

		case SIZE:
			if (attachment != null) {
				try {
					if (parsedText != null) {
						attachment.setLength(Long.parseLong(parsedText));
					}
				} catch (NumberFormatException e) {
					// ignore
				}
			}
			break;
		case ATTACHMENT:
			if (attachment != null) {
				attachment.applyTo(attachmentAttribute);
			}
			isPatch = false;
			isDeprecated = false;
			attachment = null;
			attachmentAttribute = null;
			break;
		case DATA:
			// ignored
			break;
		case BUGZILLA:
			// ignored
			break;
		case BUG:
			// Reached end of bug.
			if (bugParseErrorOccurred) {
				bugParseErrorOccurred = false;
				break;
			}
			addDescriptionAndComments();

			// Need to set LONGDESCLENGTH to number of comments + 1 for description
			TaskAttribute numCommentsAttribute = repositoryTaskData.getRoot().getMappedAttribute(
					BugzillaAttribute.LONGDESCLENGTH.getKey());
			if (numCommentsAttribute == null) {
				numCommentsAttribute = BugzillaTaskDataHandler.createAttribute(repositoryTaskData,
						BugzillaAttribute.LONGDESCLENGTH);
			}

			numCommentsAttribute.setValue("" + commentNum); //$NON-NLS-1$

			updateAttachmentMetaData();
			TaskAttribute attrCreation = repositoryTaskData.getRoot().getAttribute(
					BugzillaAttribute.CREATION_TS.getKey());

			updateCustomFields(repositoryTaskData);

			if (token != null) {
				TaskAttribute tokenAttribute = BugzillaTaskDataHandler.createAttribute(repositoryTaskData,
						BugzillaAttribute.TOKEN);
				tokenAttribute.setValue(token);
			}

			// Work around (bug#285796) If planning enabled (ESTIMATE_TIME attr present) create DEADLINE attr since it should be present.
			TaskAttribute estimatedTimeAttr = repositoryTaskData.getRoot().getMappedAttribute(
					BugzillaAttribute.ESTIMATED_TIME.getKey());
			TaskAttribute deadlineAttr = repositoryTaskData.getRoot().getMappedAttribute(
					BugzillaAttribute.DEADLINE.getKey());
			if (estimatedTimeAttr != null && deadlineAttr == null) {
				BugzillaTaskDataHandler.createAttribute(repositoryTaskData, BugzillaAttribute.DEADLINE);
			}

			// Save/Add to collector. Guard against empty data sets
			if (attrCreation != null && !attrCreation.equals("")) { //$NON-NLS-1$
				collector.accept(repositoryTaskData);
			}
			repositoryTaskData = null;
			break;
		case BLOCKED:
			// handled similarly to DEPENDSON
		case DEPENDSON:
			TaskAttribute blockOrDepends = repositoryTaskData.getRoot().getMappedAttribute(tag.getKey());
			if (blockOrDepends == null) {
				BugzillaTaskDataHandler.createAttribute(repositoryTaskData, tag).setValue(parsedText);
			} else {
				if (blockOrDepends.getValue().equals("")) { //$NON-NLS-1$
					blockOrDepends.setValue(parsedText);
				} else {
					blockOrDepends.setValue(blockOrDepends.getValue() + ", " + parsedText); //$NON-NLS-1$
				}
			}
			break;
		case DUP_ID:
			TaskAttribute duplicateOf = repositoryTaskData.getRoot().getMappedAttribute(tag.getKey());
			if (duplicateOf == null) {
				BugzillaTaskDataHandler.createAttribute(repositoryTaskData, tag).setValue(parsedText);
			} else {
				if (duplicateOf.getValue().equals("")) { //$NON-NLS-1$
					duplicateOf.setValue(parsedText);
				}
			}
			break;
		case UNKNOWN:
			//ignore
			break;
		case FLAG:
			//ignore
			break;
		case TOKEN:
			if (attachment != null) {
				attachment.setToken(parsedText);
			} else {
				token = parsedText;
			}
			break;
		case ATTACHER:
			if (attachment != null) {
				IRepositoryPerson author = repositoryTaskData.getAttributeMapper()
						.getTaskRepository()
						.createPerson(parsedText);
				attachment.setAuthor(author);
			}
			break;
		case TARGET_MILESTONE:
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USETARGETMILESTONE, true);
			break;
		case QA_CONTACT:
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USEQACONTACT, true);
			break;
		case STATUS_WHITEBOARD:
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USESTATUSWHITEBOARD, true);
			break;
		case CLASSIFICATION:
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USECLASSIFICATION, false);
			break;
		case ALIAS:
			if (repositoryTaskData == null) {
				repositoryTaskData = taskDataMap.get(parsedText.trim());
				if (repositoryTaskData == null) {
					errorMessage = parsedText + Messages.SaxMultiBugReportContentHandler_id_not_found;
					bugParseErrorOccurred = true;
					break;
				} else {
					errorMessage = null;
					bugParseErrorOccurred = false;
				}
			}
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USEBUGALIASES, false);
			TaskAttribute attr = repositoryTaskData.getRoot().getMappedAttribute(BugzillaAttribute.BUG_ID.getKey());
			if (attr == null) {
				attr = BugzillaTaskDataHandler.createAttribute(repositoryTaskData, BugzillaAttribute.BUG_ID);
			}
			attr.setValue(bugIDValue);

			if (exporter != null) {
				createAttrribute(exporter, BugzillaAttribute.EXPORTER_NAME);
			} else {
				createAttrribute("", BugzillaAttribute.EXPORTER_NAME); //$NON-NLS-1$
			}
			break;
		case SEE_ALSO:
			BugzillaUtil.createAttributeWithKindDefaultIfUsed(parsedText, tag, repositoryTaskData,
					IBugzillaConstants.BUGZILLA_PARAM_USE_SEE_ALSO, false);
			break;
		case COMMENTID:
			if (taskComment != null) {
				taskComment.id = Integer.parseInt(parsedText);
			}
			break;
		default:
			createAttrribute(parsedText, tag);
			break;
		}

	}

	private TaskAttribute createAttrribute(String parsedText, BugzillaAttribute tag) {
		TaskAttribute attribute = repositoryTaskData.getRoot().getMappedAttribute(tag.getKey());
		if (attribute == null) {
			attribute = BugzillaTaskDataHandler.createAttribute(repositoryTaskData, tag);
			attribute.setValue(parsedText);
		} else {
			attribute.addValue(parsedText);
		}
		return attribute;
	}

	private void updateCustomFields(TaskData taskData) {
		RepositoryConfiguration config = connector.getRepositoryConfiguration(repositoryTaskData.getRepositoryUrl());
		if (config != null) {
			for (BugzillaCustomField bugzillaCustomField : config.getCustomFields()) {

				TaskAttribute atr = taskData.getRoot().getAttribute(bugzillaCustomField.getName());
				if (atr == null) {
					atr = taskData.getRoot().createAttribute(bugzillaCustomField.getName());
				}

				if (atr != null) {
					atr.getMetaData().defaults().setLabel(bugzillaCustomField.getDescription());
					atr.getMetaData().setKind(TaskAttribute.KIND_DEFAULT);

					switch (bugzillaCustomField.getFieldType()) {
					case FreeText:
						atr.getMetaData().setType(TaskAttribute.TYPE_SHORT_TEXT);
						break;
					case DropDown:
						atr.getMetaData().setType(TaskAttribute.TYPE_SINGLE_SELECT);
						break;
					case MultipleSelection:
						atr.getMetaData().setType(TaskAttribute.TYPE_MULTI_SELECT);
						break;
					case LargeText:
						atr.getMetaData().setType(TaskAttribute.TYPE_LONG_TEXT);
						break;
					case DateTime:
						atr.getMetaData().setType(TaskAttribute.TYPE_DATETIME);
						break;

					default:
						List<String> options = bugzillaCustomField.getOptions();
						if (options.size() > 0) {
							atr.getMetaData().setType(TaskAttribute.TYPE_SINGLE_SELECT);
						} else {
							atr.getMetaData().setType(TaskAttribute.TYPE_SHORT_TEXT);
						}
					}
					atr.getMetaData().setReadOnly(false);
				}
			}
		}

	}

	private void updateAttachmentMetaData() {
		List<TaskAttribute> taskAttachments = repositoryTaskData.getAttributeMapper().getAttributesByType(
				repositoryTaskData, TaskAttribute.TYPE_ATTACHMENT);
		for (TaskAttribute attachment : taskAttachments) {
			BugzillaAttachmentMapper attachmentMapper = BugzillaAttachmentMapper.createFrom(attachment);
			if (attachmentMapper.getAuthor() == null || attachmentMapper.getCreationDate() == null) {
				TaskCommentMapper taskComment = attachIdToComment.get(attachmentMapper.getAttachmentId());
				if (taskComment != null) {
					attachmentMapper.setAuthor(taskComment.getAuthor());
					attachmentMapper.setCreationDate(taskComment.getCreationDate());
				}
			}
			attachmentMapper.setUrl(repositoryTaskData.getRepositoryUrl()
					+ IBugzillaConstants.URL_GET_ATTACHMENT_SUFFIX + attachmentMapper.getAttachmentId());
			attachmentMapper.applyTo(attachment);
		}
	}

	private void addDescriptionAndComments() {
		int longDescsSize = longDescs.size() - 1;
		commentNum = 1;
		if (longDescsSize == 0) {
			addDescription(longDescs.get(0));
		} else if (longDescsSize == 1) {
			if (longDescs.get(0).createdTimeStamp.compareTo(longDescs.get(1).createdTimeStamp) <= 0) {
				// if created_0 is equal to created_1 we assume that longDescs at index 0 is the description.
				addDescription(longDescs.get(0));
				addComment(longDescs.get(1));
			} else {
				addDescription(longDescs.get(1));
				addComment(longDescs.get(0));
			}
		} else if (longDescsSize > 1) {
			String created_0 = longDescs.get(0).createdTimeStamp;
			String created_1 = longDescs.get(1).createdTimeStamp;
			String created_n = longDescs.get(longDescsSize).createdTimeStamp;
			if (created_0.compareTo(created_1) <= 0 && created_0.compareTo(created_n) < 0) {
				// if created_0 is equal to created_1 we assume that longDescs at index 0 is the description.
				addDescription(longDescs.get(0));

				if (created_1.compareTo(created_n) < 0) {
					for (int i = 1; i <= longDescsSize; i++) {
						addComment(longDescs.get(i));
					}
				} else {
					for (int i = longDescsSize; i > 0; i--) {
						addComment(longDescs.get(i));
					}
				}
			} else {
				addDescription(longDescs.get(longDescsSize));
				if (created_0.compareTo(created_1) < 0) {
					for (int i = 0; i < longDescsSize; i++) {
						addComment(longDescs.get(i));
					}
				} else {
					for (int i = longDescsSize - 1; i >= 0; i--) {
						addComment(longDescs.get(i));
					}
				}
			}
		}
	}

	private void addDescription(TaskComment comment) {
		TaskAttribute attrDescription = BugzillaTaskDataHandler.createAttribute(repositoryTaskData,
				BugzillaAttribute.LONG_DESC);
		TaskAttribute idAttribute = attrDescription.createAttribute(IBugzillaConstants.BUGZILLA_DESCRIPTION_ID);
		TaskAttribute isprivateAttribute = attrDescription.createAttribute(IBugzillaConstants.BUGZILLA_DESCRIPTION_IS_PRIVATE);
		attrDescription.setValue(comment.commentText);
		idAttribute.setValue(Integer.toString(comment.id));
		if (comment.isPrivate != null) {
			isprivateAttribute.setValue(comment.isPrivate);
		}
		if (!useIsPrivate) {
			if ("1".equals(comment.isPrivate)) { //$NON-NLS-1$
				TaskRepository taskRepository = mapper.getTaskRepository();
				taskRepository.setProperty(IBugzillaConstants.BUGZILLA_INSIDER_GROUP, "true"); //$NON-NLS-1$
				useIsPrivate = true;
			}
		}
	}

	private void addComment(TaskComment comment) {
		TaskAttribute attribute = repositoryTaskData.getRoot().createAttribute(
				TaskAttribute.PREFIX_COMMENT + commentNum);
		TaskCommentMapper taskComment = TaskCommentMapper.createFrom(attribute);
		taskComment.setCommentId(comment.id + ""); //$NON-NLS-1$
		taskComment.setNumber(commentNum);
		taskComment.setUrl(repositoryTaskData.getRepositoryUrl() + "/show_bug.cgi?id=" //$NON-NLS-1$
				+ repositoryTaskData.getTaskId() + "#c" + commentNum); //$NON-NLS-1$
		IRepositoryPerson author = repositoryTaskData.getAttributeMapper()
				.getTaskRepository()
				.createPerson(comment.author);
		author.setName(comment.authorName);
		taskComment.setAuthor(author);
		if (useIsPrivate) {
			taskComment.setIsPrivate("1".equals(comment.isPrivate)); //$NON-NLS-1$
		} else {
			if ("1".equals(comment.isPrivate)) { //$NON-NLS-1$
				TaskRepository taskRepository = mapper.getTaskRepository();
				taskRepository.setProperty(IBugzillaConstants.BUGZILLA_INSIDER_GROUP, "true"); //$NON-NLS-1$
				useIsPrivate = true;
				taskComment.setIsPrivate("1".equals(comment.isPrivate)); //$NON-NLS-1$
			} else {
				taskComment.setIsPrivate(null);
			}
		}
		TaskAttribute attrTimestamp = attribute.createAttribute(BugzillaAttribute.BUG_WHEN.getKey());
		attrTimestamp.setValue(comment.createdTimeStamp);
		taskComment.setCreationDate(repositoryTaskData.getAttributeMapper().getDateValue(attrTimestamp));
		if (comment.commentText != null) {
			String commentText = comment.commentText.trim();
			taskComment.setText(commentText);

		}
		taskComment.applyTo(attribute);
		commentNum++;

		if (comment.timeWorked != null) {
			TaskAttribute workTime = BugzillaTaskDataHandler.createAttribute(attribute, BugzillaAttribute.WORK_TIME);
			workTime.setValue(comment.timeWorked);
		}

		parseAttachment(taskComment);
	}

	/** determines attachment id from comment */
	private void parseAttachment(TaskCommentMapper comment) {
		String attachmentID = ""; //$NON-NLS-1$
		String commentText = comment.getText();
		int firstDelimiter = commentText.indexOf("\n"); //$NON-NLS-1$
		if (firstDelimiter < 0) {
			firstDelimiter = commentText.length();
		}
		int startIndex = commentText.indexOf(ID_STRING_BEGIN);
		if (startIndex > 0 && startIndex < firstDelimiter) {
			int endIndex = commentText.indexOf(ID_STRING_END, startIndex);
			if (endIndex > 0 && endIndex < firstDelimiter) {
				startIndex += ID_STRING_BEGIN.length();
				int p = startIndex;
				while (p < endIndex) {
					char c = commentText.charAt(p);
					if (c < '0' || c > '9') {
						break;
					}
					p++;
				}
				if (p == endIndex) {
					attachmentID = commentText.substring(startIndex, endIndex);
					if (!attachmentID.equals("")) { //$NON-NLS-1$
						attachIdToComment.put(attachmentID, comment);
					}
				}
			}
		}
	}

	private static class TaskComment {

		@SuppressWarnings("unused")
		public int number;

		public int id;

		public String author;

		public String authorName;

		public String createdTimeStamp;

		public String commentText;

		public String timeWorked;

		public String isPrivate;

		public TaskComment(String isprivate) {
			this.isPrivate = isprivate;
		}
	}

}
