package org.roklib.urifragmentrouting.parameter;

import org.roklib.urifragmentrouting.exception.ParameterValueConversionException;
import org.roklib.urifragmentrouting.parameter.converter.ParameterValueConverter;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This URI parameter handles parameter values consisting of two parts: a numerical value (interpreted as a number of
 * type Long) and a textual suffix. This is useful if you want to add an id (e. g. the primary key of some item)
 * including a human-readable textual description of the referred item into a URI fragment. Consider, for example, a
 * blogging software where individual blog posts are referred in the URI fragment by their database id. In order to give
 * the users more context about the referred blog posts, a post's title can be added to the post id. For example:
 * <p>
 * <tt>http://www.example.com/blog#!posts/67234-my-first-blog-post</tt>
 * <p>
 * For these use cases you can use this parameter type. It converts the parameter's String value into an object of type
 * {@link IdWithText}. This object contains the number as a Long value and the remaining text as a String. In the
 * example above, the object would contain <tt>id=67234</tt> and <tt>text="-my-first-blog-post"</tt>.
 */
public class SingleLongWithIgnoredTextUriParameter extends AbstractSingleUriParameter<SingleLongWithIgnoredTextUriParameter.IdWithText> {
    private static final long serialVersionUID = 7990237721421647271L;

    public SingleLongWithIgnoredTextUriParameter(String parameterName) {
        super(parameterName, IdWithTextParameterValueConverter.INSTANCE);
    }

    /**
     * Defines a data object which combines two values: a numerical ID together with a text. This is used as the domain
     * type for values of {@link SingleLongWithIgnoredTextUriParameter}.
     */
    public interface IdWithText {
        Long getId();

        String getText();

        void setId(Long id);

        void setText(String text);
    }

    /**
     * Implementation of interface {@link IdWithText}. Two objects of this class are considered equal if their ids
     * match. The text is ignored.
     */
    public static class IdWithTextImpl implements IdWithText {
        private Long id;
        private String text;

        public IdWithTextImpl() {
        }

        public IdWithTextImpl(Long id, String text) {
            this.id = id;
            this.text = text;
        }

        @Override
        public Long getId() {
            return id;
        }

        @Override
        public String getText() {
            return text;
        }

        @Override
        public void setId(Long id) {
            this.id = id;
        }

        @Override
        public void setText(String text) {
            this.text = text;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (!(obj instanceof IdWithTextImpl)) {
                return false;
            }
            IdWithTextImpl other = (IdWithTextImpl) obj;
            return id != null && other.id.equals(id);
        }

        @Override
        public int hashCode() {
            return id == null ? 0 : id.hashCode();
        }
    }


    /**
     * Converter class for converting a String into an object of type {@link IdWithText}. The converter tries to match
     * an input String against the regular expression {@code ^(\d+)(.*)}. This will match any number with an arbitrary
     * textual suffix. An example for such a String would be
     * <p>
     * <tt>50923-my-first-blog-post</tt>
     */
    public static class IdWithTextParameterValueConverter implements ParameterValueConverter<IdWithText> {
        private final static Pattern PATTERN = Pattern.compile("^(\\d+)(.*)");
        public final static IdWithTextParameterValueConverter INSTANCE = new IdWithTextParameterValueConverter();

        @Override
        public String convertToString(IdWithText value) {
            if (value == null || value.getId() == null) {
                return "";
            }

            return value.getId().toString() + (value.getText() == null ? "" : value.getText());
        }

        @Override
        public IdWithText convertToValue(String valueAsString) throws ParameterValueConversionException {
            Matcher m = PATTERN.matcher(valueAsString);
            IdWithTextImpl result = new IdWithTextImpl();
            if (m.find()) {
                try {
                    result.setId(Long.valueOf(m.group(1)));
                } catch (NumberFormatException e) {
                    throw new ParameterValueConversionException(e);
                }
                result.setText(m.group(2));

            }
            return result;
        }
    }
}

@@ -4312,16 +4312,22 @@
 ToValue(
+final 
 String v
@@ -4395,16 +4395,22 @@
         
+final 
 Matcher 
@@ -4449,32 +4449,38 @@
 g);%0A            
+final 
 IdWithTextImpl r
@@ -4644,16 +4644,22 @@
  catch (
+final 
 NumberFo
@@ -4807,16 +4807,46 @@
 up(2));%0A
+                return result;
 %0A       
@@ -4843,32 +4843,39 @@
 t;%0A            %7D
+ else %7B
 %0A            ret
@@ -4867,38 +4867,88 @@
             
-return result;
+    throw new ParameterValueConversionException();%0A            %7D
 %0A        %7D%0A 

Throw exception if regex doesn't match