/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool
          with fuzzy matching, translation memory, keyword search,
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2020 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT 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 3 of the License, or
 (at your option) any later version.

 OmegaT 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, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.gui.glossary;

import java.util.Comparator;
import java.text.Collator;

import org.omegat.util.OStrings;
import org.omegat.core.Core;
import org.omegat.core.glossaries.GlossaryEntryStore;

/**
 * Sort criteria
 * @author Thomas Cordonnier
 */
public enum GlossarySortCriteria {
	ALPHABETIC("TF_OPTIONSMENU_GLOSSARY_SORT_ALPHA", 'A'),
	PRIORITY("TF_OPTIONSMENU_GLOSSARY_SORT_PRIO", 'P'),
	LENGTH("TF_OPTIONSMENU_GLOSSARY_SORT_LEN", 'L'),
	CASE("TF_OPTIONSMENU_GLOSSARY_SORT_CASE", 'C');
	
	private GlossarySortCriteria(String tra, char sName) {
		this.translated = tra; this.shortName = sName;
	}
	
	public static GlossarySortCriteria forChar(char ch) {
		switch (ch) {
			case 'a': case 'A': return ALPHABETIC;
			case 'l': case 'L': return LENGTH;
			case 'p': case 'P': return PRIORITY;
			case 'c': case 'C': return CASE;
			default: return null;
		}
	}
	
	public Comparator<GlossaryEntryStore> buildComparator(boolean src, boolean desc) {
		if (desc) {
			final Comparator<GlossaryEntryStore> cmp = buildComparator(src, false);
			return (a, b) -> cmp.compare(b, a);
		}
		switch (this) {
			case PRIORITY: return (o1, o2) -> {
				int p1 = o1.getPriority() ? 1 : 2, p2 = o2.getPriority() ? 1 : 2;
				return p1 - p2;
			};
			case ALPHABETIC: 
				if (src) {
					final Collator cmp = Collator.getInstance (Core.getProject().getProjectProperties().getSourceLanguage().getLocale());    
					return (a, b) -> cmp.compare(a.getSrcText().toLowerCase(), b.getSrcText().toLowerCase()); // use locale, but case insensitive
				} else {
					final Collator cmp = Collator.getInstance (Core.getProject().getProjectProperties().getTargetLanguage().getLocale());    
					return (a, b) -> cmp.compare(a.getLocText().toLowerCase(), b.getLocText().toLowerCase()); // use locale, but case insensitive
				}
			case CASE: 
				if (src) return (a, b) -> caseCompare(a.getSrcText(), b.getSrcText());
				else return (a, b) -> caseCompare(a.getLocText(), b.getLocText());				
			case LENGTH: 
				if (src) return (a, b) -> Integer.compare(a.getSrcText().length(), b.getSrcText().length());
				else return (a, b) -> Integer.compare(a.getLocText().length(), b.getLocText().length());
		}
		return null;
	}
	
	/** 
	 * A comparator which puts any upercase word before any lowercase, independently from letters : C &lt; X &lt; a &lt; d <br/>
	 * If both are upper or both are lower, return 0 so that we can combine with an alphabetic order
	 **/
	public static int caseCompare(String a, String b) {
		for (int i = 0 ; ; i++) {
			if (i >= a.length()) return 0; if (i >= b.length()) return 0;
						
			int ia = Character.isLowerCase(a.charAt(i)) ? 2 : Character.isUpperCase(a.charAt(i)) ? 1 : 0;
			int ib = Character.isLowerCase(b.charAt(i)) ? 2 : Character.isUpperCase(b.charAt(i)) ? 1 : 0;
			if (ia != ib) return ia - ib;
		}
	}
	
	public static Comparator<GlossaryEntryStore> buildSourceComparator(String spec) {
		char[] specArray = spec.substring(0,spec.indexOf(";")).toCharArray(); final Comparator<GlossaryEntryStore>[] comps = new Comparator[specArray.length];
		for (int i = 0; i < specArray.length; i++) comps[i] = forChar(specArray[i]).buildComparator(true, Character.isLowerCase(specArray[i]));
		return (a,b) -> {
			for (int i = 0; i < specArray.length; i++) {
				int cmp = comps[i].compare(a,b); if (cmp != 0) return cmp;
			}
			return 0;
		};
	}

	public static Comparator<GlossaryEntryStore> buildTargetComparator(String spec) {
		char[] specArray = spec.substring(spec.indexOf(";") + 1).toCharArray(); final Comparator<GlossaryEntryStore>[] comps = new Comparator[specArray.length];
		for (int i = 0; i < specArray.length; i++) comps[i] = forChar(specArray[i]).buildComparator(false, Character.isLowerCase(specArray[i]));
		return (a,b) -> {
			for (int i = 0; i < specArray.length; i++) {
				int cmp = comps[i].compare(a,b); if (cmp != 0) return cmp;
			}
			return 0;
		};
	}

	public static Comparator<GlossaryEntryStore> buildFullComparator(String spec) {
		char[] specArray = spec.toCharArray(); final Comparator<GlossaryEntryStore>[] comps = new Comparator[specArray.length - 1];
		for (int i = 0; i < spec.indexOf(";"); i++) comps[i] = forChar(specArray[i]).buildComparator(true, Character.isLowerCase(specArray[i]));
		for (int i = spec.indexOf(";") + 1; i < spec.length(); i++) comps[i - 1] = forChar(specArray[i]).buildComparator(false, Character.isLowerCase(specArray[i]));
		return (a,b) -> {
			for (int i = 0; i <= 5; i++) {
				int cmp = comps[i].compare(a,b); if (cmp != 0) return cmp;
			}
			return 0;
		};
	}
	
	public final String translated;
	public final char shortName;
	
	/** Class to combine GlossarySortCriteria with ascending/descending specification **/
	public static class WithDirection {
		public GlossarySortCriteria criteria;
		public boolean desc = false;
		
		public WithDirection(GlossarySortCriteria criteria, boolean desc) { this.criteria = criteria; this.desc = desc; }
		
		public String toString() { return (desc ? "-" : "+") + OStrings.getString(criteria.translated); }
		
		public char shortName() { if (desc) return Character.toLowerCase(criteria.shortName); else return criteria.shortName; }
	}
}
