/*
 * 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.service.modules;

import hu.astrid.debgen.patientmanagement.domain.modules.Result;
import hu.astrid.shared.server.service.BaseOperationsAbstract;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

/**
 * The Service, which is handling the Results
 * @author inagy
 */
public class ResultManagerService extends BaseOperationsAbstract {

    /**
     * Returns an instance of a ResultManagerService and sets its session.
     * @param session The session os the Service to use the DB connection.
     * @return A new instance.
     */
    public static BaseOperationsAbstract getInstance(Session session) {
        ResultManagerService resultManagerService = new ResultManagerService();
        resultManagerService.session = session;
        return resultManagerService;
    }

    /**
     * Loads the Result by the given id
     * @param id The id of the Result
     * @return The Result object with the specified id (if there is such thing)
     */
    public Result load(Long id) {
        Result result = super.load(Result.class, id);
        if (!result.isTemplate()) {
            Hibernate.initialize(result.getPatient().getAnalysisRequests());
        }
        return result;
    }

    /**
     * Returns a list of Results
     * @param order The ordering attribute to order the results ascending
     * @param offset The index of the first result entry.
     * @param length The number of elements on one page
     * @return The list of records on the current page.
     */
    public List<Result> list(String order, int offset, int length) {
        return this.find(null, order, offset, length);
    }

    /**
     * Finds the matching Result(s) using the criterias
     * @param what The search criterias
     * @param order The ordering attribute to order the results ascending
     * @param offset The index of the first result entry.
     * @param length The number of elements on one page
     * @return The list of records on the current page.
     */
    public List<Result> find(Result what, String order, int offset, int length) {
        Criteria criteria = session.createCriteria(Result.class);
        List list = criteria.addOrder(Order.asc(order)).setFirstResult(offset).setMaxResults(length).list();
        List<Result> results = new ArrayList<Result>(length);
        if (list != null && !list.isEmpty()) {
            for (Object o : list) {
                results.add((Result) o);
            }
        }
        return results;
    }

    /**
     * Deletes the given result objects record from the DB
     * @param result The Result which should be deleted
     */
    public void delete(Result result) {
        session.delete(result);
    }

    /**
     * Returns the number of matching Result records
     * @param all Include all Results?
     * @return The number of matching Results
     */
    public double countList() {
        return this.countSearchList(null);
    }

    /**
     * Returns the number of matching Result records
     * @param what The search criterias.
     * @return The number of matching Result
     */
    public double countSearchList(Result what) {
        Criteria criteria = session.createCriteria(Result.class);
        Object result = criteria.setProjection(Projections.countDistinct("id")).uniqueResult();
        return Double.parseDouble(result.toString());
    }

    /**
     * Lists all data of the given String type attribute from the Result class
     * @param clazz The persistens class, which contains the field.
     * @param field The name of the field.
     * @param query The pattern which should match in START MatchMode
     * @return The list of matching attribute values
     */
    public List<String> listField(Class clazz, String field, String query) {
        Criteria criteria = session.createCriteria(clazz).add(Restrictions.like(field, query, MatchMode.START));
        List list = criteria.addOrder(Order.asc(field)).list();
        ArrayList<String> result = new ArrayList<String>(list.size() + 1);
        for (Object o : list) {
            try {
                String value = (String) clazz.getMethod("get" + field.substring(0, 1).toUpperCase() + field.substring(1)).invoke((Result) o);
                if (!result.contains(value)) {
                    result.add(value);
                }
            } catch (IllegalAccessException ex) {
                //IGNORED
            } catch (IllegalArgumentException ex) {
                //IGNORED
            } catch (InvocationTargetException ex) {
                //IGNORED
            } catch (NoSuchMethodException ex) {
                //IGNORED
            } catch (SecurityException ex) {
                //IGNORED
            }
        }
        return result;
    }

    /**
     * Returns the number of templates
     * @return The number of templates
     */
    public double countTemplateList() {
        Criteria criteria = session.createCriteria(Result.class).add(Restrictions.eq("template", true));
        Object result = criteria.setProjection(Projections.countDistinct("id")).uniqueResult();
        return Double.parseDouble(result.toString());
    }

    /**
     * Lists the Template(s) using the criterias
     * @param offset The index of the first result entry.
     * @param length The number of elements on one page
     * @return The list of records on the current page.
     */
    public List<Result> listTemplates(int offset, int length) {
        Criteria criteria = session.createCriteria(Result.class).add(Restrictions.eq("template", true)).addOrder(Order.asc("objectType")).addOrder(Order.asc("templateName"));
        List list = criteria.setFirstResult(offset).setMaxResults(length).list();
        List<Result> results = new ArrayList<Result>(length);
        if (list != null && !list.isEmpty()) {
            for (Object o : list) {
                results.add((Result) o);
            }
        }
        return results;
    }
}
