/*****************************************************************************
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version
 * 1.0 (the "License"). You may not use this file except in compliance with
 * the License. A copy of the License is available at http://www.sun.com/
 *
 * The Original Code is the CVS Client Library.
 * The Initial Developer of the Original Code is Thomas Singer.
 * Portions created by Thomas Singer Copyright (C) 2001.
 * All Rights Reserved.
 *
 * Contributor(s): Thomas Singer, Milos Kleint
 *****************************************************************************/
package org.netbeans.lib.cvsclient.util;

import java.util.*;

/**
 * @author Thomas Singer
 */
public final class SimpleStringPattern
		implements IStringPattern {

	private static final char MATCH_EACH = '*';
	private static final char MATCH_ONE = '?';

	private final List<SubPattern> subPatterns = new LinkedList<SubPattern>();

	/**
	 * Creates a SimpleStringPattern for the specified definition.
	 * The definition might contain two special characters ('*' and '?').
	 */
	public SimpleStringPattern(String definition) {
		splitInSubPattern(definition);
	}

	/**
	 * Returns whether the specified string matches thiz pattern.
	 */
	public boolean doesMatch(String string) {
		int index = 0;
		SubPattern subPattern = null;
		for (Iterator<SubPattern> it = subPatterns.iterator(); it.hasNext();) {
			subPattern = it.next();
			index = subPattern.doesMatch(string, index);
			if (index < 0) {
				return false;
			}
		}

		if (index == string.length()) {
			return true;
		}
		if (subPattern == null) {
			return false;
		}
		return subPattern.checkEnding(string);
	}

	private void splitInSubPattern(String definition) {
		char prevSubPattern = ' ';

		int prevIndex = 0;
		for (int index = 0; index >= 0;) {
			prevIndex = index;

			index = definition.indexOf(MATCH_EACH, prevIndex);
			if (index >= 0) {
				final String match = definition.substring(prevIndex, index);
				addSubPattern(match, prevSubPattern);
				prevSubPattern = MATCH_EACH;
				index++;
				continue;
			}
			index = definition.indexOf(MATCH_ONE, prevIndex);
			if (index >= 0) {
				final String match = definition.substring(prevIndex, index);
				addSubPattern(match, prevSubPattern);
				prevSubPattern = MATCH_ONE;
				index++;
			}
		}
		final String match = definition.substring(prevIndex);
		addSubPattern(match, prevSubPattern);
	}

	private void addSubPattern(String match, char subPatternMode) {
		final SubPattern subPattern;
		switch (subPatternMode) {
		case MATCH_EACH:
			subPattern = new MatchEachCharPattern(match);
			break;
		case MATCH_ONE:
			subPattern = new MatchOneCharPattern(match);
			break;
		default:
			subPattern = new MatchExactSubPattern(match);
			break;
		}

		subPatterns.add(subPattern);
	}

	@Override
	public String toString() {
		final StringBuffer buffer = new StringBuffer();
		for (SubPattern subPattern : subPatterns) {
			buffer.append(subPattern.toString());
		}
		return buffer.toString();
	}

	private abstract static class SubPattern {
		protected final String match;

		protected SubPattern(String match) {
			this.match = match;
		}

		/**
		 * @param string ... the whole string to compile for matching
		 * @param index  ... the index in string where this' compile should begin
		 * @return ... if successful the next compile-position, if not -1
		 */
		public abstract int doesMatch(String string, int index);

		public boolean checkEnding(String string) {
			return false;
		}
	}

	private static class MatchExactSubPattern extends SubPattern {
		public MatchExactSubPattern(String match) {
			super(match);
		}

		@Override
		public int doesMatch(String string, int index) {
			if (!string.startsWith(match, index)) {
				return -1;
			}
			return index + match.length();
		}

		@Override
		public String toString() {
			return match;
		}
	}

	private static final class MatchEachCharPattern extends SubPattern {
		private MatchEachCharPattern(String match) {
			super(match);
		}

		@Override
		public int doesMatch(String string, int index) {
			final int matchIndex = string.indexOf(match, index);
			if (matchIndex < 0) {
				return -1;
			}
			return matchIndex + match.length();
		}

		@Override
		public boolean checkEnding(String string) {
			return string.endsWith(match);
		}

		@Override
		public String toString() {
			return MATCH_EACH + match;
		}
	}

	private static final class MatchOneCharPattern extends MatchExactSubPattern {
		private MatchOneCharPattern(String match) {
			super(match);
		}

		@Override
		public int doesMatch(String string, int index) {
			index++;
			if (string.length() < index) {
				return -1;
			}
			return super.doesMatch(string, index);
		}

		@Override
		public String toString() {
			return MATCH_ONE + match;
		}
	}
}
