/*
 * This file is part of DebGen Biobank Software developed by ASTRID Research.
 * All rights reserved. Reproduction, modification, use or disclosure
 * to third parties without express authority is forbidden.
 *
 * Copyright ASTRID Research Inc., Hungary, 2009-2011.
 *
 * ASTRID Research
 * Headquaters: Csapo utca 42. Debrecen H-4029 HUNGARY
 * Phone: +36-52-533-173        Fax: +36-52-533-174
 * Web: http://www.astrid.hu    E-mail: info@astrid.hu
 *
 * Project name: DebGen Biobank Software project.
 * URLS:
 * Developers: Istvan Zoltan Nagy (istvan.nagy@astrid.hu)
 */
package hu.astrid.debgen.patientmanagement.domain.modules.requests;

import hu.astrid.debgen.exception.DebGenException;
import hu.astrid.debgen.exception.ValidationFailedException;
import hu.astrid.debgen.localization.service.LocalizationService;
import hu.astrid.debgen.patientmanagement.domain.Patient;
import hu.astrid.debgen.patientmanagement.domain.modules.DebGenModule;
import hu.astrid.debgen.patientmanagement.domain.modules.Result;
import hu.astrid.debgen.patientmanagement.domain.sample.Sample;
import hu.astrid.toucan.control.bean.ToucanBean;
import hu.astrid.toucan.servlet.httprequesttoobjectparser.HttpRequestToObjectParser;
import hu.astrid.xmltools.beans.XmlConvertable;
import hu.astrid.xmltools.beantoxml.BeanToXmlConverter;
import hu.astrid.xmltools.beantoxml.XmlToBeanConverter;
import org.hibernate.annotations.Cascade;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.persistence.*;
import javax.servlet.http.HttpServletRequest;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;

/**
 * The abstract superclass of analysis requests
 *
 * @author inagy
 */
@Entity
@Table(name = "debgen_analysis_common")
public abstract class AnalysisRequest extends DebGenModule implements ToucanBean, Serializable, XmlConvertable {

    private static final long serialVersionUID = 1L;
    @Transient
    protected HttpRequestToObjectParser requestParser;

    public static AnalysisRequest getInstance(String objectType) throws DebGenException {
        if (objectType.equals("")) {
            throw new hu.astrid.debgen.exception.DebGenException("UNKNOWN_ANALYSIS_REQUEST_TYPE");
        } else if (objectType.equals("const")) {
            return new AnalysisRequestConst();
        } else if (objectType.equals("born")) {
            return new AnalysisRequestBornDefect();
        } else if (objectType.equals("onkohaem")) {
            return new AnalysisRequestOnkohaematology();
        } else {
            throw new hu.astrid.debgen.exception.DebGenException("UNKNOWN_ANALYSIS_REQUEST_TYPE");
        }
    }

    // <editor-fold defaultstate="collapsed" desc="Inherited from DebGenModule">

    public String getLocalizeableName() {
        return getObjectType() + getClassType();
    }

