package industries.doublehelix.furrynotemanager.gui;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;

import javax.swing.table.AbstractTableModel;

import industries.doublehelix.furrynotemanager.Note;
import industries.doublehelix.furrynotemanager.Site;
import industries.doublehelix.furrynotemanager.User;

public class NoteTableModel extends AbstractTableModel {
	private static final long serialVersionUID = 688397758475300184L;
	private ArrayList<Note> list;
	private ArrayList<ColumnType> columns = new ArrayList<>();

	private static DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL);
	
	public enum ColumnType {
		USER("User"), SITE("Site"), FROM("From"), TO("To"), TITLE("Title"), TEXT("Message"), DATE("Date"), NOTEID("Note ID");
		private String title;
		ColumnType(String title) {
			this.title = title;
		}
		public String toString() {
			return title;
		}
	}
	
	public NoteTableModel(TreeSet<Note> notes, boolean displayUserColumn, boolean displaySiteColumn) {
		ArrayList<Note> list = new ArrayList<>(notes);
		Collections.sort(list, new Comparator<Note>() {
			public int compare(Note o1, Note o2) {
				if (o1 == null && o2 == null) return 0;
				if (o1 == null || o2 == null) return o1 == null ? -1 : 1;
				
				User u1 = o1.getUser();
				User u2 = o2.getUser();
				Site s1 = u1.getSite();
				Site s2 = u2.getSite();
				
				if (s1.getSiteId() != s2.getSiteId()) {
					return s1.getName().compareTo(s2.getName());
				}
				if (u1.getUserId() != u2.getUserId()) {
					return u1.getName().compareTo(u2.getName());
				}
				// smaller id = earlier note, no need to compare date. *-1 because we want biggest first
				return Integer.compare(o1.getNoteId(), o2.getNoteId()) * -1;
			}
		});
		init(list, displayUserColumn, displaySiteColumn);
	}
	
	public NoteTableModel(ArrayList<Note> notes, boolean displayUserColumn, boolean displaySiteColumn) {
		init(notes, displayUserColumn, displaySiteColumn);
	}
	
	private void init(ArrayList<Note> notes, boolean displayUserColumn, boolean displaySiteColumn) {
		this.list = notes;
		
		for (ColumnType type : ColumnType.values()) {
			if (type == ColumnType.USER && !displayUserColumn) continue;
			if (type == ColumnType.SITE && !displaySiteColumn) continue;
			this.columns.add(type);
		}
	}

	@Override
	public int getRowCount() {
		return list.size();
	}

	@Override
	public int getColumnCount() {
		return this.columns.size();
	}
	
	@Override
	public String getColumnName(int column) {
		if (column < 0 || column > getColumnCount()) throw new IllegalArgumentException("No such column: " + column);
		return this.columns.get(column).toString();
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		if (columnIndex < 0 || columnIndex > getColumnCount()) throw new IllegalArgumentException("No such column: " + columnIndex);
		if (rowIndex < 0 || rowIndex > getRowCount()) throw new IllegalArgumentException("No such row: " + rowIndex);
		
		ColumnType colType = this.columns.get(columnIndex);
		Note note = list.get(rowIndex);
		
		return new SortableColumnClass(note, colType);
		
//		switch (colType) {
//			case USER:
//				return note.getUser().getName();
//			case SITE:
//				return note.getUser().getSite().getName();
//			case FROM:
//				return note.getSender();
//			case TO:
//				return note.getReceiver();
//			case TITLE:
//				return note.getTitle();
//			case TEXT:
//				return note.getRawText();
//			case DATE:
//				return formatter.format(note.getDate().getTime());
//			case NOTEID:
//			default:
//				return note.getOriginalNoteId();
//		}
	}
	
	public Note getNoteAtRow(int rowId) {
		if (rowId < 0 || rowId > list.size()) return null;
		return list.get(rowId);
	}
	
	public ColumnType getColumnType(int columnId) {
		return this.columns.get(columnId);
	}
	
	@Override
	public Class<?> getColumnClass(int columnIndex) {
		// same class for every column
		return SortableColumnClass.class;
	}

	class SortableColumnClass implements Comparable<SortableColumnClass> {
		private Note note;
		private ColumnType column;
		
	
		public SortableColumnClass(Note note, ColumnType colType) {
			this.note = note;
			this.column = colType;
		}
		
		@Override
		public String toString() {
			switch (column) {
				case USER:
					return note.getUser().getName();
				case SITE:
					return note.getUser().getSite().getName();
				case FROM:
					return note.getSender() == null ? note.getUser().getName() : note.getSender();
				case TO:
					return note.getReceiver() == null ? note.getUser().getName() : note.getReceiver();
				case TITLE:
					return note.getTitle();
				case TEXT:
					return note.getRawText();
				case DATE:
					return formatter.format(note.getDate().getTime());
				case NOTEID:
				default:
					return Integer.toString(note.getOriginalNoteId());
			}
		}
		
		@Override
		public int compareTo(SortableColumnClass o) {
			if (this.column != o.column) throw new IllegalArgumentException("Trying to compare different colums, " + this.column + " and " + o.column);
			
			Note oNote = o.note;
			User myUser = note.getUser();
			User oUser = oNote.getUser();
			Site mySite = myUser == null ? null : myUser.getSite();
			Site oSite = oUser == null ? null : oUser.getSite();
			
			// sort by alphabet save for when it's the same (don't wanna throw together same-named users on different sites)
			int retVal;
			switch (column) {
				case USER:
					retVal = note.getUser().getName().compareTo(oNote.getUser().getName());
					if (retVal != 0) return retVal;
					return Integer.compare(note.getUser().getUserId(), oNote.getUser().getUserId());
				case SITE:
					retVal = mySite.getName().compareTo(oSite.getName());
					if (retVal != 0) return retVal;
					return Integer.compare(mySite.getSiteId(), oSite.getSiteId());
				case FROM:
					String from = note.getSender();
					if (from == null) from = note.getUser().getName();
					String oFrom = oNote.getSender();
					if (oFrom == null) oFrom = oNote.getUser().getName();
					retVal = from.compareToIgnoreCase(oFrom);
					if (retVal != 0) return retVal;
					// dunno user id here, so we do the same as with the site
					return Integer.compare(mySite.getSiteId(), oSite.getSiteId());
				case TO:
					String to = note.getReceiver();
					if (to == null) to = note.getUser().getName();
					String oTo = oNote.getReceiver();
					if (oTo == null) oTo = oNote.getUser().getName();
					retVal = to.compareToIgnoreCase(oTo);
					if (retVal != 0) return retVal;
					// dunno user id here, so we do the same as with the site
					return Integer.compare(mySite.getSiteId(), oSite.getSiteId());
				case TITLE:
					if (note.getTitle() == null && oNote.getTitle() == null) return 0;
					if (note.getTitle() == null && oNote.getTitle() != null) return 1;
					if (note.getTitle() != null && oNote.getTitle() == null) return -1;
					return note.getTitle().compareToIgnoreCase(oNote.getTitle());
				case TEXT:
					if (note.getRawText() == null && oNote.getRawText() == null) return 0;
					if (note.getRawText() == null && oNote.getRawText() != null) return 1;
					if (note.getRawText() != null && oNote.getRawText() == null) return -1;
					return note.getRawText().compareToIgnoreCase(oNote.getRawText());
				case DATE:
					if (note.getDate() == null && oNote.getDate() == null) return 0;
					if (note.getDate() == null && oNote.getDate() != null) return 1;
					if (note.getDate() != null && oNote.getDate() == null) return -1;
					return note.getDate().compareTo(oNote.getDate());
				case NOTEID:
				default:
					return Integer.compare(note.getOriginalNoteId(), oNote.getOriginalNoteId());
			}
		}
		
	}
}
