/**
 * This file is part of Todo.txt Touch, an Android app for managing your todo.txt file (http://todotxt.com).
 *
 * Copyright (c) 2009-2013 Todo.txt contributors (http://todotxt.com)
 *
 * LICENSE:
 *
 * Todo.txt Touch is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
 * later version.
 *
 * Todo.txt Touch 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 for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with Todo.txt Touch.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * @author Todo.txt contributors <[email protected]>
 * @license http://www.gnu.org/licenses/gpl.html
 * @copyright 2009-2013 Todo.txt contributors (http://todotxt.com)
 */
package com.todotxt.todotxttouch.task;

import com.todotxt.todotxttouch.util.RelativeDate;
import com.todotxt.todotxttouch.util.Strings;

import java.io.Serializable;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@SuppressWarnings("serial")
public class Task implements Serializable {
	private static final String COMPLETED = "x ";
	private static final String DATE_FORMAT = "yyyy-MM-dd";
	private final String originalText;
	private final Priority originalPriority;

	private long id;
	private Priority priority;
	private boolean deleted = false;
	private boolean completed = false;
	private String text;
	private String completionDate;
	private String prependedDate;
	private String relativeAge = "";
	private Date thresholdDate;
	private Date dueDate;
	private List<String> contexts;
	private List<String> projects;
	private List<String> mailAddresses;
	private List<URL> links;
	private List<String> phoneNumbers;

	private boolean rec;
	private boolean isFromThreshold;
	private int duration;
	private int amount;

	private boolean hidden;

	public Task(long id, String rawText, Date defaultPrependedDate) {
		this.id = id;
		this.init(rawText, defaultPrependedDate);
		this.originalPriority = priority;
		this.originalText = text;
	}

	public Task(long id, String rawText) {
		this(id, rawText, null);
	}
	
	public Task(Task task) {
		this(task.getId(), task.toString(), null);
	}
	
	public Task() {
		this(0, "", null);
	}

	public void update(String rawText) {
		this.init(rawText, null);
	}

	private void init(String rawText, Date defaultPrependedDate) {
		TextSplitter splitter = TextSplitter.getInstance();
		TextSplitter.SplitResult splitResult = splitter.split(rawText);
		this.priority = splitResult.priority;
		this.text = splitResult.text;
		this.prependedDate = splitResult.prependedDate;
		this.completed = splitResult.completed;
		this.completionDate = splitResult.completedDate;

		this.contexts = ContextParser.getInstance().parse(text);
		this.projects = ProjectParser.getInstance().parse(text);
		this.mailAddresses = MailAddressParser.getInstance().parse(text);
		this.links = LinkParser.getInstance().parse(text);
		this.phoneNumbers = PhoneNumberParser.getInstance().parse(text);
		this.deleted = Strings.isEmptyOrNull(text);
		this.hidden = HiddenParser.getInstance().parse(text);
		this.thresholdDate = ThresholdDateParser.getInstance().parseThresholdDate(rawText);
		this.dueDate = ThresholdDateParser.getInstance().parseDueDate(rawText);
		String[] parsedRec = RecParser.getInstance().parse(rawText);

		rec = parsedRec != null;
		if (rec) {
			isFromThreshold = !(parsedRec[0].isEmpty());
			amount = Integer.parseInt(parsedRec[1]);
			if ("d".equals(parsedRec[2])) {
				duration = Calendar.DAY_OF_YEAR;
			} else if ("w".equals(parsedRec[2])) {
				duration = Calendar.WEEK_OF_YEAR;
			} else if ("m".equals(parsedRec[2])) {
				duration = Calendar.MONTH;
			} else if ("y".equals(parsedRec[2])) {
				duration = Calendar.YEAR;
			}
		}

		if (defaultPrependedDate != null
				&& Strings.isEmptyOrNull(this.prependedDate)) {
			SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
			this.prependedDate = formatter.format(defaultPrependedDate);
		}

		if (!Strings.isEmptyOrNull(this.prependedDate)) {
			SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
			try {
				Date d = sdf.parse(this.prependedDate);
				this.relativeAge = RelativeDate.getRelativeDate(d);
			} catch (ParseException e) {
				// e.printStackTrace();
			}
		}
	}

	public Priority getOriginalPriority() {
		return originalPriority;
	}

	public String getOriginalText() {
		return originalText;
	}

	public String getText() {
		return text;
	}

	public long getId() {
		return id;
	}

	public void setPriority(Priority priority) {
		this.priority = priority;
	}

	public Priority getPriority() {
		return priority;
	}

	public List<String> getContexts() {
		return contexts;
	}

	public List<URL> getLinks() {
		return links;
	}

	public List<String> getPhoneNumbers() {
		return phoneNumbers;
	}

	public List<String> getProjects() {
		return projects;
	}

	public List<String> getMailAddresses() {
		return mailAddresses;
	}

	public String getPrependedDate() {
		return prependedDate;
	}

	public String getRelativeAge() {
		return relativeAge;
	}

	public boolean isDeleted() {
		return deleted;
	}

	public boolean isCompleted() {
		return completed;
	}

	public boolean isHidden() {
		return hidden;
	}

	public Date getThresholdDate() {
		return thresholdDate;
	}

	public Date getDueDate() {
		return dueDate;
	}

	public String getCompletionDate() {
		return completionDate;
	}

	public boolean isRec() {
		return rec;
	}