    public String getClassType() {
        return "AnalysisRequest";
    }
    // </editor-fold>
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    protected String nameOfSenderInstitute;
    protected String senderCode;
    protected String diagnosis;
    @Temporal(TemporalType.DATE)
    protected Date dateOfSampleArrival;
    @Temporal(TemporalType.DATE)
    protected Date dateOfSampleProcessing;
    protected long numberOfWhiteCorpuscles;
    protected String immuneFenotypeFAB;
    @Temporal(TemporalType.DATE)
    protected Date startOfTherapy;
    protected String DNAIndex;
    protected int financialCategory;
    @Temporal(TemporalType.DATE)
    protected Date createdOn;
    protected String BNOCode;
    @ManyToOne
    protected Patient patient;
    protected String objectType = getType();
    @OrderBy(value = "sampleCode")
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "analysisRequest")
    @Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    protected Set<Sample> samples = new TreeSet<Sample>();

    @Transient
    protected LabelList labels;
    @Transient
    protected String languageIsoCode = "hun";
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    protected Set<Result> results = new TreeSet<Result>();
    @Transient
    protected boolean empty = false;
    protected String logNumber;
    @Transient
    protected Result result = null;


    // <editor-fold defaultstate="collapsed" desc="Getters and Setters">

    public boolean isEmpty() {
        return empty;
    }

    public void setEmpty(boolean empty) {
        this.empty = empty;
    }

    public Date getDateOfSampleArrival() {
        return dateOfSampleArrival;
    }

    public String getDateOfSampleArrivalForWeb() {
        try {
            return new SimpleDateFormat("yyyy. MM. dd.").format(dateOfSampleArrival);
        } catch (Exception e) {
            return "";
        }
    }

    public void setDateOfSampleArrival(Date dateOfSampleArrival) {
        this.dateOfSampleArrival = dateOfSampleArrival;
    }

    public Date getDateOfSampleProcessing() {
        return dateOfSampleProcessing;
    }

    public String getDateOfSampleProcessingForWeb() {
        try {
            return new SimpleDateFormat("yyyy. MM. dd.").format(dateOfSampleProcessing);
        } catch (Exception e) {
            return "";
        }
    }

    public void setDateOfSampleProcessing(Date dateOfSampleProcessing) {
        this.dateOfSampleProcessing = dateOfSampleProcessing;
    }

    /**
     * Get the value of logNumber
     *
     * @return the value of logNumber
     */
    public String getLogNumber() {
        return logNumber;
    }

    /**
     * Set the value of logNumber
     *
     * @param logNumber new value of logNumber
     */
    public void setLogNumber(String logNumber) {
        this.logNumber = logNumber;
    }

    public Result getResult() {
        return result;
    }

    public void setResult(Result result) {
        this.result = result;
    }

    public Set<Result> getResults() {
        return results;
    }

    public void setResults(Set<Result> results) {
        this.results = results;
    }

    public void setLanguageIsoCode(String languageIsoCode) {
        this.languageIsoCode = languageIsoCode;
    }

    public LabelList getLabels() {
        if (labels == null || labels.getLabels().isEmpty()) {
            labels = new LabelList();
            labels.buildFromSampleSet(samples);
        }
        return labels;
    }

    public void setLabels(LabelList labels) {
        this.labels = labels;
    }

    public String getCreatedOnForWeb() {
        try {
            return new SimpleDateFormat("yyyy. MM. dd.").format(createdOn);
        } catch (Exception e) {
            return "";
        }
    }

    public Date getCreatedOn() {
        return createdOn;
    }

    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    public Patient getPatient() {
        return patient;
    }

    public void setPatient(Patient patient) {
        this.patient = patient;
    }

    /**
     * Get the value of BNOCode
     *
     * @return the value of BNOCode
     */
    public String getBNOCode() {
        return BNOCode;
    }

    /**
     * Set the value of BNOCode
     *
     * @param BNOCode new value of BNOCode
     */
    public void setBNOCode(String BNOCode) {
        this.BNOCode = BNOCode;
    }

    /**
     * Get the value of objectType
     *
     * @return the value of objectType
     */
    public String getObjectType() {
        return objectType;
    }

    /**
     * Set the value of objectType
     *
     * @param objectType new value of objectType
     */
    public void setObjectType(String objectType) {
        this.objectType = objectType;
    }

    /**
     * Returns the type of the class such as: common
     *
     * @return The type of the class;
     */
    public abstract String getType();

    /**
     * Get the value of id
     *
     * @return the value of id
     */
    public Long getId() {
        return id;
    }

    /**
     * Set the value of id
     *
     * @param id new value of id
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * Get the value of financialCategory
     *
     * @return the value of financialCategory
     */
    public int getFinancialCategory() {
        return financialCategory;
    }

    /**
     * Set the value of financialCategory
     *
     * @param financialCategory new value of financialCategory
     */
    public void setFinancialCategory(int financialCategory) {
        this.financialCategory = financialCategory;
    }

    /**
     * Get the value of DNAIndex
     *
     * @return the value of DNAIndex
     */
    public String getDNAIndex() {
        return DNAIndex;
    }

    /**
     * Set the value of DNAIndex
     *
     * @param DNAIndex new value of DNAIndex
     */
    public void setDNAIndex(String DNAIndex) {
        this.DNAIndex = DNAIndex;
    }

    /**
     * Get the value of startOfTherapy
     *
     * @return the value of startOfTherapy
     */
    public Date getStartOfTherapy() {
        return startOfTherapy;
    }

    public String getStartOfTherapyForWeb() {
        try {
            return new SimpleDateFormat("yyyy. MM. dd.").format(startOfTherapy);
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * Set the value of startOfTherapy
     *
     * @param startOfTherapy new value of startOfTherapy
     */
    public void setStartOfTherapy(Date startOfTherapy) {
        this.startOfTherapy = startOfTherapy;
    }

    /**
     * Get the value of immuneFenotypeFAB
     *
     * @return the value of immuneFenotypeFAB
     */
    public String getImmuneFenotypeFAB() {
        return immuneFenotypeFAB;
    }

    /**
     * Set the value of immuneFenotypeFAB
     *
     * @param immuneFenotypeFAB new value of immuneFenotypeFAB
     */
    public void setImmuneFenotypeFAB(String immuneFenotypeFAB) {
        this.immuneFenotypeFAB = immuneFenotypeFAB;
    }

    /**
     * Get the value of numberOfWhiteCorpuscles
     *
     * @return the value of numberOfWhiteCorpuscles
     */
    public long getNumberOfWhiteCorpuscles() {
        return numberOfWhiteCorpuscles;
    }

    /**
     * Set the value of numberOfWhiteCorpuscles
     *
     * @param numberOfWhiteCorpuscles new value of numberOfWhiteCorpuscles
     */
    public void setNumberOfWhiteCorpuscles(long numberOfWhiteCorpuscles) {
        this.numberOfWhiteCorpuscles = numberOfWhiteCorpuscles;
    }

    /**
     * Get the value of diagnosis
     *
     * @return the value of diagnosis
     */
    public String getDiagnosis() {
        return diagnosis;
    }

    /**
     * Set the value of diagnosis
     *
     * @param diagnosis new value of diagnosis
     */
    public void setDiagnosis(String diagnosis) {
        this.diagnosis = diagnosis;
    }

    /**
     * Get the value of senderCode
     *
     * @return the value of senderCode
     */
    public String getSenderCode() {
        return senderCode;
    }

    /**
     * Set the value of senderCode
     *
     * @param senderCode new value of senderCode
     */
    public void setSenderCode(String senderCode) {
        this.senderCode = senderCode;
    }

    /**
     * Get the value of nameOfSenderInstitute
     *
     * @return the value of nameOfSenderInstitute
     */
    public String getNameOfSenderInstitute() {
        return nameOfSenderInstitute;
    }

    public Set<Sample> getSamples() {
        return samples;
    }

    public void setSamples(Set<Sample> samples) {
        this.samples = samples;
    }

    /**
     * Set the value of nameOfSenderInstitute
     *
     * @param nameOfSenderInstitute new value of nameOfSenderInstitute
     */
    public void setNameOfSenderInstitute(String nameOfSenderInstitute) {
        this.nameOfSenderInstitute = nameOfSenderInstitute;
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Toucan request processing">

    public void convert(HttpServletRequest request) throws Exception {
        requestParser = HttpRequestToObjectParser.getInstance(request);
        requestParser.parseRequest(AnalysisRequest.class, this);
        if (this.createdOn == null) {
            this.createdOn = new Date();
        }
    }// </editor-fold>

    /**
     * Validate the current analysis requests datas
     *
     * @return True if the current analysis request (this) is valid
     * @throws ValidationFailedException When the analysis request is not valid
     */
    public boolean validate() throws ValidationFailedException {
        if (this.createdOn == null) {
            this.createdOn = new Date();
        }
        ArrayList<String> properties = new ArrayList<String>(4);
        if (this.nameOfSenderInstitute == null || this.nameOfSenderInstitute.equals("")) {
            properties.add("BEKÜLDŐ_NEVE_CÍME");
        }
        if (this.senderCode == null || this.senderCode.equals("")) {
            properties.add("BEKÜLDŐ_KÓD");
        }
        if (this.dateOfSampleArrival == null) {
            properties.add("dateOfSampleArrival_");
        }
        if (this.dateOfSampleProcessing == null) {
            properties.add("dateOfSampleProcessing_");
        }
        if (this.diagnosis == null || this.diagnosis.equals("")) {
            properties.add("DIAGNÓZIS_");
        }
        if (properties.isEmpty()) {
            return true;
        } else {
            throw new ValidationFailedException(properties);
        }
    }

    // <editor-fold defaultstate="collapsed" desc="XML conversion">

    public void convertToXml(URL schema, OutputStream output) throws Exception {
        BeanToXmlConverter beanToXmlConverter = new BeanToXmlConverter(schema, output);
        beanToXmlConverter.setNameSpacePrefix("debgenbiobank");
        beanToXmlConverter.convertToXml(this);
    }

    public abstract void buildFromXml(Node node) throws Exception;

    public abstract void buildFromXml(Node node, String string) throws Exception;

    public abstract Node toXml(Document doc) throws Exception;

    public abstract Node toXml(Document doc, String string) throws Exception;

    public XmlToBeanConverter getXmlConverter() throws Exception {
        return new XmlToBeanConverter();
    }

    protected void setXmlCommonValues(Document doc, Node root) throws Exception {
        if (this.result != null) {
            root.appendChild(this.result.toXml(doc));
        }
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "nameOfSenderInstitute", nameOfSenderInstitute));
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "senderCode", senderCode));
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "BNOcode", BNOCode));
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "logNumber", logNumber));
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "diagnosis", diagnosis));
        try {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "createdOn", new SimpleDateFormat("yyyy. MM. dd.").format(createdOn)));
        } catch (Exception ex) {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "createdOn", ""));
        }
        try {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "dateOfSampleArrival", new SimpleDateFormat("yyyy. MM. dd.").format(dateOfSampleArrival)));
        } catch (Exception ex) {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "dateOfSampleArrival", ""));
        }
        try {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "dateOfSampleProcessing", new SimpleDateFormat("yyyy. MM. dd.").format(dateOfSampleProcessing)));
        } catch (Exception ex) {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "dateOfSampleProcessing", ""));
        }
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "numberOfWhiteCorpuscles", numberOfWhiteCorpuscles + ""));
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "immuneFenotypeFAB", immuneFenotypeFAB));
        try {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "startOfTherapy", new SimpleDateFormat("yyyy. MM. dd.").format(startOfTherapy)));
        } catch (Exception ex) {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "startOfTherapy", ""));
        }
        root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "DNAIndex", DNAIndex));
        try {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "financialCategory", new LocalizationService().getMessage("FINCAT" + financialCategory, languageIsoCode)));
        } catch (Exception ex) {
            root.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "financialCategory", ""));
        }
    }

    protected void setXmlMetaData(Document doc, Node parent) throws DOMException {
        Element metaData = doc.createElement("metadata");
        parent.appendChild(metaData);
        metaData.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "dateOfPrinting", new SimpleDateFormat("yyyy. MM. dd.").format(new Date())));
        metaData.appendChild(XmlToBeanConverter.getNodeWithValue(doc, "timeOfPrinting", new SimpleDateFormat("hh:mm:ss").format(new Date())));
    }// </editor-fold>
}
