public class ClipboardExporter extends Job {

	private Clipboard clipboard;
	private File tmpDir;
	private Session session;
	private String username;
	private boolean preferMultiframe;
	private String niftiMultiframeScript;

	public ClipboardExporter(Clipboard clipboard, File tmpDir, Session session, String username, boolean preferMultiframe, String niftiMultiframeScript) {
		this.clipboard = clipboard;
		this.tmpDir = tmpDir;
		this.session = session;
		this.username = username;
		this.preferMultiframe = preferMultiframe;
		this.niftiMultiframeScript = niftiMultiframeScript;
		addExport();
	}

	@Override
	public void doJob() throws Exception {
		for (GenericModel object : clipboard.getObjects()) {
			if (object instanceof Study) {
				Study study = (Study) object;
				Download.study(study, tmpDir, Format.dcm, preferMultiframe, niftiMultiframeScript);
			} else if (object instanceof Series) {
				Series series = (Series) object;
				Download.series(series, tmpDir, Format.dcm, preferMultiframe, niftiMultiframeScript);
			}
		}
		File dest = getDest();
		getDest().getParentFile().mkdirs();
		Files.copyDir(tmpDir, dest);
	}

	private void addExport() {
		String exports = session.get(Application.EXPORTS);
		if (exports == null) {
			exports = "";
		}
		String name;
		search:
			for (int i = 1; ; i++) {
				name = String.format("%s-%s-%s", username, new SimpleDateFormat("yyyyMMdd").format(new Date()), i);
				if (!exports.isEmpty()) {
					for (String export : exports.split(",")) {
						if (name.equals(export.split(":")[1])) {
							continue search;
						}
					}
				}
				break;
			}
		String fullName = String.format("%s:%s", tmpDir.getName(), name);
		session.put(Application.EXPORTS, exports.isEmpty() ? fullName : StringUtils.join(ArrayUtils.add(exports.split(","), fullName), ','));
	}

	private File getDest() {
		String exports = session.get(Application.EXPORTS);
		for (String export : Arrays.asList(exports.split(","))) {
			if (tmpDir.getName().equals(export.split(":")[0])) {
				return new File(new File(Properties.getExports(), export.split(":")[1].split("-")[0]), export.split(":")[1]);
			}
		}
		return null;
	}

	public static List<File> getExports(Session session) {
		String exports = session.get(Application.EXPORTS);
		List<File> archives = new ArrayList<File>();
		if (exports != null) {
			List<String> exportList = new ArrayList(Arrays.asList(exports.split(",")));
			for (Iterator<String> i = exportList.iterator(); i.hasNext();) {
				String export = i.next();
				File tmpDir = new File(Properties.getDownloads(), export.split(":")[0]);
				if (tmpDir.exists()) {
					archives.add(new File(new File(Properties.getExports(), export.split(":")[1].split("-")[0]), export.split(":")[1]));
				} else {
					i.remove();
				}
			}
			if (exportList.isEmpty()) {
				session.remove(Application.EXPORTS);
			} else {
				session.put(Application.EXPORTS, StringUtils.join(exportList, ","));
			}
		}
		return archives;
	}

} public class Downloader extends Job<File> {

	public static enum Format {
		dcm, //DICOM
		nii, //NIfTI
		img  //Analyze
	}

	private Item[] items;
	private Format format;
	private File tmpDir;
	private boolean preferMultiframe;
	private String niftiMultiframeScript;

	public Downloader(Format format, File tmpDir, boolean preferMultiframe, String niftiMultiframeScript, Item... items) {
		this.format = format;
		this.tmpDir = tmpDir;
		this.preferMultiframe = preferMultiframe;
		this.niftiMultiframeScript = niftiMultiframeScript;
		this.items = items;
	}

	@Override
	public File doJobWithResult() throws Exception {
		for (Item item : items) {
			DomainModel model = item.getModel();
			if (model instanceof Study) {
				Download.study((Study) model, tmpDir, format, preferMultiframe, niftiMultiframeScript);
			} else if (model instanceof Series) {
				Download.series((Series) model, tmpDir, format, preferMultiframe, niftiMultiframeScript);
			}
			JPA.em().clear();
		}
		return tmpDir.listFiles()[0];
	}

} public static enum Format {
	dcm, //DICOM
	nii, //NIfTI
	img  //Analyze
} @MappedSuperclass
public abstract class DomainModel extends GenericModel {