	public boolean isFromThreshold() {
		return isFromThreshold;
	}

	public int getDuration() {
		return duration;
	}

	public int getAmount() {
		return amount;
	}

	public void markComplete(Date date) {
		if (!this.completed) {
			this.priority = Priority.NONE;
			this.completionDate = new SimpleDateFormat(Task.DATE_FORMAT)
					.format(date);
			this.deleted = false;
			this.completed = true;
		}
	}

	public void markIncomplete() {
		if (this.completed) {
			this.completionDate = "";
			this.completed = false;
		}
	}

	public void delete() {
		this.update("");
	}
	
	public String toString() {
		return inFileFormat();
	}

	// TODO need a better solution (TaskFormatter?) here
	public String inScreenFormat() {
		StringBuilder sb = new StringBuilder();
		if (this.completed) {
			sb.append(COMPLETED).append(this.completionDate).append(" ");
			if (!Strings.isEmptyOrNull(this.prependedDate)) {
				sb.append(this.prependedDate).append(" ");
			}
		}
		sb.append(this.text);
		return sb.toString();
	}

	public String inFileFormat() {
		StringBuilder sb = new StringBuilder();
		sb.append(inFileFormatHeader());
		sb.append(this.text);
		return sb.toString();
	}
	
	public String inFileFormatHeader() {
		StringBuilder sb = new StringBuilder();
		if (this.completed) {
			sb.append(COMPLETED).append(this.completionDate).append(" ");
			if (!Strings.isEmptyOrNull(this.prependedDate)) {
				sb.append(this.prependedDate).append(" ");
			}
		} else {
			if (priority != Priority.NONE) {
				sb.append(priority.inFileFormat()).append(" ");
			}
			if (!Strings.isEmptyOrNull(this.prependedDate)) {
				sb.append(this.prependedDate).append(" ");
			}
		}
		return sb.toString();
	}
	
	public String inFileFormatHeaderNoDate() {
		StringBuilder sb = new StringBuilder();
		if (this.completed) {
			sb.append(COMPLETED).append(this.completionDate).append(" ");
		} else {
			if (priority != Priority.NONE) {
				sb.append(priority.inFileFormat()).append(" ");
			}
		}
		return sb.toString();
	}

	public void copyInto(Task destination) {
		destination.id = this.id;
		destination.init(this.inFileFormat(), null);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Task other = (Task) obj;
		if (completed != other.completed)
			return false;
		if (completionDate == null) {
			if (other.completionDate != null)
				return false;
		} else if (!completionDate.equals(other.completionDate))
			return false;
		if (contexts == null) {
			if (other.contexts != null)
				return false;
		} else if (!contexts.equals(other.contexts))
			return false;
		if (deleted != other.deleted)
			return false;
		if (id != other.id)
			return false;
		if (links == null) {
			if (other.links != null)
				return false;
		} else if (!links.equals(other.links))
			return false;
		if (mailAddresses == null) {
			if (other.mailAddresses != null)
				return false;
		} else if (!mailAddresses.equals(other.mailAddresses))
			if (phoneNumbers == null) {
				if (other.phoneNumbers != null)
					return false;
			} else if (!phoneNumbers.equals(other.phoneNumbers))
				return false;
		if (prependedDate == null) {
			if (other.prependedDate != null)
				return false;
		} else if (!prependedDate.equals(other.prependedDate))
			return false;
		if (priority != other.priority)
			return false;
		if (projects == null) {
			if (other.projects != null)
				return false;
		} else if (!projects.equals(other.projects))
			return false;
		if (relativeAge == null) {
			if (other.relativeAge != null)
				return false;
		} else if (!relativeAge.equals(other.relativeAge))
			return false;
		if (text == null) {
			if (other.text != null)
				return false;
		} else if (!text.equals(other.text))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (completed ? 1231 : 1237);
		result = prime * result
				+ ((completionDate == null) ? 0 : completionDate.hashCode());
		result = prime * result
				+ ((contexts == null) ? 0 : contexts.hashCode());
		result = prime * result + (deleted ? 1231 : 1237);
		result = prime * result + (int) (id ^ (id >>> 32));
		result = prime * result + ((links == null) ? 0 : links.hashCode());
		result = prime * result
				+ ((mailAddresses == null) ? 0 : mailAddresses.hashCode())
				+ ((phoneNumbers == null) ? 0 : phoneNumbers.hashCode());
		result = prime * result
				+ ((prependedDate == null) ? 0 : prependedDate.hashCode());
		result = prime * result
				+ ((priority == null) ? 0 : priority.hashCode());
		result = prime * result
				+ ((projects == null) ? 0 : projects.hashCode());
		result = prime * result
				+ ((relativeAge == null) ? 0 : relativeAge.hashCode());
		result = prime * result + ((text == null) ? 0 : text.hashCode());
		return result;
	}

	public void initWithFilters(ArrayList<Priority> prios,
			ArrayList<String> ctxts, ArrayList<String> pjs) {
		if ((prios != null) && (prios.size() == 1))
		{
			setPriority(prios.get(0));
		}
		if ((ctxts != null) && (ctxts.size() == 1))
		{
			contexts.clear();
			contexts.add(ctxts.get(0));
		}
		if ((pjs != null) && (pjs.size() == 1))
		{
			projects.clear();
			projects.add(pjs.get(0));
		}
	}
}