package seedu.ezdo.logic.parser;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import seedu.ezdo.commons.exceptions.IllegalValueException;
import seedu.ezdo.commons.util.StringUtil;
import seedu.ezdo.model.tag.Tag;
import seedu.ezdo.model.tag.UniqueTagList;
import seedu.ezdo.model.todo.Address;
import seedu.ezdo.model.todo.Email;
import seedu.ezdo.model.todo.Name;
import seedu.ezdo.model.todo.Priority;
import seedu.ezdo.model.todo.StartDate;

/**
 * Contains utility methods used for parsing strings in the various *Parser classes
 */
public class ParserUtil {

    private static final Pattern INDEX_ARGS_FORMAT = Pattern.compile("(?<targetIndex>.+)");

    /**
     * Returns the specified index in the {@code command} if it is a positive unsigned integer
     * Returns an {@code Optional.empty()} otherwise.
     */
    public static Optional<Integer> parseIndex(String command) {
        final Matcher matcher = INDEX_ARGS_FORMAT.matcher(command.trim());
        if (!matcher.matches()) {
            return Optional.empty();
        }

        String index = matcher.group("targetIndex");
        if (!StringUtil.isUnsignedInteger(index)) {
            return Optional.empty();
        }
        return Optional.of(Integer.parseInt(index));

    }

    /**
     * Returns a new Set populated by all elements in the given list of strings
     * Returns an empty set if the given {@code Optional} is empty,
     * or if the list contained in the {@code Optional} is empty
     */
    public static Set<String> toSet(Optional<List<String>> list) {
        List<String> elements = list.orElse(Collections.emptyList());
        return new HashSet<>(elements);
    }

    /**
    * Splits a preamble string into ordered fields.
    * @return A list of size {@code numFields} where the ith element is the ith field value if specified in
    *         the input, {@code Optional.empty()} otherwise.
    */
    public static List<Optional<String>> splitPreamble(String preamble, int numFields) {
        return Arrays.stream(Arrays.copyOf(preamble.split("\\s+", numFields), numFields))
                .map(Optional::ofNullable)
                .collect(Collectors.toList());
    }

    /**
     * Parses a {@code Optional<String> name} into an {@code Optional<Name>} if {@code name} is present.
     */
    public static Optional<Name> parseName(Optional<String> name) throws IllegalValueException {
        assert name != null;
        return name.isPresent() ? Optional.of(new Name(name.get())) : Optional.empty();
    }

    /**
     * Parses a {@code Optional<String> priority} into an {@code Optional<Priority>} if {@code priority} is present.
     */
    public static Optional<Priority> parsePriority(Optional<String> priority) throws IllegalValueException {
        assert priority != null;
        return priority.isPresent() ? Optional.of(new Priority(priority.get())) : Optional.empty();
    }

    /**
     * Parses a {@code Optional<String> address} into an {@code Optional<Address>} if {@code address} is present.
     */
    public static Optional<Address> parseAddress(Optional<String> address) throws IllegalValueException {
        assert address != null;
        return address.isPresent() ? Optional.of(new Address(address.get())) : Optional.empty();
    }

    /**
     * Parses a {@code Optional<String> startDate} into an {@code Optional<StartDate>} if {@code startDate} is present.
     */
    public static Optional<StartDate> parseStartDate(Optional<String> startDate) throws IllegalValueException {
        assert startDate != null;
        return startDate.isPresent() ? Optional.of(new StartDate(startDate.get())) : Optional.empty();
    }

    /**
     * Parses a {@code Optional<String> email} into an {@code Optional<Email>} if {@code email} is present.
     */
    public static Optional<Email> parseEmail(Optional<String> email) throws IllegalValueException {
        assert email != null;
        return email.isPresent() ? Optional.of(new Email(email.get())) : Optional.empty();
    }

    /**
     * Parses {@code Collection<String> tags} into an {@code UniqueTagList}.
     */
    public static UniqueTagList parseTags(Collection<String> tags) throws IllegalValueException {
        assert tags != null;
        final Set<Tag> tagSet = new HashSet<>();
        for (String tagName : tags) {
            tagSet.add(new Tag(tagName));
        }
        return new UniqueTagList(tagSet);
    }
}

@@ -518,23 +518,23 @@
 el.todo.
-Address
+DueDate
 ;%0Aimport
@@ -3187,23 +3187,25 @@
 String%3E 
-address
+startDate
 %7D into a
@@ -3222,23 +3222,25 @@
 ptional%3C
-Address
+StartDate
 %3E%7D if %7B@
@@ -3244,23 +3244,25 @@
  %7B@code 
-address
+startDate
 %7D is pre
@@ -3306,29 +3306,33 @@
 nal%3C
-Address%3E parseAddress
+StartDate%3E parseStartDate
 (Opt
@@ -3345,23 +3345,25 @@
 String%3E 
-address
+startDate
 ) throws
@@ -3402,23 +3402,25 @@
  assert 
-address
+startDate
  != null
@@ -3436,23 +3436,25 @@
  return 
-address
+startDate
 .isPrese
@@ -3480,23 +3480,27 @@
 new 
-Address(address
+StartDate(startDate
 .get
@@ -3522,32 +3522,36 @@
 .empty();%0A    %7D%0A
+    
 %0A    /**%0A     * 
@@ -3575,37 +3575,35 @@
 ptional%3CString%3E 
-start
+due
 Date%7D into an %7B@
@@ -3608,37 +3608,35 @@
 %7B@code Optional%3C
-Start
+Due
 Date%3E%7D if %7B@code
@@ -3628,37 +3628,35 @@
 ate%3E%7D if %7B@code 
-start
+due
 Date%7D is present
@@ -3684,37 +3684,35 @@
 static Optional%3C
-Start
+Due
 Date%3E parseStart
@@ -3698,37 +3698,35 @@
 l%3CDueDate%3E parse
-Start
+Due
 Date(Optional%3CSt
@@ -3723,37 +3723,35 @@
 ptional%3CString%3E 
-start
+due
 Date) throws Ill
@@ -3778,37 +3778,35 @@
 %0A        assert 
-start
+due
 Date != null;%0A  
@@ -3810,37 +3810,35 @@
 %0A        return 
-start
+due
 Date.isPresent()
@@ -3852,39 +3852,35 @@
 onal.of(new 
-StartDate(start
+DueDate(due
 Date.get()))

Replace parseAddress with parseDueDate