	@Id
	public long pk;

} @Entity
public class Instance extends DomainModel {

	public String sop_iuid;
	public String sop_cuid;
	public byte[] inst_attrs;
	public String inst_no;
	@ManyToOne
	@JoinColumn(name="series_fk")
	public Series series;
	@OneToMany(mappedBy = "instance")
	public Set<Files> files;

	@Override
	public String toString() {
		return String.format("%s", pk);
	}
} @Entity
public class Person extends GenericModel {

	@Id
	public String username;
	public String password;
	public Role role = Role.Guest;
	@Lob
	public String clipboard;
	public Boolean preferMultiframe;
	public String niftiMultiframeScript;

	public Person(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		if (!password.startsWith("$2a$")) {
			this.password = BCrypt.hashpw(password, BCrypt.gensalt());
		}
	}

	@PostUpdate
	public void postUpdate() {
		Cache.delete(username);
	}

	@Override
	public String toString() {
		return String.format("%s (%s)", username, role);
	}

	public static enum Role {
		Researcher, Administrator, Visitor, Guest;
	}

} public static enum Role {
	Researcher, Administrator, Visitor, Guest;
} @Entity
public class Project extends Model {

	@Index(name="name")
	public String name;
	@OneToMany(mappedBy = "project")
	public Set<ProjectAssociation> projectAssociations;

	public Project(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return name;
	}
} @Entity
public class ProjectAssociation extends Model {

	@Index(name="participationid")
	public String participationID;
	@ManyToOne
	public Project project;
	@ManyToOne
	public Study study;

	public ProjectAssociation(Project project, Study study) {
		this.project = project;
		this.study = study;
	}

} @Entity
public class Series extends DomainModel {

	public String series_no;
	public String series_iuid;
	public String station_name;
	public String series_desc;
	public String modality;
	@ManyToOne
	@JoinColumn(name="study_fk")
	public Study study;
	@OneToMany(mappedBy = "series")
	public Set<Instance> instances;

	public String toDownloadString() {
		return String.format("%s_%s_%s", study.toDownloadString(), series_no, series_desc).replaceAll("\\W+", "");
	}

} @Entity
//@NamedNativeQuery(name = "nativeSQL", query = "select * from study", resultClass = Study.class)
public class Study extends DomainModel {

	public String study_id;
	public String study_desc;
	public Date study_datetime;
	public String study_custom1;
	public Date created_time;
	@ManyToOne
	@JoinColumn(name="patient_fk")
	public Patient patient;
	@OneToMany(mappedBy = "study")
	public Set<Series> series;
	@OneToMany(mappedBy = "study")
	public Set<ProjectAssociation> projectAssociations;

	public List<ProjectAssociation> getProjectAssociations() {
		return ProjectAssociation.find("select projectAssociation from Project project, in(project.projectAssociations) projectAssociation where projectAssociation.study = ?", this).fetch();
	}

	public String toDownloadString() {
		List<String> parts = new ArrayList<String>();
		List<ProjectAssociation> projectAssociations = getProjectAssociations();
		if (projectAssociations.size() > 0) {
			ProjectAssociation projectAssociation = projectAssociations.iterator().next();
			if (projectAssociation.participationID != null && !projectAssociation.participationID.isEmpty()) {
				parts.add(projectAssociation.participationID);
			}
			parts.add(projectAssociation.project.name);
		}
		parts.add(new SimpleDateFormat("yyyyMMddHHmm").format(study_datetime));
		String station_name = getStation_name();
		if (station_name != null) {
			parts.add(station_name);
		}
		return StringUtils.join(parts, "_").replaceAll("\\W+", "");
	}

