/*  Copyright (C) 2003-2011 JabRef contributors.
    This program 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.

    This program 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 this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref;

import net.sf.jabref.gui.MainTableFormat;

import java.text.Collator;
import java.text.ParseException;
import java.text.RuleBasedCollator;
import java.util.Comparator;
import java.util.Locale;

/**
 * 
 * A comparator for BibtexEntry fields
 * 
 * Initial Version:
 * 
 * @author alver
 * @version Date: Oct 13, 2005 Time: 10:10:04 PM To
 * 
 * Current Version:
 * 
 * @author $Author$
 * @version $Revision$ ($Date$)
 * 
 * TODO: Testcases
 * 
 */
public class FieldComparator implements Comparator<BibtexEntry> {

    private static Collator collator;

    static {
        try {
            collator = new RuleBasedCollator(
                        ((RuleBasedCollator) Collator.getInstance()).getRules()
                                .replaceAll("<'\u005f'", "<' '<'\u005f'"));
        } catch (ParseException e) {
            collator = Collator.getInstance();
        }
    }

	private String[] field;
    private String fieldName;

    boolean isNameField, isTypeHeader, isYearField, isMonthField, isNumeric;

	int multiplier;

	public FieldComparator(String field) {
		this(field, false);
	}

	public FieldComparator(String field, boolean reversed) {
        this.fieldName = field;
        this.field = field.split(MainTableFormat.COL_DEFINITION_FIELD_SEPARATOR);
		multiplier = reversed ? -1 : 1;
		isTypeHeader = this.field[0].equals(GUIGlobals.TYPE_HEADER);
        isNameField = (this.field[0].equals("author")
                || this.field[0].equals("editor"));
		isYearField = this.field[0].equals("year");
		isMonthField = this.field[0].equals("month");
        isNumeric = BibtexFields.isNumeric(this.field[0]);
    }

	public int compare(BibtexEntry e1, BibtexEntry e2) {
		Object f1, f2;

		if (isTypeHeader) {
			// Sort by type.
			f1 = e1.getType().getName();
			f2 = e2.getType().getName();
		} else {

			// If the field is author or editor, we rearrange names so they are
			// sorted according to last name.
			f1 = getField(e1);
			f2 = getField(e2);
		}

		/*
		 * [ 1598777 ] Month sorting
		 * 
		 * http://sourceforge.net/tracker/index.php?func=detail&aid=1598777&group_id=92314&atid=600306
		 */
		int localMultiplier = multiplier;
		if (isMonthField)
			localMultiplier = -localMultiplier;
		
		// Catch all cases involving null:
		if (f1 == null)
			return f2 == null ? 0 : localMultiplier;

		if (f2 == null)
			return -localMultiplier;

		// Now we now that both f1 and f2 are != null
		if (isNameField) {
			f1 = AuthorList.fixAuthorForAlphabetization((String) f1);
			f2 = AuthorList.fixAuthorForAlphabetization((String) f2);
		} else if (isYearField) {
			/*
			 * [ 1285977 ] Impossible to properly sort a numeric field
			 * 
			 * http://sourceforge.net/tracker/index.php?func=detail&aid=1285977&group_id=92314&atid=600307
			 */
			f1 = Util.toFourDigitYear((String) f1);
			f2 = Util.toFourDigitYear((String) f2);
		} else if (isMonthField) {
			/*
			 * [ 1535044 ] Month sorting
			 * 
			 * http://sourceforge.net/tracker/index.php?func=detail&aid=1535044&group_id=92314&atid=600306
			 */
			f1 = new Integer(Util.getMonthNumber((String)f1));			
			f2 = new Integer(Util.getMonthNumber((String)f2));
		}

        if (isNumeric) {
            Integer i1 = null, i2 = null;
            try {
                i1 = Integer.parseInt((String)f1);
            } catch (NumberFormatException ex) {
                // Parsing failed.
            }

            try {
                i2 = Integer.parseInt((String)f2);
            } catch (NumberFormatException ex) {
                // Parsing failed.
            }

            if (i2 != null && i1 != null) {
                // Ok, parsing was successful. Update f1 and f2:
                f1 = i1;
                f2 = i2;
            } else if (i1 != null) {
                // The first one was parseable, but not the second one.
                // This means we consider one < two
                f1 = i1;
                f2 = new Integer(i1.intValue()+1);
            } else if (i2 != null) {
                // The second one was parseable, but not the first one.
                // This means we consider one > two
                f2 = i2;
                f1 = new Integer(i2.intValue()+1);
            }
            // Else none of them were parseable, and we can fall back on comparing strings.    
        }

        int result = 0;
		if ((f1 instanceof Integer) && (f2 instanceof Integer)) {
			result = (((Integer) f1).compareTo((Integer) f2));
		} else if (f2 instanceof Integer) {
			Integer f1AsInteger = new Integer(f1.toString());
			result = -((f1AsInteger).compareTo((Integer) f2));
		} else if (f1 instanceof Integer) {
			Integer f2AsInteger = new Integer(f2.toString());
			result = -(((Integer) f1).compareTo(f2AsInteger));
		} else {
			String ours = ((String) f1).toLowerCase(), theirs = ((String) f2).toLowerCase();
            result = collator.compare(ours, theirs);//ours.compareTo(theirs);
		}

		return result * localMultiplier;
	}

    private Object getField(BibtexEntry entry) {
        for (int i = 0; i < field.length; i++) {
            Object o = entry.getField(field[i]);
            if (o != null)
                return o;
        }
        return null;
    }

    /**
	 * Returns the field this Comparator compares by.
	 * 
	 * @return The field name.
	 */
	public String getFieldName() {
		return fieldName;
	}
}
