package gov.va.vinci.dart;

import gov.va.vinci.dart.biz.Activity;
import gov.va.vinci.dart.biz.DataSource;
import gov.va.vinci.dart.biz.DocumentReviewNote;
import gov.va.vinci.dart.biz.DocumentReviewStatus;
import gov.va.vinci.dart.biz.DocumentTemplate;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.Location;
import gov.va.vinci.dart.biz.OperationalRequest;
import gov.va.vinci.dart.biz.Participant;
import gov.va.vinci.dart.biz.Person;
import gov.va.vinci.dart.biz.Request;
import gov.va.vinci.dart.biz.RequestAdminLocationDocument;
import gov.va.vinci.dart.biz.RequestAdminParticipantDocument;
import gov.va.vinci.dart.biz.RequestLocationDocument;
import gov.va.vinci.dart.biz.RequestParticipantDocument;
import gov.va.vinci.dart.biz.RequestWorkflow;
import gov.va.vinci.dart.biz.Review;
import gov.va.vinci.dart.biz.Role;
import gov.va.vinci.dart.biz.WorkflowTemplate;
import gov.va.vinci.dart.common.exception.ObjectNotFoundException;
import gov.va.vinci.dart.common.exception.ValidationException;
import gov.va.vinci.dart.common.json.ErrorView;
import gov.va.vinci.dart.db.util.HibernateSessionManager;
import gov.va.vinci.dart.dms.biz.Content;
import gov.va.vinci.dart.dms.biz.Document;
import gov.va.vinci.dart.json.DocumentIdView;
import gov.va.vinci.dart.json.DocumentListView;
import gov.va.vinci.dart.json.DocumentReviewNoteView;
import gov.va.vinci.dart.json.DocumentTemplateListView;
import gov.va.vinci.dart.json.DocumentTemplateView;
import gov.va.vinci.dart.json.DocumentVersionView;
import gov.va.vinci.dart.json.DocumentView;
import gov.va.vinci.dart.json.GroupDocumentListView;
import gov.va.vinci.dart.json.GroupDocumentView;
import gov.va.vinci.dart.json.RequestIdView;
import gov.va.vinci.dart.json.RequestLocationDocumentListView;
import gov.va.vinci.dart.json.RequestLocationDocumentView;
import gov.va.vinci.dart.json.RequestParticipantDocumentListView;
import gov.va.vinci.dart.json.RequestParticipantDocumentView;
import gov.va.vinci.dart.usr.UserPreferences;
import gov.va.vinci.dart.wf2.WorkflowResolver;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

/**
 * The Class DocumentController.
 */
@Controller
public class DocumentController extends DartController {

    /** The Constant REQUIRED_FOR. */
    private static final String REQUIRED_FOR = "Required for ";

    /** The Constant REAL_SSN_ACCESS_REQUEST. */
    private static final String REAL_SSN_ACCESS_REQUEST = "Real SSN Access Request";

    /** The Constant SDF. */
    public static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy");

    /** The Constant LONG_FORMAT_SDF. */
    public static final SimpleDateFormat LONG_FORMAT_SDF = new SimpleDateFormat("MM/dd/yy hh:mmaa");

    /** The log. */
    private static Log log = LogFactory.getLog(DocumentController.class);

    /** The multipart resolver. */
    private CommonsMultipartResolver multipartResolver;


    /**
     * Sets the multipart resolver.
     *
     * @param multipartResolver
     *            the new multipart resolver
     */
    @Autowired
    public void setMultipartResolver(CommonsMultipartResolver multipartResolver) {
        this.multipartResolver = multipartResolver;
    }

    /**
     * Returns the Content object for the Document head. If this document represents the head of the chain, returns the Content
     * for the given Document.
     * 
     * Returns null if there is no Document.
     *
     * @param doc
     *            the doc
     * @return the head document content
     */
    public static Content getHeadDocumentContent(final Document doc) {

        Content docContent = null;

        if (doc != null) {

            docContent = doc.getContent();

            if (doc.getHead() != doc.getId()) { // if not at the 1st upload of this document, get the head document's contentId
                Document headDoc = Document.findById(doc.getHead());
                if (headDoc != null) {
                    docContent = headDoc.getContent();
                }
            }
        }

        return docContent;
    }