	public String getStation_name() {
		Series scannedSeries = (Series) CollectionUtils.find(series, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return ((Series) arg0).station_name != null;
			}
		});
		return scannedSeries == null ? null : scannedSeries.station_name;
	}

} public class Mail extends Mailer {

	public static void exception(Request request, Session session, Throwable exception) {
		setSubject(String.format("[%s] Exception", Play.configuration.getProperty("application.name")));
		addRecipient(Properties.getString("mail.to"));
		setFrom(String.format("%s <%s>", Properties.getString("application.name"), Properties.getString("mail.from")));
		send(request, session, exception);
	}

} public class Clipboard {

	static final String SEPARATOR = ",";

	private Set<Item> items = new HashSet<Item>();

	public Clipboard(String clipboard) {
		if (clipboard != null && !clipboard.isEmpty()) {
			for (String item : clipboard.split(SEPARATOR)) {
				items.add(new Item(item));
			}
		}
	}

	public void add(String type, long pk) throws ClassNotFoundException {
		items.add(new Item(type, pk));
	}

	public void remove(String type, long pk) throws ClassNotFoundException {
		items.remove(new Item(type, pk));
	}

	public void clear() {
		items.clear();
	}

	public Set<GenericModel> getObjects() throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Set<GenericModel> objects = new HashSet<GenericModel>();
		for (Item item : items) {
			GenericModel object = item.getModel();
			if (!(object instanceof Series) || !objects.contains(((Series) object).study)) {
				objects.add(object);
			}
		}
		return objects;
	}

	@Override
	public String toString() {
		return StringUtils.join(items, SEPARATOR);
	}

	public static class Item {

		private static final String DELIMITER = ":";
		private static final List types = Arrays.asList(Study.class, Series.class);

		public Class type;
		public long pk;

		public Item(String item) {
			String[] parts = item.split(DELIMITER);
			type = (Class) types.get(Integer.parseInt(parts[0]));
			pk = Long.parseLong(parts[1]);
		}

		private Item(Class type, long pk) {
			this.type = type;
			this.pk = pk;
		}

		public Item(String type, long pk) throws ClassNotFoundException {
			this(Class.forName(String.format("models.%s", type)), pk);
		}

		public Item(DomainModel object) {
			this(object.getClass(), object.pk);
		}

		public static Item[] serialize(List<DomainModel> objects) {
			Item[] items = new Item[objects.size()];
			for (int i = 0; i < objects.size(); i++) {
				DomainModel object = objects.get(i);
				items[i] = new Item(object);
			}
			return items;
		}

		public DomainModel getModel() throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
			return (DomainModel) type.getMethod("findById", Object.class).invoke(null, pk);
		}

		@Override
		public int hashCode() {
			return HashCodeBuilder.reflectionHashCode(this);
		}

		@Override
		public boolean equals(Object obj) {
			return EqualsBuilder.reflectionEquals(this, obj);
		}

		@Override
		public String toString() {
			return String.format("%s%s%s", types.indexOf(type), DELIMITER, pk);
		}
	}

} public static class Item {

	private static final String DELIMITER = ":";
	private static final List types = Arrays.asList(Study.class, Series.class);

	public Class type;
	public long pk;

	public Item(String item) {
		String[] parts = item.split(DELIMITER);
		type = (Class) types.get(Integer.parseInt(parts[0]));
		pk = Long.parseLong(parts[1]);
	}

	private Item(Class type, long pk) {
		this.type = type;
		this.pk = pk;
	}

	public Item(String type, long pk) throws ClassNotFoundException {
		this(Class.forName(String.format("models.%s", type)), pk);
	}

	public Item(DomainModel object) {
		this(object.getClass(), object.pk);
	}

	public static Item[] serialize(List<DomainModel> objects) {
		Item[] items = new Item[objects.size()];
		for (int i = 0; i < objects.size(); i++) {
			DomainModel object = objects.get(i);
			items[i] = new Item(object);
		}
		return items;
	}

	public DomainModel getModel() throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		return (DomainModel) type.getMethod("findById", Object.class).invoke(null, pk);
	}

	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}

	@Override
	public boolean equals(Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}

	@Override
	public String toString() {
		return String.format("%s%s%s", types.indexOf(type), DELIMITER, pk);
	}
} public class Dicom {

	public static String attribute(byte[] dataset, String tag) throws IOException {
		Dataset d = DcmObjectFactory.getInstance().newDataset();
		d.readFile(new ByteArrayInputStream(dataset), null, -1);
		return d.getString(Tags.forName(tag));
	}

	public static File file(Instance instance) {
		return new File(Properties.getArchive(), files(instance).filepath);
	}

	static Files files(Instance instance) {
		for (Files files : instance.files) {
			if (new File(Properties.getArchive(), files.filepath).exists()) {
				return files;
			}
		}
		return null;
	}

	public static File collate(Series series, boolean preferMultiframe) {
		File collated = new File(Properties.getCollations(), UUID.randomUUID().toString());
		collated.mkdir();
		for (Files files : Dicom.getFiles(series, preferMultiframe)) {
			play.libs.Files.copy(new File(Properties.getArchive(), files.filepath), new File(collated, files.toDownloadString()));
		}
		return collated;
	}

	public static Collection<Files> getFiles(Series series, boolean preferMultiframe) {
		Collection<Instance> instances = Dicom.singleFrames(series);
		{
			Instance instance = null;
			if (preferMultiframe || instances.isEmpty()) {
				instance = Dicom.multiFrame(series);
				if (instance != null) {
					instances = Collections.EMPTY_SET;
				}
			}
			if (instances.isEmpty()) {
				if (instance == null) {
					instance = Dicom.spectrogram(series);
				}
				instances = Collections.singleton(instance);
			}
		}
		List<Files> filesList = new ArrayList<Files>();
		for (Instance instance : instances) {
			Files files = files(instance);
			if (files == null) {
				Logger.warn("No Files found for Instance %s of Series %s", instance, series.toDownloadString());
				continue;
			}
			filesList.add(files);
		}
		return filesList;
	}

	//retrieve attributes that aren't in the table or blob by looking in the file
	public static Dataset dataset(File dicom) throws IOException {
		Dataset dataset = DcmObjectFactory.getInstance().newDataset();
		dataset.readFile(dicom, null, Tags.PixelData);
		return dataset;
	}

	public static Set<String> echoes(Dataset dataset) {
		Set<String> echoes = new LinkedHashSet<String>();
		if (dataset.contains(Tags.EchoTime)) {
			echoes.add(dataset.getString(Tags.EchoTime));
		} else {
			for (int i = 0; i < dataset.get(Tags.PerFrameFunctionalGroupsSeq).countItems(); i++) {
				boolean unseen = echoes.add(dataset.getItem(Tags.PerFrameFunctionalGroupsSeq, i).getItem(Tags.MREchoSeq).getString(Tags.EffectiveEchoTime));
				if (!unseen) {
					break;
				}
			}
		}
		return echoes;
	}

	private static final String dcmodify = new File(Properties.getString("dcmtk"), "bin/dcmodify").getPath();
	private static final Map<String, String> environment = new HashMap<String, String>() {{
		put("DCMDICTPATH", new File(Properties.getString("dcmtk"), "share/dcmtk/dicom.dic").getPath());
	}};
	private static final int[] tags = new int[] {
		Tags.PatientID,
		Tags.PatientName,
		Tags.PatientSex,
		Tags.PatientBirthDate,
		Tags.PatientAddress,
		Tags.ReferringPhysicianName,
		Tags.InstitutionName,
		Tags.StationName,
		Tags.ManufacturerModelName
	};
	public static void anonymise(File inFile, File outFile, String identifier) throws Exception {
		FileUtils.copyFile(inFile, outFile);
		String[] command = new String[3 + 2 * tags.length + 1];
		int i = 0;
		command[i++] = dcmodify;
		command[i++] = "-nb";
		command[i++] = "-imt";
		for (int tag : tags) {
			if (identifier != null && (tag == Tags.PatientID || tag == Tags.PatientName)) {
				command[i++] = "-m";
				command[i++] = String.format("%s=%s", Tags.toString(tag), identifier);
			} else {
				command[i++] = "-e";
				command[i++] = Tags.toString(tag);
			}
		}
		command[i++] = outFile.getPath();
		Util.exec(null, environment, command);
	}

	public static int numberOfFrames(Series series) throws IOException {
		Instance instance = multiFrame(series);
		if (instance != null) {
			Dataset d = DcmObjectFactory.getInstance().newDataset();
			d.readFile(new ByteArrayInputStream(instance.inst_attrs), null, -1);
			return d.getInteger(Tags.forName("NumberOfFrames"));
		}
		return singleFrames(series).size();
	}

	private enum CUID {
		MRImageStorage("1.2.840.10008.5.1.4.1.1.4"),
		EnhancedMRImageStorage("1.2.840.10008.5.1.4.1.1.4.1"),
		MRSpectroscopyStorage("1.2.840.10008.5.1.4.1.1.4.2"),
		PrivateSiemensCSANonImageStorage("1.3.12.2.1107.5.9.1");

		final String value;
		CUID(String value) {
			this.value = value;
		}
	}

	public static Instance multiFrame(Series series) {
		return (Instance) CollectionUtils.find(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return CUID.EnhancedMRImageStorage.value.equals(((Instance) arg0).sop_cuid);
			}
		});
	}

	public static Collection<Instance> singleFrames(Series series) {
		return CollectionUtils.select(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return CUID.MRImageStorage.value.equals(((Instance) arg0).sop_cuid);
			}
		});
	}

	public static boolean singleFrame(Series series) {
		return CollectionUtils.exists(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return CUID.MRImageStorage.value.equals(((Instance) arg0).sop_cuid);
			}
		});
	}

	public static Instance spectrogram(Series series) {
		return (Instance) CollectionUtils.find(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return Arrays.asList(CUID.MRSpectroscopyStorage.value, CUID.PrivateSiemensCSANonImageStorage.value).contains(((Instance) arg0).sop_cuid);
			}
		});
	}

	private static final List<String> renderable = Arrays.asList(CUID.MRImageStorage.value, CUID.EnhancedMRImageStorage.value);
	public static boolean renderable(Series series) {
		return CollectionUtils.exists(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return renderable.contains(((Instance) arg0).sop_cuid);
			}
		});
	}

	private static final List<String> downloadable = Arrays.asList(CUID.MRImageStorage.value, CUID.EnhancedMRImageStorage.value, CUID.MRSpectroscopyStorage.value, CUID.PrivateSiemensCSANonImageStorage.value);
	public static boolean downloadable(Series series) {
		return CollectionUtils.exists(series.instances, new Predicate() {
			@Override
			public boolean evaluate(Object arg0) {
				return downloadable.contains(((Instance) arg0).sop_cuid);
			}
		});
	}

	//	public static Dataset privateDataset(Dataset dataset) throws IOException {
	//		Dataset privateDataset;
	//		Dataset perFrameFunctionalGroupsSeq = dataset.getItem(Tags.PerFrameFunctionalGroupsSeq);
	//		if (perFrameFunctionalGroupsSeq.get(Tags.valueOf("(2005,140F)")).hasItems()) {
	//			privateDataset = perFrameFunctionalGroupsSeq.getItem(Tags.valueOf("(2005,140F)"));
	//		} else {
	//			byte[] buf = dataset.getItem(Tags.PerFrameFunctionalGroupsSeq).get(Tags.valueOf("(2005,140F)")).getDataFragment(0).array();
	//			privateDataset = DcmObjectFactory.getInstance().newDataset();
	//			privateDataset.readFile(new ByteArrayInputStream(buf), null, -1);
	//		}
	//		return privateDataset;
	//	}

	//	public static String attribute(Instance instance, String tag) throws IOException {
	//		Dataset d = DcmObjectFactory.getInstance().newDataset();
	//		d.readFile(new File(Properties.getString("archive"), instance.files.iterator().next().filepath), null, -1);
	//		return d.getItem(Tags.SharedFunctionalGroupsSeq).getItem(Tags.MRFOVGeometrySeq).getString(Tags.forName(tag));
	//	}
} public class DomainModelBinder implements TypeBinder<List> {

	@Override
	public DomainModel bind(String name, Annotation[] annotations, String value, Class actualClass, Type genericType) throws Exception {
		Item item = new Item(value);
		return (DomainModel) item.type.getMethod("findById", Object.class).invoke(null, item.pk);
	}

} public class Medcon {

	private static final Map<Format, String> formats = new HashMap<Format, String>() {{
		put(Format.dcm, "dicom"); //DICOM
		put(Format.nii, "nifti"); //NIfTI
		put(Format.img, "anlz");  //Analyze
	}};
	public static void convert(File from, Format format, File to) throws InterruptedException, IOException {
		File[] files = from.isDirectory() ? from.listFiles() : new File[] { from };
		File directory = to.isDirectory() ? to : to.getParentFile();
		List<String> prefix = new ArrayList(Arrays.asList(
				new File(Properties.getString("xmedcon"), "bin/medcon").getPath(),
				"-noprefix",
				"-anon",
				"-c", formats.get(format)
				));
		if (from.isDirectory() && !to.isDirectory()) {
			prefix.addAll(Arrays.asList(
					"-stack3d",
					"-o", to.getName()
					));
		}
		if (format == Format.nii || format == Format.img) {
			prefix.add("-fv");
		}
		prefix.add("-f");
		String[] command = Arrays.copyOf(prefix.toArray(new String[0]), prefix.size() + files.length);
		for (int i = 0; i < files.length; i++) {
			command[prefix.size() + i] = files[i].getPath();
		}
		Util.exec(directory, Collections.EMPTY_MAP, command);
	}

} public class PersistentLogger {

	public static void log(String message, Object... args) {
		new Log(String.format(message, args)).save();
	}
} public class Properties {

	public static int getInt(String key) {
		return Integer.parseInt(Play.configuration.getProperty(key));
	}

	public static String getString(String key) {
		return Play.configuration.getProperty(key);
	}

	public static File getArchive() {
		File archive = new File(Filesystem.<Filesystem>findAll().get(0).dirpath);
		return archive.isAbsolute() ? archive : new File(getString("dcm4chee"), String.format("server/default/%s", archive.getPath()));
	}

	public static int pageSize() {
		return Integer.parseInt(Play.configuration.getProperty("page.size", String.valueOf(20)));
	}

	public static File getDownloads() {
		File downloads = new File(Play.tmpDir, "downloads");
		downloads.mkdir();
		return downloads;
	}

	public static File getExports() {
		return new File(getString("exports"));
	}

	public static int getMaximumExportSize() {
		return Integer.parseInt(Play.configuration.getProperty("export.maxsize", String.valueOf(100)));
	}

	public static File getCollations() {
		File downloads = new File(Play.tmpDir, "collations");
		downloads.mkdir();
		return downloads;
	}
}
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;
import org.dcm4che.data.Dataset;
import au.com.bytecode.opencsv.CSVParser;
import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;
import controllers.Secure.Security;
import jobs.ClipboardExporter;
import jobs.Downloader;
import jobs.Downloader.Format;
import models.DomainModel;
import models.Instance;
import models.Person;
import models.Person.Role;
import models.Project;
import models.ProjectAssociation;
import models.Series;
import models.Study;
import notifiers.Mail;
import play.Invoker;
import play.Logger;
import play.Play;
import play.cache.Cache;
import play.data.binding.As;
import play.data.validation.Validation;
import play.db.jpa.GenericModel.JPAQuery;
import play.db.jpa.JPA;
import play.libs.Files;
import play.libs.IO;
import play.mvc.Before;
import play.mvc.Catch;
import util.Clipboard;
import util.Clipboard.Item;
import util.Dicom;
import util.DomainModelBinder;
import util.Medcon;
import util.PersistentLogger;
import util.Properties;

	public static void preferences(Person person, String frame) {
		if ("POST".equals(request.method)) {
			person.preferMultiframe = "multi".equals(frame);
			person.save();
		}
		render(person);
	}

	public static void advanced() {
		render();
	}

	public static void batch(File spreadsheet) throws IOException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
		if (spreadsheet == null) {
			if ("POST".equals(request.method)) {
				Validation.addError("spreadsheet", "Please select a file");
			}
			render();
		}

		List<List<DomainModel>> objects = new ArrayList<List<DomainModel>>();
		List<String> found = new ArrayList<String>();
		List<String> missing = new ArrayList<String>();

		CSVReader reader = new CSVReader(new FileReader(spreadsheet), CSVParser.DEFAULT_SEPARATOR, CSVParser.DEFAULT_QUOTE_CHARACTER, 1);
		for (String[] line : reader.readAll()) {
			List<String> series_descs = new ArrayList<String>();
			for (int i = 2; line.length > i && !line[i].trim().isEmpty(); i++) {
				series_descs.add(line[i].trim().toUpperCase());
			}
			String pat_id = line[0].trim().toUpperCase();
			if (!pat_id.isEmpty()) {
				if (!series_descs.isEmpty()) {
					Map<Study, List<DomainModel>> studySerieses = new LinkedHashMap<Study, List<DomainModel>>();
					for (String series_desc : series_descs) {
						List<Series> candidates = (List<Series>) CollectionUtils.select(Series.find("study.patient.pat_id = ? and series_desc = ?", pat_id, series_desc).<Series>fetch(), new Predicate() {
							@Override
							public boolean evaluate(Object candidate) {
								return Dicom.renderable((Series) candidate);
							}
						});
						if (!candidates.isEmpty()) {
							for (Series series : candidates) {
								found.add(String.format("%s - %s", pat_id, series_desc));
								List serieses = studySerieses.get(series.study);
								if (serieses == null) {
									serieses = new ArrayList<DomainModel>();
									studySerieses.put(series.study, serieses);
								}
								serieses.add(series);
							}
						} else {
							missing.add(String.format("%s - %s", pat_id, series_desc));
						}
					}
					objects.addAll(studySerieses.values());
				} else {
					List<DomainModel> studies = Study.find("patient.pat_id", pat_id).<DomainModel>fetch();
					if (!studies.isEmpty()) {
						for (DomainModel study : studies) {
							found.add(pat_id);
							objects.add(Collections.singletonList(study));
						}
					} else {
						missing.add(pat_id);
					}
				}
			} else {
				String participationID = line[1].trim().toUpperCase();
				if (!series_descs.isEmpty()) {
					Map<Study, List<DomainModel>> studySerieses = new LinkedHashMap<Study, List<DomainModel>>();
					for (String series_desc : series_descs) {
						List<Series> candidates = (List<Series>) CollectionUtils.select(Series.find("select series from Series series, in(series.study.projectAssociations) projectAssociation where projectAssociation.participationID = ? and series_desc = ?", participationID, series_desc).<Series>fetch(), new Predicate() {
							@Override
							public boolean evaluate(Object candidate) {
								return Dicom.renderable((Series) candidate);
							}
						});
						if (candidates.isEmpty()) {
							missing.add(String.format("%s - %s", participationID, series_desc));
						} else {
							for (Series series : candidates) {
								found.add(String.format("%s - %s", participationID, series_desc));
								List serieses = studySerieses.get(series.study);
								if (serieses == null) {
									serieses = new ArrayList<DomainModel>();
									studySerieses.put(series.study, serieses);
								}
								serieses.add(series);
							}
						}
					}
					objects.addAll(studySerieses.values());
				} else {
					List<DomainModel> studies = Study.find("select study from Study study, in(study.projectAssociations) projectAssociation where projectAssociation.participationID = ?", participationID).<DomainModel>fetch();
					if (!studies.isEmpty()) {
						for (DomainModel study : studies) {
							found.add(participationID);
							objects.add(Collections.singletonList(study));
						}
					} else {
						missing.add(participationID);
					}
				}
			}
		}
		reader.close();

		if (objects.isEmpty()) {
			Validation.addError(null, "No studies or series found");
		}

		if (Validation.hasErrors()) {
			render();
		}

		Map<String, String> pks = new LinkedHashMap<String, String>();
		for (List<DomainModel> object : objects) {
			Study study = object.get(0) instanceof Study ? ((Study) object.get(0)) : ((Series) object.get(0)).study;
			pks.put("pk=" + StringUtils.join(Item.serialize(object), "&pk="), study.patient.pat_id);