/*  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 java.util.Comparator;

/**
 * This implementation of Comparator takes care of most of the details of sorting BibTeX entries in JabRef.
 * It is structured as a node in a linked list of comparators, where each node can contain a link to a
 * new comparator that decides the ordering (by recursion) if this one can't find a difference. The next
 * node, if any, is given at construction time, and an arbitrary number of nodes can be included.
 * If the entries are equal by this comparator, and there is no next entry, the entries' unique IDs will
 * decide the ordering. Consequently, this comparator can never return 0 unless the entries are the same
 * object.
 */
public class EntryComparator implements Comparator<BibtexEntry> {

    String sortField;
    boolean descending, binary=false, numeric;
    Comparator<BibtexEntry> next;

    public EntryComparator(boolean binary, boolean desc, String field, Comparator<BibtexEntry> next) {
        this.binary = binary;
        this.sortField = field;
        this.descending = desc;
        this.next = next;
        this.numeric = BibtexFields.isNumeric(sortField);
    }

    public EntryComparator(boolean binary, boolean desc, String field) {
        this.binary = binary;
        this.sortField = field;
        this.descending = desc;
        this.next = null;
        this.numeric = BibtexFields.isNumeric(sortField);
    }


    public int compare(BibtexEntry e1, BibtexEntry e2) throws ClassCastException {

    if (e1 == e2)
        return 0;

    //Util.pr("EntryComparator: "+e1+" : "+e2);
    Object f1 = e1.getField(sortField),
        f2 = e2.getField(sortField);

    if (binary) {
        // We just separate on set and unset fields:
        if (f1 != null)
            return (f2 == null) ? -1 :
                    (next != null ? next.compare(e1, e2) : idCompare(e1, e2));
        else
            return (f2 == null) ? (next != null ? next.compare(e1, e2) : idCompare(e1, e2))
                    : 1;
    }

    // If the field is author or editor, we rearrange names so they are
    // sorted according to last name.
    if (sortField.equals("author") || sortField.equals("editor")) {
        if (f1 != null)
        f1 = AuthorList.fixAuthorForAlphabetization((String)f1).toLowerCase();
                //ImportFormatReader.fixAuthor_lastNameFirst((String)f1);
        if (f2 != null)
        f2 = AuthorList.fixAuthorForAlphabetization((String)f2).toLowerCase();
                //ImportFormatReader.fixAuthor_lastNameFirst((String)f2);

    } else if (sortField.equals(GUIGlobals.TYPE_HEADER)) {
          // Sort by type.
          f1 = e1.getType().getName();
          f2 = e2.getType().getName();
        }
    else if (numeric) {
        try {
            Integer i1 = Integer.parseInt((String)f1);
            Integer i2 = Integer.parseInt((String)f2);
            // Ok, parsing was successful. Update f1 and f2:
            f1 = i1;
            f2 = i2;
        } catch (NumberFormatException ex) {
            // Parsing failed. Give up treating these as numbers.
            // TODO: should we check which of them failed, and sort based on that?
        }
    }

    if ((f1 == null) && (f2 == null)) return (next != null ? next.compare(e1, e2) : idCompare(e1, e2));
	if ((f1 != null) && (f2 == null)) return -1;
	if ((f1 == null) && (f2 != null)) return 1;

	int result = 0;

	//String ours = ((String)e1.getField(sortField)).toLowerCase(),
	//    theirs = ((String)e2.getField(sortField)).toLowerCase();
	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();
        int comp = ours.compareTo(theirs);
		result = -comp;
	}
	if (result != 0)
	    return (descending ? result : -result); // Primary sort.
	if (next != null)
	    return next.compare(e1, e2); // Secondary sort if existent.
	else {

        return idCompare(e1, e2); // If still equal, we use the unique IDs.
    }
    }

    private int idCompare(BibtexEntry b1, BibtexEntry b2) {
    return ((b1.getId())).compareTo((b2.getId()));
    }

}