    // see RFC 6570
    /**
     * View document.
     *
     * @param docId
     *            the doc id
     * @param response
     *            the response
     */
    // and mind the known bug in Spring MVC URI template handling.
    @RequestMapping(value = "/files/{docId}/*", method = RequestMethod.GET)
    @ResponseBody
    public void viewDocument(@Valid @PathVariable(value = "docId") String docId, final HttpServletResponse response) {

        log.debug("viewDocument doc id = " + docId);

        InputStream is = null;

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = Person.findById(prefs.getUserId());

            int iDocId = Integer.parseInt(docId);

            Document doc = Document.findById(iDocId);
            if (doc == null) {
                log.error("Error downloading document" + iDocId + " : document not found.");
                return;
            }

            Request request = doc.getRequest();
            if (request == null) {
                log.error("Error downloading document.");
                return;
            }

            Activity activity = request.getActivity();
            if (activity == null) {
                log.error("Error downloading document.");
                return;
            }

            // verify the access permissions
            if (!activity.verifyReadAccessPermissions(person)) {
                log.error("Error downloading document: invalid user permissions.");
                return;
            }

            log.debug("doc = " + doc);
            log.debug("doc.getContent = " + doc.getContent());

            // get your file as InputStream
            is = doc.getContent().openStream(true);

            log.debug("doc.getContent.openStream returns " + is);

            // should we set the length of the returned document in the response headers?

            // copy it to response's OutputStream
            setResponseMIMEType(response, doc);
            IOUtils.copy(is, response.getOutputStream());

            // there is a known issue with IE -- it doesn't support long filenames (may need to modify the filename)
            // String documentName = doc.getName();
            // response.setHeader("Content-Disposition", "attachment;filename=\""+documentName+"\""); //set an explicit
            // attachment name

            response.flushBuffer();
        } catch (Exception ex) {
            log.info("Error writing file to output stream.", ex);
            HibernateSessionManager.rollback();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            HibernateSessionManager.close();
        }
    }

    /**
     * Sets the response mime type.
     *
     * @param response
     *            the response
     * @param doc
     *            the doc
     */
    private static void setResponseMIMEType(final HttpServletResponse response, final Document doc) {
        if ("doc".equalsIgnoreCase(doc.getTypeSuffix()) || "docx".equalsIgnoreCase(doc.getTypeSuffix())) {
            response.setContentType("application/msword");
        } else if ("pdf".equalsIgnoreCase(doc.getTypeSuffix())) {
            response.setContentType("application/pdf");
        }
    }

    /**
     * Checks if is allowed file extension.
     *
     * @param extension
     *            the extension
     * @return true, if is allowed file extension
     */
    public static boolean isAllowedFileExtension(final String extension) {
        return ("pdf".equalsIgnoreCase(extension) || "doc".equalsIgnoreCase(extension) || "docx".equalsIgnoreCase(extension));
    }

    /**
     * Upload document.
     *
     * @param docid
     *            the docid
     * @param file
     *            the file
     * @param response
     *            the response
     */
    @RequestMapping(value = "/uploadDocument", method = RequestMethod.POST)
    @ResponseBody
    public void uploadDocument(@Valid @RequestParam("docid") final String docid,
            @RequestParam("fileField") final MultipartFile file, final HttpServletResponse response) {

        log.debug("upload document: (" + docid + ") " + file.getName());

        // force the response type to plain text (IE issues)
        response.addHeader("X-UA-Compatible", "IE=edge,chrome=1");
        response.setContentType("text/plain");

        PrintWriter outputStreamWriter = null;
        InputStream fileInputStream = null;

        try {
            HibernateSessionManager.start();
            UserPreferences prefs = getUserPreferences();

            Person person = Person.findById(prefs.getUserId());

            outputStreamWriter = new PrintWriter(response.getOutputStream());

            log.debug("upload document docid = " + docid);
            log.debug("upload document file = " + file);
            log.debug("content type  = " + file.getContentType());
            log.debug("content name  = " + file.getName());
            log.debug("content original name  = " + file.getOriginalFilename());

            int documentId = 1;
            String contentExtension = "txt";

            int i = file.getOriginalFilename().lastIndexOf('.');
            if (i > 0) {
                contentExtension = file.getOriginalFilename().substring(i + 1);
            }
            log.debug("contentExtension  = " + contentExtension);

            // validation
            if (!isAllowedFileExtension(contentExtension)) {

                if (outputStreamWriter != null) {
                    outputStreamWriter.print("Error: only .doc,.docx, and .pdf files can be uploaded.");
                    outputStreamWriter.flush();
                }
                response.flushBuffer();
                return;
            }

            try {
                documentId = Integer.parseInt(docid);
            } catch (NumberFormatException e) {

                if (outputStreamWriter != null) {
                    outputStreamWriter.print("Error: unknown document.");
                    outputStreamWriter.flush();
                }
                response.flushBuffer();
                return;
            }

            Document doc = Document.findById(documentId);
            if (doc == null) {
                // the Document should exist at this point, but verify, just in case
                // might want to create the Document if it doesn't exist here (but need the DocumentTemplate to do so)
                log.error("Error uploading document" + documentId + " : document not found.");
                return;
            }

            Request request = doc.getRequest();
            if (request == null) {
                log.error("Error uploading document: null request.");
                return;
            }

            // verify the access permissions
            boolean isAdminDoc = false;
            List<RequestAdminLocationDocument> requestAdminLocationDocuments =
                    RequestAdminLocationDocument.listByDocumentId(doc.getHead());
            List<RequestAdminParticipantDocument> requestAdminParticipantDocuments =
                    RequestAdminParticipantDocument.listByDocumentId(doc.getHead());
            if ((requestAdminLocationDocuments != null && requestAdminLocationDocuments.size() > 0)
                    || (requestAdminParticipantDocuments != null && requestAdminParticipantDocuments.size() > 0)) {
                isAdminDoc = true;
            }

            if (person != null) {
                if (!person.hasRole(Role.SUPER_USER) && !person.hasRole(Role.NDS_ADMIN) && !person.hasRole(Role.REVIEWER)
                        && !person.hasRole(Role.READ_ONLY_STAFF)) {

                    if (!request.hasCreatorOrParticipant(person)) { // participant or the request creator?
                        log.error("Error uploading document: invalid user permissions.");
                        return;
                    }

                    if (isAdminDoc) {
                        log.error("Error uploading document: invalid user permissions.");
                        return;
                    }

                }

                Group adminGroup = null;
                for (Group group : person.getGroups()) {
                    adminGroup = group; // grab the last group the user belongs to? why? Because we like it...
                }

                if (adminGroup != null) {

                    if (!isAdminDoc) {
                        log.error("Error uploading document: invalid user permissions.");
                        return;

                    } else {

                        if (requestAdminLocationDocuments != null && requestAdminLocationDocuments.size() > 0) {
                            for (RequestAdminLocationDocument adminDoc : requestAdminLocationDocuments) {
                                if (adminDoc.getGroupId() != adminGroup.getId()) { // user does not belong to the this group
                                    log.error("Error uploading document: invalid user permissions.");
                                    return;
                                }
                            }
                        }

                        if (requestAdminParticipantDocuments != null && requestAdminParticipantDocuments.size() > 0) {
                            for (RequestAdminParticipantDocument adminDoc : requestAdminParticipantDocuments) {
                                if (adminDoc.getGroupId() != adminGroup.getId()) { // user does not belong to the this group
                                    log.error("Error uploading document: invalid user permissions.");
                                    return;
                                }
                            }
                        }
                    }

                } else {

                    if (isAdminDoc) {
                        log.error("Error uploading document: invalid user permissions.");
                        return;
                    }
                }
            }

            if (doc.isNotCurrent()) {
                log.error("Error uploading document.");
                return;
            }

            Document doc2 = doc;

            // on the first upload of version 1 of the doc, can we just set the doc object contents instead of creating a new
            // doc object?
            if (doc.getUpdatedBy() != null && doc.getContent() != null) {

                log.debug("allocating new document object");
                doc2 = doc.createNewVersion(prefs.getUserLoginId());
                log.debug("allocating new document object id = " + doc2.getId());
            }

            // copy on write
            doc2.createContent(contentExtension);
            log.debug("doc2 create content");

            log.debug("doc2 id = " + doc2.getId());
            log.debug("doc2 content = " + doc2.getContent().getId());

            log.debug("Doc description = " + doc.getDescription());

            // get the new document name (if someone new uploads the document, we need to change the document name)
            final String newDocName = constructDocumentName(doc2, doc.getDescription(), contentExtension, prefs);

            doc2.setName(newDocName);
            doc2.setUpdatedBy(prefs.getUserLoginId());
            doc2.setUpdatedOn(new Date());
            doc2.setTypeSuffix(contentExtension);

            try {
                fileInputStream = file.getInputStream();
                if (fileInputStream != null) {
                    doc2.getContent().write(fileInputStream, true);
                }
            } catch (IOException exc) {
                throw exc;
            }

            outputStreamWriter.print(Integer.toString(doc2.getHead()));
            outputStreamWriter.flush();
            response.flushBuffer();
            return;

        } catch (Exception e) {
            log.error("Error saving uploaded document.", e);
            HibernateSessionManager.rollback();

            if (outputStreamWriter != null) {
                outputStreamWriter.print("Error saving uploaded document.");
                outputStreamWriter.flush();
            }
        } finally {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }

            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException exc) {
                log.error("Error closing the file input stream: " + exc.getMessage());
            }

            HibernateSessionManager.close();
        }
    }

    /**
     * Construct document name.
     *
     * @param document
     *            the document
     * @param description
     *            the description
     * @param extension
     *            the extension
     * @param prefs
     *            the prefs
     * @return the string
     */
    private static String constructDocumentName(final Document document, final String description, final String extension,
            final UserPreferences prefs) {
        String documentName = document.getRequest().getTrackingNumber() + " " + description;

        String location = Document.findLocationByDocId(document.getId());

        if (location == null) {
            location = "";
        }

        if (!location.isEmpty()) {
            documentName += ("-" + location);
        }

        String participantName = prefs.getUserFullName();

        if (!RequestController.isEmpty(participantName)) {

            if (participantName == null || participantName.equalsIgnoreCase("null")) {
                participantName = "";
            }
            // else {
            // participantName = "-" + participantName;
            // }

        } else {
            participantName = "";
        }

        if (!participantName.isEmpty()) {
            documentName += ("-" + participantName);
        }

        documentName += ("." + extension);

        documentName = documentName.replaceAll("\\/", " ");
        documentName = documentName.replaceAll("\\\\", " ");

        return documentName;
    }

    /**
     * List documents by request.
     *
     * @param view
     *            the view
     * @return the object
     */
    @RequestMapping(value = "/listDocumentsByRequest", method = RequestMethod.POST)
    @ResponseBody
    public Object listDocumentsByRequest(@RequestBody @Valid final RequestIdView view) {

        log.debug("listDocumentsByRequest");

        if (view == null) {
            return new ErrorView("Error loading document list");
        }

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();
            Person person = Person.findById(prefs.getUserId());

            // TODO- pathetic hack!
            // (wanna explain it then?)
            multipartResolver.setMaxUploadSize(-1L);

            Request request = RequestController.retrieveRequest(view.getRequestId());
            if (request == null) {
                throw new ValidationException("Request not found with id " + view.getRequestId());
            }

            Activity activity = request.getActivity();
            if (activity == null) {
                return new ErrorView("Error loading document list");
            }

            // verify the access permissions
            if (!activity.verifyReadAccessPermissions(person)) {
                log.error("Error loading document list: invalid user permissions.");
                return new ErrorView("Error loading document list");
            }

            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) {
                workflow = RequestWorkflow.findById(view.getWorkflowId());
                
            }

            DocumentListView result = new DocumentListView();

            List<Integer> prevRequestIDList = null;
            if (request != null && request.isAmendment()) {
                prevRequestIDList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }

            Set<RequestWorkflow> dataSourceWorkflowSet = getOpenWorkflowsForSelectedDataSources(request);

            listLocationDocumentsByRequest(result, request, request.getSites(), prevRequestIDList, person, workflow,
                    dataSourceWorkflowSet);

            listParticipantDocumentsByRequest(result, request, request.getParticipants(), prevRequestIDList, person, workflow,
                    dataSourceWorkflowSet);

            return result;
        } catch (Exception e) {
            log.error("Error loading document list", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading document list: " + e.getMessage());
        } finally {
            HibernateSessionManager.close();
        }
    }

    // Note: currently unused (instead, we retrieve the Admin documents for all groups, as specified by the NDS workgroup)
    /**
     * List admin documents by request.
     *
     * @param view
     *            the view
     * @return the object
     */
    @RequestMapping(value = "/listAdminDocumentsByRequest", method = RequestMethod.POST)
    @ResponseBody
    public Object listAdminDocumentsByRequest(@RequestBody @Valid final RequestIdView view) {

        log.debug("listAdminDocumentsByRequest");

        if (view == null) {
            return new ErrorView("Error loading document list");
        }

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }
            Role.initialize();

            if (!person.hasRole(Role.SUPER_USER)) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            // TODO- pathetic hack!
            // (wanna explain it then?)
            multipartResolver.setMaxUploadSize(-1L);

            Request request = null;
            try {
                request = RequestController.retrieveRequest(view.getRequestId());

            } catch (ObjectNotFoundException e) {
                log.error("Error loading request " + view.getRequestId(), e);
                return new ErrorView("Error loading request " + view.getRequestId());
            }

            if (request == null) {
                throw new ValidationException("Request not found with id " + view.getRequestId());
            }

            DocumentListView result = new DocumentListView();

            /*
             * insert -- get the admin documents in case they have changed (smaller set) * // update the Admin documents.
             * request.createAdminDocuments(prefs.getUserLoginId()); /* end insert
             */

            listAdminLocationDocumentsByRequest(result.getLocations(), request, request.getSites(),
                    RequestAdminLocationDocument.listByRequestId(request.getId()), person, -1, false);

            listAdminParticipantDocumentsByRequest(result.getParticipants(), request, request.getParticipants(),
                    RequestAdminParticipantDocument.listByRequestId(request.getId()), person, -1, false);

            // TODO- if the requesting user is a reviewer, only show the admin documents assigned to their review group.
            // but Irma gets to see everything
            return result;
        } catch (Exception e) {
            log.error("Error loading document list.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading document list.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * Used to retrieve the admin documents in the create wizard. (Requestor)
     *
     * @param view
     *            the view
     * @return the object
     */
    @RequestMapping(value = "/listAdminDocumentsForAllGroups", method = RequestMethod.POST)
    @ResponseBody
    public Object listAdminDocumentsForAllGroups(@RequestBody @Valid final RequestIdView view) {

        log.debug("listAdminDocumentsForAllGroups");

        if (view == null) {
            return new ErrorView("Error loading admin document list.");
        }

        // TODO- pathetic hack!
        // (wanna explain it then?)
        multipartResolver.setMaxUploadSize(-1L);

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();
            Person person = Person.findById(prefs.getUserId());

            Request request = null;
            try {
                request = RequestController.retrieveRequest(view.getRequestId());

            } catch (ObjectNotFoundException e) {
                log.error("Error loading request " + view.getRequestId(), e);
                return new ErrorView("Error loading request " + view.getRequestId());
            }

            if (request == null) {
                throw new ValidationException("Request not found with id " + view.getRequestId());
            }

            Activity activity = request.getActivity();
            if (activity == null) {
                log.error("Error loading document list: null activity.");
                return new ErrorView("Error loading document list.");
            }

            // verify the access permissions
            if (!activity.verifyReadAccessPermissions(person)) {
                log.error("Error loading document list: invalid user permissions.");
                return new ErrorView("Error loading document list.");
            }

            GroupDocumentListView result = new GroupDocumentListView();

            //
            // get the admin documents per group
            List<Group> reviewGroupList = new ArrayList<Group>();

            Group.initialize();

            if (request.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST || request.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST) {

                Set<RequestWorkflow> openWorkflows = request.getWorkflows(true);
                if (openWorkflows != null) {
                    for (RequestWorkflow workflow : openWorkflows) {

                        if (workflow != null) {
                            if (workflow.getWorkflowTemplate() != null
                                    && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS) {
                                reviewGroupList.add(Group.NDS);
                            }

                            Set<Review> reviewSet = request.getReviews(workflow);
                            if (reviewSet != null) {
                                for (Review review : reviewSet) {
                                    reviewGroupList.add(review.getReviewer());
                                }
                            }
                        }
                    }
                }

            } else {

                reviewGroupList.add(Group.NDS);

                Set<Review> reviewSet = request.getAllReviews();
                if (reviewSet != null) {
                    for (Review currReview : reviewSet) {
                        reviewGroupList.add(currReview.getReviewer());
                    }
                }
            }

            for (Group group : reviewGroupList) {
                GroupDocumentView groupDocumentView = new GroupDocumentView();

                groupDocumentView.setGroupDocsHeader(group.getShortName() + " Administrator Documents");

                groupDocumentView.setEditable(false);

                listAdminLocationDocumentsByRequest(groupDocumentView.getLocations(), request, request.getSites(),
                        RequestAdminLocationDocument.listByRequestIdAndGroupId(request.getId(), group.getId()), person,
                        group.getId(), false);

                listAdminParticipantDocumentsByRequest(groupDocumentView.getParticipants(), request, request.getParticipants(),
                        RequestAdminParticipantDocument.listByRequestIdAndGroupId(request.getId(), group.getId()), person,
                        group.getId(), false);

                result.getGroupDocuments().add(groupDocumentView);
            }

            return result;
        } catch (Exception e) {
            log.error("Error loading admin document list.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading admin document list.");
        } finally {
            HibernateSessionManager.close();
        }

    }

    /**
     * Used to retrieve the admin documents in the create wizard. (Reviewer)
     *
     * @param view
     *            the view
     * @return the object
     */
    @RequestMapping(value = "/listAdminReviewDocumentsForAllGroups", method = RequestMethod.POST)
    @ResponseBody
    public Object listAdminReviewDocumentsForAllGroups(@RequestBody @Valid final RequestIdView view) {

        log.debug("listAdminReviewDocumentsForAllGroups");

        if (view == null) {
            return new ErrorView("Error loading document list");
        }

        GroupDocumentListView result = new GroupDocumentListView();

        // TODO- pathetic hack!
        // (wanna explain it then?)
        multipartResolver.setMaxUploadSize(-1L);

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();
            Person reviewerPerson = Person.findById(prefs.getUserId());

            Role.initialize();
            Group.initialize();

            boolean hasEditableReviewerRole = false;
            if (reviewerPerson.hasRole(Role.NDS_ADMIN) || reviewerPerson.hasRole(Role.REVIEWER)) { // can possibly upload admin
                                                                                                   // documents?
                hasEditableReviewerRole = true;
            }

            Request request = null;
            try {
                request = RequestController.retrieveRequest(view.getRequestId());

            } catch (ObjectNotFoundException e) {
                log.error("Error loading request " + view.getRequestId(), e);
                return new ErrorView("Error loading request " + view.getRequestId());
            }

            if (request == null) {
                throw new ValidationException("Request not found with id " + view.getRequestId());
            }

            Activity activity = request.getActivity();
            if (activity == null) {
                log.error("Error loading document list: null activity.");
                return new ErrorView("Error loading document list.");
            }

            // verify the access permissions
            if (!activity.verifyReadAccessPermissions(reviewerPerson)) {
                log.error("Error loading document list: invalid user permissions.");
                return new ErrorView("Error loading document list.");
            }

            List<Group> reviewGroupList = new ArrayList<Group>();

            if (request.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST || request.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST) {

                Set<RequestWorkflow> openWorkflows = request.getWorkflows(true);
                if (openWorkflows != null) {
                    for (RequestWorkflow workflow : openWorkflows) {

                        if (workflow != null) {
                            if (workflow.getWorkflowTemplate() != null
                                    && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS) {
                                reviewGroupList.add(Group.NDS);
                            }

                            Set<Review> reviewSet = request.getReviews(workflow);
                            if (reviewSet != null) {
                                for (Review review : reviewSet) {
                                    reviewGroupList.add(review.getReviewer());
                                }
                            }
                        }
                    }
                }

            } else {

                reviewGroupList.add(Group.NDS);

                Set<Review> reviewSet = request.getAllReviews();
                if (reviewSet != null) {
                    for (Review currReview : reviewSet) {
                        reviewGroupList.add(currReview.getReviewer());
                    }
                }

            }

            for (Group group : reviewGroupList) {
                GroupDocumentView groupDocumentView = new GroupDocumentView();

                groupDocumentView.setGroupDocsHeader(group.getShortName() + " Administrator Documents");

                final boolean editableForReviewer = (hasEditableReviewerRole && reviewerPerson.inGroup(group));
                groupDocumentView.setEditable(editableForReviewer);

                listAdminLocationDocumentsByRequest(groupDocumentView.getLocations(), request, request.getSites(),
                        RequestAdminLocationDocument.listByRequestIdAndGroupId(request.getId(), group.getId()), reviewerPerson,
                        group.getId(), editableForReviewer);

                listAdminParticipantDocumentsByRequest(groupDocumentView.getParticipants(), request, request.getParticipants(),
                        RequestAdminParticipantDocument.listByRequestIdAndGroupId(request.getId(), group.getId()),
                        reviewerPerson, group.getId(), editableForReviewer);

                result.getGroupDocuments().add(groupDocumentView);
            }

        } catch (Exception e) {
            log.error("Error loading admin document list.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading admin document list.");
        } finally {
            HibernateSessionManager.close();
        }

        return result;
    }

    /**
     * Retrieve only the specified document (uploading one document at a time) Returns the most recent version in the version
     * chain for the requested doc id.
     *
     * @param view
     *            the view
     * @return the object
     */
    @RequestMapping(value = "/listDocumentById", method = RequestMethod.POST)
    @ResponseBody
    public Object listDocumentById(@RequestBody @Valid final DocumentIdView view) {

        log.debug("listDocumentById");

        if (view == null) {
            return new ErrorView("Error loading document list");
        }

        DocumentView dview = new DocumentView();

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }

            // TODO- pathetic hack!
            // (wanna explain it then?)
            multipartResolver.setMaxUploadSize(-1L);

            Document doc = Document.getCurrentOrMostRecentDocument(view.getDocumentId());
            if (doc == null) {
                return new ErrorView("Error loading document list");
            }

            Request request = doc.getRequest();
            if (request == null) {
                log.error("Error loading document: null request.");
                return new ErrorView("Error loading document list.");
            }

            Activity activity = request.getActivity();
            if (activity == null) {
                log.error("Error loading document: null activity.");
                return new ErrorView("Error loading document list.");
            }

            // verify the access permissions
            if (!activity.verifyReadAccessPermissions(person)) {
                log.error("Error loading document: invalid user permissions.");
                return new ErrorView("Error loading document list.");
            }

            Person reviewer = Person.findById(prefs.getUserId()); // used to retrieve any review information for this document
                                                                  // (ignored if not part of a review group)

            dview = populateDocumentView(doc, reviewer);
            populateDocumentVersionList(dview, view.getDocumentId()); // get all versions of this document

            dview.setEditable(true); // just uploaded this document, so it must be editable
            if (reviewer != null && reviewer.hasRole(Role.SUPER_USER)) { // documents are not editable for the DART
                                                                         // Admin (this should be an extra test)
                dview.setEditable(false);
            }

        } catch (Exception e) {
            log.error("Error loading document list", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading document list: " + e.getMessage());
        } finally {
            HibernateSessionManager.close();
        }

        return dview;
    }

    /**
     * List participant documents by request.
     *
     * @param result
     *            the result
     * @param request
     *            the request
     * @param participants
     *            the participants
     * @param prevRequestsList
     *            the prev requests list
     * @param reviewer
     *            the reviewer
     * @param workflow
     *            the workflow
     * @param dataSourceWorkflowSet
     *            the data source workflow set
     */
    @SuppressWarnings("unchecked")
    private void listParticipantDocumentsByRequest(final DocumentListView result, final Request request,
            final Set<Participant> participants,
            // final List<RequestParticipantDocument> rpdList,
            List<Integer> prevRequestsList, final Person reviewer, final RequestWorkflow workflow,
            // final int workflowTypeId) {
            final Set<RequestWorkflow> dataSourceWorkflowSet) {

        // retrieve the request chain once for this request (only care if this is an amendment)
        if (prevRequestsList == null) {
            if (request != null && request.isAmendment()) {
                prevRequestsList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }
        }

        //
        // get the review group specific info
        Group adminGroup = null;
        Set<DocumentTemplate> documentTemplatesToHide = null;
        Set<DocumentTemplate> independentDocumentTemplatesToShow = null;
        if (reviewer != null) {

            for (Group group : reviewer.getGroups()) {
                adminGroup = group; // grab the last group the user belongs to? why? Because we like it...
            }

            Role.initialize();
            if (!reviewer.hasRole(Role.SUPER_USER)) {

                if (adminGroup != null) {

                    documentTemplatesToHide = getDocumentsToHide(workflow, adminGroup);

                    if (workflow != null && workflow.getWorkflowTemplate() != null
                            && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_INDEPENDENT) {
                        independentDocumentTemplatesToShow = getIndependentDocumentsToShow(request, workflow);
                    }
                }
            }
        }

        // sort that participant list by name
        ArrayList<Participant> sortedList = new ArrayList<Participant>();
        sortedList.addAll(participants);
        Collections.sort(sortedList);

        // loop on participants so we don't list a participant more than once in the JSON result
        for (Participant part : sortedList) {
            RequestParticipantDocumentView requestParticipantDocumentView = null;
            boolean allDocsUploaded = true;

            List<RequestParticipantDocument> requestParticipantDocuments =
                    RequestParticipantDocument.listActiveDocsByRequestAndParticipantId(request.getId(), part.getId());
            if (requestParticipantDocuments != null) {
                for (RequestParticipantDocument requestParticipantDocument : requestParticipantDocuments) {
                    if (requestParticipantDocument.getParticipantId() == part.getId() 
                            && requestParticipantDocument.isActive()) {

                        if (requestParticipantDocumentView == null) {
                            // create the outer collection on demand
                            requestParticipantDocumentView = new RequestParticipantDocumentView();
                            requestParticipantDocumentView.setId(Integer.toString(part.getPerson().getId()));

                            String participantName =
                                    part.getPerson().getFullName() == null ? "" : part.getPerson().getFullName();

                            if (!OperationalRequest.class.isAssignableFrom(request.getClass())) {
                                if (part.getPrincipalInvestigator()) {
                                    participantName += " ( Principal Investigator )";
                                }
                            }

                            requestParticipantDocumentView.setParticipantName(participantName);
                        }

                        Document doc = Document.getCurrentOrMostRecentDocument(requestParticipantDocument.getDocumentId());
                        if (doc != null) {

                            DocumentTemplate docTemplate = doc.getDocumentTemplate();
                            if( ( adminGroup == null ||                 
                                  documentTemplatesToHide == null ||   
                                  (docTemplate != null && documentTemplatesToHide != null && documentTemplatesToHide.contains(docTemplate) == false) ) &&   
                                ( adminGroup == null ||                
                                  independentDocumentTemplatesToShow == null ||     
                                  (docTemplate != null && independentDocumentTemplatesToShow != null 
                                  && independentDocumentTemplatesToShow.contains(docTemplate) == true) ) ) {

                                DocumentView dview = populateDocumentView(doc, reviewer);

                                populateDocumentVersionList(dview, requestParticipantDocument.getDocumentId());

                                if (adminGroup != null) {

                                    populateDocumentReviewNotes(dview, request, prevRequestsList, doc, reviewer);

                                    populateDocumentReviewStatus(dview, request, prevRequestsList, doc, reviewer);
                                }

                                populateDocumentEditableStatus(dview, reviewer, doc, dataSourceWorkflowSet, true, false);

                                if (doc == null || !doc.isUploaded()) {
                                    allDocsUploaded = false;
                                }

                                try {

                                    if (doc != null && doc.isUploaded() || !request.isCompletedStatus()) {

                                        requestParticipantDocumentView.getDocuments().add(dview);
                                    }

                                } catch (ObjectNotFoundException e) {
                                    log.error("Error retrieving the document status: " + e.getMessage());
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }

            if (requestParticipantDocumentView != null) {
                ArrayList<DocumentView> dvlist = new ArrayList<DocumentView>();
                dvlist.addAll(requestParticipantDocumentView.getDocuments());
                Collections.sort(dvlist);
                requestParticipantDocumentView.getDocuments().clear();
                requestParticipantDocumentView.getDocuments().addAll(dvlist);

                requestParticipantDocumentView.setUploaded(allDocsUploaded);

                if (dvlist != null && dvlist.size() > 0) {
                    result.getParticipants().add(requestParticipantDocumentView);
                }
            }
        }

        Collections.sort(result.getParticipants());
    }

    /**
     * List admin participant documents by request.
     *
     * @param resultParticipants
     *            the result participants
     * @param request
     *            the request
     * @param participants
     *            the participants
     * @param rpdList
     *            the rpd list
     * @param reviewer
     *            the reviewer
     * @param groupId
     *            the group id
     * @param editable
     *            the editable
     */
    @SuppressWarnings("unchecked")
    private void listAdminParticipantDocumentsByRequest(List<RequestParticipantDocumentView> resultParticipants,
            final Request request, final Set<Participant> participants, final List<RequestAdminParticipantDocument> rpdList,
            final Person reviewer, final int groupId, final boolean editable) {

        Set<RequestWorkflow> dataSourceWorkflowSet = null;

        // sort that participant list by name
        ArrayList<Participant> sortedList = new ArrayList<Participant>();
        sortedList.addAll(participants);
        Collections.sort(sortedList);

        // loop on participants so we don't list a participant more than once in the JSON result
        for (Participant part : sortedList) {
            RequestParticipantDocumentView rpdView = null;
            boolean allDocsUploaded = true;

            // TODO- sort this list by document name
            for (RequestAdminParticipantDocument rpd : rpdList) {
                if (rpd.getParticipantId() == part.getId() && rpd.isActive()) {

                    if (rpdView == null) {
                        rpdView = new RequestParticipantDocumentView();
                        rpdView.setId(Integer.toString(part.getPerson().getId()));

                        if (groupId != -1) {
                            String groupParticpantId = groupId + "_" + part.getPerson().getId();
                            rpdView.setId(groupParticpantId);
                        }

                        String participantName = part.getPerson().getFullName() == null ? "" : part.getPerson().getFullName();

                        if (!OperationalRequest.class.isAssignableFrom(request.getClass())) {
                            if (part.getPrincipalInvestigator()) {
                                participantName += " ( Principal Investigator )";
                            }
                        }

                        rpdView.setParticipantName(participantName);

                        resultParticipants.add(rpdView);
                    }

                    Document doc = Document.getCurrentOrMostRecentDocument(rpd.getDocumentId());
                    if (doc != null) {

                        DocumentView dview = populateDocumentView(doc, null);
                        populateDocumentVersionList(dview, rpd.getDocumentId()); // get all versions of this document

                        populateDocumentEditableStatus(dview, reviewer, doc, dataSourceWorkflowSet, false, editable);

                        // if( !dview.isUploaded() ) {
                        if (doc == null || !doc.isUploaded()) {
                            allDocsUploaded = false; // haven't uploaded this document
                        }

                        rpdView.getDocuments().add(dview);
                    }
                }

            }

            // if there are any documents, sort them.
            if (rpdView != null) {
                ArrayList<DocumentView> dvlist = new ArrayList<DocumentView>();
                dvlist.addAll(rpdView.getDocuments());
                Collections.sort(dvlist);
                rpdView.getDocuments().clear();
                rpdView.getDocuments().addAll(dvlist);

                rpdView.setUploaded(allDocsUploaded); // have all documents been uploaded for this participant?
            }

        }

        Collections.sort(resultParticipants); // principal investigator first
    }

    /**
     * Compute document template requirement string.
     *
     * @param request
     *            the request
     * @param documentTemplateId
     *            the document template id
     * @param requestDataSources
     *            the request data sources
     * @return the string
     */
    private String computeDocumentTemplateRequirementString(final Request request, final int documentTemplateId, final Set<DataSource> requestDataSources) {
        List<DataSource> dsList = null; 

        //dsList = listDataSourcesByDocumentTemplateId(documentTemplateId);
        dsList = DataSource.findByDocumentTemplateId(documentTemplateId);
        
        final String requiredForStr = "Required for ";
        StringBuffer sb = new StringBuffer(requiredForStr);
        boolean first = true;
        
        if( requestDataSources != null ) {

            // As of 2/25/2015:  Real SSN Access Request no longer required for any Independent Approver Workflow
            boolean isRealSSNAccessRequest = false;
            DocumentTemplate temp = null;
            try {
                temp = DocumentTemplate.findById(documentTemplateId);
            } catch (ObjectNotFoundException e) {
                log.error("Error retrieving the Document Template for id: " + documentTemplateId);
                e.printStackTrace();
            }
            if( temp != null && temp.getName().startsWith("Real SSN Access Request") ) {
                isRealSSNAccessRequest = true;
            }//end if
            
            
            for (DataSource ds : DataSource.intersect(dsList, requestDataSources)) {
                boolean isIndependentOnly = false;
                if( isRealSSNAccessRequest == true ) {
                    isIndependentOnly = evaluateIndependentOnlyDataSource(request, ds); //is this an Independent-only data source attached to a new request?
                }

                if( isRealSSNAccessRequest == false || isIndependentOnly == false ) {
                    if (!first) {
                        sb.append(", ");
                    }
                    first = false;
                    sb.append(ds.getName());
                }//end if
            }
        }
        
        //if we have no required for info, assume it's the legacy DART system (this could also be caused by missing mappings in the DB table)
        //if( sb.toString().equals(requiredForStr) )
        if( first == true )
            sb.append("Legacy DART");
        
        return sb.toString();
    }
    

    /**
     * Checks if is document template real ssn access request.
     *
     * @param documentTemplate
     *            the document template
     * @return true, if is document template real ssn access request
     */
    private boolean isDocumentTemplateRealSSNAccessRequest(DocumentTemplate documentTemplate) {
        return documentTemplate != null && documentTemplate.getName() != null
                && documentTemplate.getName().startsWith(REAL_SSN_ACCESS_REQUEST);
    }

    /**
     * Evaluate independent only data source.
     *
     * @param req
     *            the req
     * @param ds
     *            the ds
     * @return true, if successful
     */
    private static boolean evaluateIndependentOnlyDataSource(final Request req, final DataSource ds) {

        if (req != null && req.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST || req.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST) { // new request

            Set<WorkflowTemplate> dataSourceWorkflowTemplates = ds.getWorkflowTemplates();
            if (dataSourceWorkflowTemplates != null) {
                for (WorkflowTemplate workflowTemplate : dataSourceWorkflowTemplates) {

                    if (workflowTemplate != null && workflowTemplate.getWorkflowTypeId() != WorkflowResolver.WF_INDEPENDENT) {
                        return false;
                    }
                }
            }
            return true;
        }

        return false;
    }

    /**
     * Retrieve the location-specific documents.
     * 
     * If this is a reviewer (adminGroup != null), only add the document to the result if it is valid for this review group
     * (display)
     *
     * @param result
     *            the result
     * @param request
     *            the request
     * @param locations
     *            the locations
     * @param prevRequestsList
     *            the prev requests list
     * @param reviewer
     *            the reviewer
     * @param workflow
     *            the workflow
     * @param dataSourceWorkflowSet
     *            the data source workflow set
     */
    @SuppressWarnings("unchecked")
    private void listLocationDocumentsByRequest(final DocumentListView result, final Request request,
            final Set<Location> locations, List<Integer> prevRequestsList, final Person reviewer,
            final RequestWorkflow workflow, final Set<RequestWorkflow> dataSourceWorkflowSet) {

        if (prevRequestsList == null) {
            if (request != null && request.isAmendment()) {
                prevRequestsList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }
        }

        Group adminGroup = null;
        Set<DocumentTemplate> documentTemplatesToHide = null;
        Set<DocumentTemplate> independentDocumentTemplatesToShow = null;
        if (reviewer != null) {

            for (Group group : reviewer.getGroups()) {
                adminGroup = group;
            }

            Role.initialize();
            if (!reviewer.hasRole(Role.SUPER_USER)) {

                if (adminGroup != null) {
                    documentTemplatesToHide = getDocumentsToHide(workflow, adminGroup);
                    if (workflow != null && workflow.getWorkflowTemplate() != null
                            && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_INDEPENDENT) {
                        independentDocumentTemplatesToShow = getIndependentDocumentsToShow(request, workflow);
                    }
                }
            }
        }

        // sort that location list by name
        ArrayList<Location> sortedList = new ArrayList<Location>();
        sortedList.addAll(locations);
        Collections.sort(sortedList);

        // loop on locations so we don't list a location more than once in the JSON result
        for (Location loc : sortedList) {
            RequestLocationDocumentView rldView = new RequestLocationDocumentView();
            boolean allDocsUploaded = true;

            // Does this location happen to be the primary site for the study?
            String siteName = loc.getName();
            if (!OperationalRequest.class.isAssignableFrom(request.getClass())) {
                if (request.getPrimaryLocation() != null && request.getPrimaryLocation().getId() == loc.getId()) {
                    siteName += " ( Primary Site )";
                }
            }

            rldView.setLocationName(siteName);
            rldView.setId(Integer.toString(loc.getId()));

            List<RequestLocationDocument> rldList =
                    RequestLocationDocument.listActiveDocsByRequestAndLocationId(request.getId(), loc.getId());
            if (rldList != null) {
                for (RequestLocationDocument rld : rldList) {
                    if (rld.getLocationId() == loc.getId() && rld.isActive()) {

                        Document doc = Document.getCurrentOrMostRecentDocument(rld.getDocumentId());
                        if (doc != null) {

                            DocumentTemplate docTemplate = doc.getDocumentTemplate();

                            if( ( adminGroup == null ||                
                                    documentTemplatesToHide == null ||    
                                    (docTemplate != null && documentTemplatesToHide != null && documentTemplatesToHide.contains(docTemplate) == false) ) &&
                                  ( adminGroup == null ||                
                                    independentDocumentTemplatesToShow == null ||     
                                    (docTemplate != null && independentDocumentTemplatesToShow != null && independentDocumentTemplatesToShow.contains(docTemplate) == true) ) ) { 
                                DocumentView dview = populateDocumentView(doc, reviewer);

                                populateDocumentVersionList(dview, rld.getDocumentId());

                                if (adminGroup != null) {
                                    populateDocumentReviewNotes(dview, request, prevRequestsList, doc, reviewer);
                                    populateDocumentReviewStatus(dview, request, prevRequestsList, doc, reviewer);
                                }

                                populateDocumentEditableStatus(dview, reviewer, doc, dataSourceWorkflowSet, true, false);

                                if (doc == null || !doc.isUploaded()) {
                                    allDocsUploaded = false;
                                }

                                try {

                                    if (doc != null && doc.isUploaded() || !request.isCompletedStatus()) {

                                        rldView.getDocuments().add(dview);
                                    }
                                } catch (ObjectNotFoundException e) {
                                    e.printStackTrace();
                                }

                            }
                        }

                    }
                }
            }

            // sort this list by document sort order
            ArrayList<DocumentView> dvlist = new ArrayList<DocumentView>();
            dvlist.addAll(rldView.getDocuments());
            Collections.sort(dvlist);
            rldView.getDocuments().clear();
            rldView.getDocuments().addAll(dvlist);

            rldView.setUploaded(allDocsUploaded); // have all documents been uploaded for this location?

            if (dvlist != null && dvlist.size() > 0) { // only add this location if we have some documents for it
                result.getLocations().add(rldView);
            }
        }

        Collections.sort(result.getLocations()); // primary location first
    }





    /**
     * List admin location documents by request.
     *
     * @param resultLocations
     *            the result locations
     * @param request
     *            the request
     * @param locations
     *            the locations
     * @param rldList
     *            the rld list
     * @param reviewer
     *            the reviewer
     * @param groupId
     *            the group id
     * @param editable
     *            the editable
     */
    @SuppressWarnings("unchecked")
    private void listAdminLocationDocumentsByRequest(List<RequestLocationDocumentView> resultLocations, final Request request,
            final Set<Location> locations, final List<RequestAdminLocationDocument> rldList, final Person reviewer,
            final int groupId, final boolean editable) {

        Set<RequestWorkflow> dataSourceWorkflowSet = null;

        // sort that location list by name
        ArrayList<Location> sortedList = new ArrayList<Location>();
        sortedList.addAll(locations);
        Collections.sort(sortedList);

        // loop on locations so we don't list a location more than once in the JSON result
        for (Location loc : sortedList) {
            RequestLocationDocumentView rldView = new RequestLocationDocumentView();
            boolean allDocsUploaded = true;

            // Does this location happen to be the primary site for the study?
            String siteName = loc.getName();
            if (!OperationalRequest.class.isAssignableFrom(request.getClass())) {
                if (request.getPrimaryLocation() != null && request.getPrimaryLocation().getId() == loc.getId()) {
                    siteName += " ( Primary Site )";
                }
            }

            rldView.setLocationName(siteName);
            rldView.setId(Integer.toString(loc.getId())); // made this a string so that we can concatenate the groupId and the
                                                          // locationId

            // For group-based documents: need a different ID to key off of (need something unique for each group and each
            // location)
            if (groupId != -1) { // add the groupID to the locationID
                String groupLocationId = groupId + "_" + loc.getId();
                rldView.setId(groupLocationId);
            }

            // find all documents associated with the request
            for (RequestAdminLocationDocument rld : rldList) {
                if (rld.getLocationId() == loc.getId() && rld.isActive()) {

                    Document doc = Document.getCurrentOrMostRecentDocument(rld.getDocumentId());
                    if (doc != null) {

                        DocumentView dview = populateDocumentView(doc, null);
                        populateDocumentVersionList(dview, rld.getDocumentId()); // get all versions of this document

                        populateDocumentEditableStatus(dview, reviewer, doc, dataSourceWorkflowSet, false, editable);

                        if (doc == null || !doc.isUploaded()) {
                            allDocsUploaded = false;
                        }

                        rldView.getDocuments().add(dview);
                    }
                }
            }

            ArrayList<DocumentView> dvlist = new ArrayList<DocumentView>();
            dvlist.addAll(rldView.getDocuments());
            Collections.sort(dvlist);
            rldView.getDocuments().clear();
            rldView.getDocuments().addAll(dvlist);

            rldView.setUploaded(allDocsUploaded);

            if (dvlist != null && dvlist.size() > 0) {
                resultLocations.add(rldView);
            }
        }

        Collections.sort(resultLocations);
    }

    /**
     * List document templates.
     *
     * @param request
     *            the request
     * @return the object
     */
    @RequestMapping(value = "/listDocumentTemplates", method = RequestMethod.GET)
    @ResponseBody
    public Object listDocumentTemplates(final HttpServletRequest request) {
        log.debug("listDocumentTemplates");
        try {
            HibernateSessionManager.start();
            DocumentTemplateListView result = new DocumentTemplateListView();

            for (DocumentTemplate temp : DocumentTemplate.listAll()) {
                DocumentTemplateView view = new DocumentTemplateView();
                view.setId(temp.getId());
                view.setName(temp.getName());

                String descrStr = temp.getDescription();
                if (descrStr == null || descrStr.isEmpty()) {
                    descrStr = temp.getName();
                }

                view.setDescription(descrStr);

                view.setLocation(temp.isLocation());
                view.setParticipant(temp.isParticipant());
                view.setPrincipalInvestigator(temp.isPrincipalInvestigator());
                view.setAdministrator(temp.isAdministrator());
                view.setActivityType(temp.getActivityType());
                view.setSortOrder(temp.getSortOrder());

                result.getTemplates().add(view);
            }

            return result;
        } catch (Exception e) {
            log.error("Error loading document list.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error loading document list.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * Populate document view.
     *
     * @param doc
     *            the doc
     * @param reviewer
     *            the reviewer
     * @return the document view
     */
    private DocumentView populateDocumentView(final Document doc, final Person reviewer) {

        DocumentView dview = new DocumentView();

        if (doc == null) {
            return dview;
        }

        dview.setId(doc.getId());
        dview.setName(doc.getName());
        dview.setSortOrder(doc.getSortOrder());
        dview.setHeadId(doc.getHead());

        String createdBy;
        try {
            createdBy = Person.findByName(doc.getCreatedBy()).getFullName();
        } catch (ValidationException e) {
            createdBy = doc.getCreatedBy();
        } catch (ObjectNotFoundException e) {
            createdBy = doc.getCreatedBy();
        }

        dview.setCreatedBy(createdBy);
        dview.setCreatedOn(SDF.format(doc.getCreatedOn()));

        String updatedBy;
        try {
            updatedBy = Person.findByName(doc.getUpdatedBy()).getFullName();
        } catch (ValidationException e) {
            updatedBy = doc.getUpdatedBy();
        } catch (ObjectNotFoundException e) {
            updatedBy = doc.getUpdatedBy();
        }

        dview.setUpdatedBy(updatedBy);
        dview.setUpdatedOn(doc.getUpdatedOn() == null ? "" : LONG_FORMAT_SDF.format(doc.getUpdatedOn()));
        dview.setVersionNumber(doc.getVersionNumber());

        String descrStr = doc.getDescription();
        if (descrStr == null || descrStr.isEmpty()) {
            descrStr = doc.getName();
        }

        dview.setDescription(descrStr);
        dview.setUploaded(doc.getUpdatedBy() != null);

        String requirementString =
                computeDocumentTemplateRequirementString(doc.getRequest(), doc.getDocumentTemplate().getId(), doc.getRequest()
                        .getDataSources());
        dview.setRequirement(requirementString);

        return dview;
    }

    /**
     * Is the requestor document editable (can be uploaded)?.
     *
     * @param documentView
     *            the document view
     * @param person
     *            the person
     * @param document
     *            the document
     * @param dataSourceWorkflowSet
     *            the data source workflow set
     * @param isRequestorDoc
     *            the is requestor doc
     * @param editableForReviewer
     *            the editable for reviewer
     */
    private void populateDocumentEditableStatus(DocumentView documentView, final Person person, final Document document,
            final Set<RequestWorkflow> dataSourceWorkflowSet, final boolean isRequestorDoc, final boolean editableForReviewer) {

        if (documentView != null) {

            if (person != null && person.hasRole(Role.SUPER_USER)) {
                documentView.setEditable(false);

            } else {

                if (!isRequestorDoc) {
                    documentView.setEditable(editableForReviewer);
                } else {
                    if (document != null) {

                        boolean editable =
                                isRequestorDocumentEditable(document.getRequest(), dataSourceWorkflowSet,
                                        document.getDocumentTemplate());
                        documentView.setEditable(editable);
                    }
                }
            }
        }
    }

    /**
     * Determines if the requestor document is editable (can be uploaded).
     * 
     * For completed NDS requests with another workflow pending resolution, prevent replacement of the following documents:
     * Research Request Memo Research Protocol CDW Domain Checklist VA Form 9957 (no longer required as of 3/23/2015) Vital
     * Status Rules of Behavior EHR Access Request Form
     * 
     * The following documents are common to all workflows and may be changed by Change Request: Research Study Institutional
     * Review Board (IRB) Approval Letter Sample Informed Consent and HIPAA Authorization Research and Development (RD)
     * Committee Approval Letter IRB Approval of Waiver of HIPAA-Compliant Authorization Real SSN Access Request
     *
		The following documents are common to all workflows and may be changed by Change Request:
				Research Study Institutional Review Board (IRB) Approval Letter
				Sample Informed Consent and HIPAA Authorization
				Research and Development (RD) Committee Approval Letter
				IRB Approval of Waiver of HIPAA-Compliant Authorization
				Real SSN Access Request

     * @param request
     *            the request
     * @param dataSourceWorkflowSet
     *            the data source workflow set
     * @param documentTemplate
     *            the document template
     * @return true, if is requestor document editable
     */
    private boolean isRequestorDocumentEditable(final Request request, final Set<RequestWorkflow> dataSourceWorkflowSet,
            final DocumentTemplate documentTemplate) {

        if (request != null && documentTemplate != null) {

            try {
                if (!request.isEditable(null)) {
                    return false;
                }

                // if there are no workflows attached to the request, the document is editable (nothing being processed yet)
                Set<RequestWorkflow> workflowSet = request.getWorkflows(true);
                if (workflowSet == null || workflowSet.isEmpty()) {
                    return true;
                }

                // if the document is not attached to any workflow, the document is editable (not locked by any workflow)
                Set<WorkflowTemplate> docWorkflowTemplateSet = documentTemplate.getWorkflowTemplates();
                if (docWorkflowTemplateSet == null || docWorkflowTemplateSet.isEmpty()) {
                    return true;
                }

                boolean hasWorkflows = false;
                if (docWorkflowTemplateSet != null && !docWorkflowTemplateSet.isEmpty()) {

                    for (WorkflowTemplate template : docWorkflowTemplateSet) {
                        if (template != null) {

                            RequestWorkflow workflow =
                                    RequestWorkflow.findOpenByRequestAndWorkflowTemplateId(request.getId(), template.getId());
                            if (workflow != null && !workflow.isClosed()) {

                                if (dataSourceWorkflowSet != null && dataSourceWorkflowSet.contains(workflow)) {

                                    hasWorkflows = true;

                                    if (request.isEditable(workflow)) {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }

                if (hasWorkflows) {
                    return false;
                }
            } catch (ObjectNotFoundException e) {
                log.error("Error retrieving the request status:  " + e.getMessage());
                e.printStackTrace();
            }

        }

        return true;
    }

    /**
     * Get the current workflows for this request and its selected data sources. Used to determine when to allow required
     * documents to be uploaded.
     *
     * @param request
     *            the request
     * @return the open workflows for selected data sources
     */
    private Set<RequestWorkflow> getOpenWorkflowsForSelectedDataSources(final Request request) {

        Set<WorkflowTemplate> dataSourcesWorkflowTemplates = new HashSet<WorkflowTemplate>();

        Set<DataSource> requestDataSources = request.getDataSources(); // currently selected data sources
        if (requestDataSources != null) {

            for (DataSource currDataSource : requestDataSources) {
                if (currDataSource != null) {

                    Set<WorkflowTemplate> currDataSourceWorkflowTemplateSet = currDataSource.getWorkflowTemplates();
                    if (currDataSourceWorkflowTemplateSet != null) {

                        for (WorkflowTemplate workflowTemplate : currDataSourceWorkflowTemplateSet) {

                            if (workflowTemplate != null) {
                                if (!dataSourcesWorkflowTemplates.contains(workflowTemplate)) {
                                    dataSourcesWorkflowTemplates.add(workflowTemplate);
                                }
                            }
                        }
                    }
                }
            }
        }

        Set<RequestWorkflow> dataSourceWorkflowSet = new HashSet<RequestWorkflow>();
        if (dataSourcesWorkflowTemplates != null && !dataSourcesWorkflowTemplates.isEmpty()) {
            for (WorkflowTemplate workflowTemplate : dataSourcesWorkflowTemplates) {

                RequestWorkflow dataSourceWorkflow =
                        RequestWorkflow.findOpenByRequestAndWorkflowTemplateId(request.getId(), workflowTemplate.getId());
                if (dataSourceWorkflow != null) {
                    if (!dataSourceWorkflowSet.contains(dataSourceWorkflow)) {
                        dataSourceWorkflowSet.add(dataSourceWorkflow);
                    }
                }
            }
        }

        return dataSourceWorkflowSet;
    }

    /**
     * Display specific documents if this is an Independent Approver Workflow.
     * 
     * Displayed to the Reviewer. (Requestor and DART Admin see all documents.)
     *
     * @param request
     *            the request
     * @param workflow
     *            the workflow
     * @return the independent documents to show
     */
    private Set<DocumentTemplate> getIndependentDocumentsToShow(final Request request, final RequestWorkflow workflow) {
        Set<DocumentTemplate> documentsToShow = new HashSet<DocumentTemplate>();

        if (workflow != null && workflow.getWorkflowTemplate() != null) {
            WorkflowTemplate workflowTemplate = workflow.getWorkflowTemplate();

            if (workflowTemplate.getWorkflowTypeId() == WorkflowResolver.WF_INDEPENDENT) {

                Set<DataSource> requestDataSources = request.getDataSources();
                Set<DataSource> workflowDataSources = workflowTemplate.getDataSources();

                if (requestDataSources != null && workflowDataSources != null) {

                    List<DataSource> selectedWorkflowDataSources =
                            DataSource.intersect(requestDataSources, workflowDataSources);
                    if (selectedWorkflowDataSources != null) {
                        for (DataSource currDataSource : selectedWorkflowDataSources) {
                            if (currDataSource != null) {

                                Set<DocumentTemplate> dataSourceDocs = currDataSource.getDocumentTemplates();
                                if (dataSourceDocs != null) {

                                    boolean isIndependentOnly = evaluateIndependentOnlyDataSource(request, currDataSource);
                                    if (isIndependentOnly) {
                                        for (DocumentTemplate documentTemplate : dataSourceDocs) {

                                            boolean isRealSSNAccessRequest = false;
                                            if (isDocumentTemplateRealSSNAccessRequest(documentTemplate)) {
                                                isRealSSNAccessRequest = true;
                                            }

                                            if (!isRealSSNAccessRequest) {
                                                documentsToShow.add(documentTemplate);
                                            }
                                        }

                                    } else {
                                        documentsToShow.addAll(dataSourceDocs);
                                    }
                                }
                            }
                        }
                    }
                }

                Set<DocumentTemplate> independentDocumentTemplatesToshow =
                        workflowTemplate.getDocumentTemplatesToShowIndependent();
                if (independentDocumentTemplatesToshow != null) {
                    documentsToShow.addAll(independentDocumentTemplatesToshow);
                }
            }
        }

        return documentsToShow;
    }

    /**
     * Hide the Independent Workflow documents, based on the workflow and review group.
     * 
     * For NDS Reviewer, display all documents on the Documents page except those specifically for an Independent data source.
     * 
     * For ORD Reviewer, display all documents on the Documents page except those specifically for an Independent data source.
     * 
     * For Privacy Reviewer, display all documents on the Documents page except those specifically for an Independent data
     * source.
     * 
     * For Security Reviewer, do not display the following documents on the Documents page: EHR Access Request Form Those
     * specific to an Independent Data Source
     * 
     * For CAPRI/VistAWeb Reviewer, do not display the following documents on the Documents page: OEF-OIF Data Use Agreement CDW
     * Domain Checklist Vital Status Rules of Behavior Those specific to an Independent Data Source
     * 
     * For Homeless Reviewer, do not display the following documents on the Documents page: EHR Access Request Form OEF-OIF Data
     * Use Agreement CDW Domain Checklist Vital Status Rules of Behavior Those specific to an Independent Data Source
     *
     * @param workflow
     *            the workflow
     * @param group
     *            the group
     * @return the documents to hide
     */
    private Set<DocumentTemplate> getDocumentsToHide(final RequestWorkflow workflow, final Group group) {
        Set<DocumentTemplate> documentsToHide = new HashSet<DocumentTemplate>();

        if (workflow != null && workflow.getWorkflowTemplate() != null) {

            if (workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS) {
                documentsToHide.addAll(getIndependentOnlyDocuments());
                if (group != null) {
                    Set<DocumentTemplate> groupDocsToHide = group.getDocumentTemplatesToHide();
                    if (groupDocsToHide != null) {
                        documentsToHide.addAll(groupDocsToHide);
                    }
                }
            }
        }

        return documentsToHide;
    }

    /**
     * Gets the independent only documents.
     *
     * @return the independent only documents
     */
    private Set<DocumentTemplate> getIndependentOnlyDocuments() {
        Set<DocumentTemplate> independentOnlyDocs = new HashSet<DocumentTemplate>();
        Set<DocumentTemplate> independentDocTemplates = new HashSet<DocumentTemplate>();

        List<WorkflowTemplate> independentWorkflows = WorkflowTemplate.listByWorkflowTypeId(WorkflowResolver.WF_INDEPENDENT);
        if (independentWorkflows != null) {

            for (WorkflowTemplate workflowTemplate : independentWorkflows) {
                if (workflowTemplate != null) {

                    Set<DocumentTemplate> docTemplates = workflowTemplate.getDocumentTemplates();
                    if (docTemplates != null) {
                        independentDocTemplates.addAll(docTemplates);
                    }
                }
            }
        }

        if (independentDocTemplates != null && independentDocTemplates.size() > 0) {
            for (DocumentTemplate docTemplate : independentDocTemplates) {
                if (docTemplate != null) {
                    boolean hasNonIndependentWorkflow = false;

                    Set<WorkflowTemplate> workflowTemplates = docTemplate.getWorkflowTemplates();
                    if (workflowTemplates != null && workflowTemplates.size() > 0) {
                        for (WorkflowTemplate currWorkflow : workflowTemplates) {
                            if (currWorkflow != null) {
                                if (currWorkflow.getWorkflowTypeId() != WorkflowResolver.WF_INDEPENDENT) {
                                    hasNonIndependentWorkflow = true;
                                    break;
                                }
                            }
                        }

                        if (!hasNonIndependentWorkflow) {
                            independentOnlyDocs.add(docTemplate);
                        }
                    }
                }
            }
        }

        return independentOnlyDocs;
    }

    /**
     * Populate document version list.
     *
     * @param dview
     *            the dview
     * @param origDocId
     *            the orig doc id
     */
    @SuppressWarnings("unchecked")
    private void populateDocumentVersionList(DocumentView dview, final int origDocId) {

        if (dview != null) { // && dview.isUploaded() ) {

            List<Document> docList = Document.listVersionsById(origDocId);
            if (docList != null) {

                for (Document currDoc : docList) {

                    if (currDoc.isNotCurrent()) {

                        DocumentVersionView docVersionView = new DocumentVersionView();

                        docVersionView.setId(currDoc.getId());
                        docVersionView.setName(currDoc.getName());
                        docVersionView.setVersionNumber(currDoc.getVersionNumber());

                        String versionUpdatedBy;
                        try {
                            versionUpdatedBy = Person.findByName(currDoc.getUpdatedBy()).getFullName();
                        } catch (ValidationException e) {
                            versionUpdatedBy = currDoc.getUpdatedBy();
                        } catch (ObjectNotFoundException e) {
                            versionUpdatedBy = currDoc.getUpdatedBy();
                        }
                        docVersionView.setUpdatedBy(versionUpdatedBy);
                        docVersionView.setUpdatedOn(currDoc.getUpdatedOn() == null ? "" : LONG_FORMAT_SDF.format(currDoc
                                .getUpdatedOn()));

                        dview.getDocVersions().add(docVersionView);
                    }
                }

                if (docList.size() == 1)
                    dview.setOnlyOneVersion(true);
                else
                    dview.setOnlyOneVersion(false);

                if (dview.getDocVersions()!=null && dview.getDocVersions().size() > 1) {
                	Collections.sort(dview.getDocVersions());
                }
            }
        }
    }

    /**
     * Populate document review notes.
     *
     * @param documentView
     *            the document view
     * @param req
     *            the req
     * @param prevRequestsList
     *            the prev requests list
     * @param doc
     *            the doc
     * @param reviewer
     *            the reviewer
     */
    private void populateDocumentReviewNotes(DocumentView documentView, final Request req, List<Integer> prevRequestsList,
            final Document doc, final Person reviewer) {

        if (documentView != null) {
            if (req != null && doc != null && reviewer != null) {

                List<DocumentReviewNote> noteList = new ArrayList<DocumentReviewNote>();

                Group adminGroup = null;

                for (Group group : reviewer.getGroups()) {
                    adminGroup = group; // grab the last group the user belongs to? why? Because we like it...
                }

                if (adminGroup != null) {

                    List<DocumentReviewNote> currRequestDocNotes =
                            DocumentReviewNote.listByGroupIdForDocumentVersions(doc.getHead(), adminGroup.getId());
                    if (currRequestDocNotes != null && currRequestDocNotes.size() > 0) {
                        noteList.addAll(currRequestDocNotes);
                    }

                    if (req.isAmendment()) {

                        Content docContent = DocumentController.getHeadDocumentContent(doc);

                        if (prevRequestsList == null && req.isAmendment()) {
                            prevRequestsList = Request.listAllPreviousRequestIds(req.getHeadId(), req.getTrackingNumber());
                        }

                        if (prevRequestsList != null) {

                            for (Integer prevRequestId : prevRequestsList) {

                                if (docContent != null) {
                                    List<Document> documentList =
                                            Document.listByRequestAndNonNullContentId(prevRequestId, docContent.getId());
                                    if (documentList != null && documentList.size() > 0) {

                                        for (Document prevDoc : documentList) { 
                                        	int cnt = DocumentReviewNote.countDocumentVersions(prevDoc.getHead(),adminGroup.getId());
                                        	if (cnt > 0) {
                                        		List<DocumentReviewNote> prevRequestDocNotes =
                                                    DocumentReviewNote.listByGroupIdForDocumentVersions(prevDoc.getHead(),
                                                            adminGroup.getId());
                                        		if (prevRequestDocNotes != null && prevRequestDocNotes.size() > 0) {
                                                noteList.addAll(prevRequestDocNotes);
                                        		}
                                        	}
                                        }

                                        Document prevDoc = documentList.get(0);
                                        docContent = DocumentController.getHeadDocumentContent(prevDoc);
                                    }
                                }

                            }
                        }
                    }

                    if (noteList != null && noteList.size() > 0) {

                        Collections.sort(noteList, DocumentReviewNote.getDescCreatedOnComparator());

                        for (DocumentReviewNote drn : noteList) {
                            DocumentReviewNoteView drnView = new DocumentReviewNoteView();

                            String noteCreatedBy = "";
                            try {
                                noteCreatedBy = Person.findByName(drn.getCreatedBy()).getFullName();
                            } catch (ValidationException e) {
                                noteCreatedBy = drn.getCreatedBy();
                            } catch (ObjectNotFoundException e) {
                                noteCreatedBy = drn.getCreatedBy();
                            }

                            drnView.setDocumentId(doc.getId());
                            drnView.setNote(drn.getText());
                            drnView.setCreated(drn.getCreatedOn() == null ? "" : SDF.format(drn.getCreatedOn()));
                            drnView.setCreatedBy(noteCreatedBy);
                            drnView.setId(drn.getId());

                            documentView.getReviewNotes().add(drnView);
                        }
                    }
                }
            }
        }
    }

    /**
     * Populate document review status.
     *
     * @param dview
     *            the dview
     * @param req
     *            the req
     * @param prevRequestsList
     *            the prev requests list
     * @param doc
     *            the doc
     * @param reviewer
     *            the reviewer
     */
    private void populateDocumentReviewStatus(DocumentView dview, final Request req, List<Integer> prevRequestsList,
            final Document doc, final Person reviewer) {

        if (dview != null) {
            if (req != null && doc != null && reviewer != null) {

                DocumentReviewStatus drs = null;

                Group adminGroup = null;

                for (Group group : reviewer.getGroups()) {
                    adminGroup = group; // grab the last group the user belongs to? why? Because we like it...
                }

                if (adminGroup != null) {

                    try {
                        drs = DocumentReviewStatus.findByGroupId(doc.getId(), adminGroup.getId());
                    } catch (ObjectNotFoundException e) {
                    }

                    if (drs != null) {

                        dview.setReviewStatus(drs.getStatus());
                        return;
                    }

                    if (req.isAmendment()) {

                        if (prevRequestsList == null && req.isAmendment()) {
                            prevRequestsList = Request.listAllPreviousRequestIds(req.getHeadId(), req.getTrackingNumber());
                        }

                        if (prevRequestsList != null) {
                            for (Integer prevRequestId : prevRequestsList) {
                                if (doc.getContent() != null) {
                                    List<Document> documentList =
                                            Document.listByRequestAndNonNullContentId(prevRequestId, doc.getContent().getId());
                                    if (documentList != null && documentList.size() > 0) {

                                        for (Document prevDoc : documentList) {
                                            try {
                                            	int count = DocumentReviewStatus.countGroupId(prevDoc.getId(), adminGroup.getId());
                                            	if (count > 0) {
                                            		drs = DocumentReviewStatus.findByGroupId(prevDoc.getId(), adminGroup.getId());
                                            	}
                                            } catch (ObjectNotFoundException e) {
                                            }

                                            if (drs != null) {

                                                dview.setReviewStatus(drs.getStatus());
                                                return;
                                            }
                                        }
                                    }
                                }
                            }

                        }
                    }
                }
            }
        }
    }

    /**
     * List documents.
     *
     * @param requestId
     *            the request id
     * @return the object
     * @throws Exception
     *             the exception
     */
    @RequestMapping(value = "/requests/{requestId}/documents", method = RequestMethod.GET)
    @ResponseBody
    @Deprecated
    public Object listDocuments(@Valid @PathVariable int requestId) throws Exception {
        /* Deprecated REST Services */
        log.debug("listDocuments");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }
            Role.initialize();

            if (!person.hasRole(Role.SUPER_USER)) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Request request = RequestController.retrieveRequest(requestId);
            if (request == null) {
                throw new ValidationException("Request not found with id " + requestId);
            }

            DocumentListView result = new DocumentListView();

            Person reviewer = Person.findById(prefs.getUserId());

            List<Integer> prevRequestIDList = null;
            if (request != null && request.isAmendment()) {
                prevRequestIDList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }

            RequestWorkflow workflow = null;
            Set<RequestWorkflow> dataSourceWorkflowSet = null;

            listLocationDocumentsByRequest(result, request, request.getSites(), prevRequestIDList, reviewer, workflow,
                    dataSourceWorkflowSet);

            listParticipantDocumentsByRequest(result, request, request.getParticipants(), prevRequestIDList, reviewer,
                    workflow, dataSourceWorkflowSet);

            return result;
        } catch (Exception e) {
            log.error("Error loading document list", e);
            HibernateSessionManager.rollback();
            throw e;
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * List documents locations.
     *
     * @param requestId
     *            the request id
     * @return the object
     * @throws Exception
     *             the exception
     */
    @RequestMapping(value = "/requests/{requestId}/documents/locations", method = RequestMethod.GET)
    @ResponseBody
    public Object listDocumentsLocations(@Valid @PathVariable int requestId) throws Exception {

        log.debug("listDocument Locations");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }
            Role.initialize();

            if (!person.hasRole(Role.SUPER_USER)) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Request request = RequestController.retrieveRequest(requestId);
            if (request == null) {
                throw new ValidationException("Request not found with id " + requestId);
            }

            DocumentListView tmpResult = new DocumentListView();

            Person reviewer = Person.findById(prefs.getUserId());

            // retrieve the request chain once for this request (only care if this is an amendment)
            List<Integer> prevRequestIDList = null;
            if (request != null && request.isAmendment()) {
                prevRequestIDList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }

            RequestWorkflow workflow = null;
            Set<RequestWorkflow> dataSourceWorkflowSet = null;

            listLocationDocumentsByRequest(tmpResult, request, request.getSites(), prevRequestIDList, reviewer, workflow,
                    dataSourceWorkflowSet);

            RequestLocationDocumentListView result = new RequestLocationDocumentListView();
            result.getLocations().clear();
            result.getLocations().addAll(tmpResult.getLocations());
            return result;
        } catch (Exception e) {
            log.error("Error loading document list", e);
            HibernateSessionManager.rollback();
            throw e;
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * List documents participants.
     *
     * @param requestId
     *            the request id
     * @return the object
     * @throws Exception
     *             the exception
     */
    @RequestMapping(value = "/requests/{requestId}/documents/participants", method = RequestMethod.GET)
    @ResponseBody
    public Object listDocumentsParticipants(@Valid @PathVariable int requestId) throws Exception {

        log.debug("listDocument Participants");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }
            Role.initialize();

            if (!person.hasRole(Role.SUPER_USER)) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Request request = RequestController.retrieveRequest(requestId);
            if (request == null) {
                throw new ValidationException("Request not found with id " + requestId);
            }

            DocumentListView tmpResult = new DocumentListView();

            Person reviewer = Person.findById(prefs.getUserId());

            List<Integer> prevRequestIDList = null;
            if (request != null && request.isAmendment()) {
                prevRequestIDList = Request.listAllPreviousRequestIds(request.getHeadId(), request.getTrackingNumber());
            }

            RequestWorkflow workflow = null;
            Set<RequestWorkflow> dataSourceWorkflowSet = null;

            listParticipantDocumentsByRequest(tmpResult, request, request.getParticipants(), prevRequestIDList, reviewer,
                    workflow, dataSourceWorkflowSet);

            RequestParticipantDocumentListView result = new RequestParticipantDocumentListView();
            result.getParticipants().clear();
            result.getParticipants().addAll(tmpResult.getParticipants());
            return result;
        } catch (Exception e) {
            log.error("Error loading document list", e);
            HibernateSessionManager.rollback();
            throw e;
        } finally {
            HibernateSessionManager.close();
        }
    }
}
