/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.modules.eqlext.ejb;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.integrator.ActionContext;
import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.SecurityException;
import com.queplix.core.jxb.entity.Dataset;
import com.queplix.core.jxb.entity.Efield;
import com.queplix.core.jxb.entity.Entity;
import com.queplix.core.modules.config.ejb.EntityViewConfigManagerLocal;
import com.queplix.core.modules.config.ejb.EntityViewConfigManagerLocalHome;
import com.queplix.core.modules.config.ejb.FocusConfigManagerLocal;
import com.queplix.core.modules.config.ejb.FocusConfigManagerLocalHome;
import com.queplix.core.modules.config.ejb.UserPropertyManagerLocal;
import com.queplix.core.modules.config.ejb.UserPropertyManagerLocalHome;
import com.queplix.core.modules.config.error.NoSuchPropertyException;
import com.queplix.core.modules.config.jxb.Form;
import com.queplix.core.modules.config.utils.SysPropertyManager;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eqlext.jxb.gr.Report;
import com.queplix.core.modules.eqlext.jxb.gr.Reportiter;
import com.queplix.core.modules.eqlext.jxb.gr.ReqEntity;
import com.queplix.core.modules.eqlext.jxb.gr.Reqs;
import com.queplix.core.modules.eqlext.jxb.gr.ResDataset;
import com.queplix.core.modules.eqlext.jxb.gr.ResField;
import com.queplix.core.modules.eqlext.jxb.gr.ResHeader;
import com.queplix.core.modules.eqlext.jxb.gr.ResHeaderDataset;
import com.queplix.core.modules.eqlext.jxb.gr.ResHeaderField;
import com.queplix.core.modules.eqlext.jxb.gr.ResRecord;
import com.queplix.core.modules.eqlext.jxb.gr.Ress;
import com.queplix.core.modules.eqlext.jxb.gr.types.DataSType;
import com.queplix.core.modules.eqlext.utils.EJBCacheActionContext;
import com.queplix.core.modules.eqlext.utils.ReportCallbackHnd;
import com.queplix.core.modules.eqlext.utils.ReportCallbackHndEvent;
import com.queplix.core.modules.eqlext.utils.ReportHelper;
import com.queplix.core.modules.eqlext.utils.ReportTransletFactory;
import com.queplix.core.modules.jeo.JEObjectHandler;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocal;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocalHome;
import com.queplix.core.modules.jeo.gen.ReportObject;
import com.queplix.core.modules.jeo.gen.ReportObjectHandler;
import com.queplix.core.modules.jeo.gen.ReportSchedulerObject;
import com.queplix.core.modules.jeo.gen.ReportSchedulerObjectHandler;
import com.queplix.core.modules.mail.Attachment;
import com.queplix.core.modules.mail.MailAddress;
import com.queplix.core.modules.mail.MailMessage;
import com.queplix.core.modules.mail.ejb.MailManagerLocal;
import com.queplix.core.modules.mail.ejb.MailManagerLocalHome;
import com.queplix.core.utils.DateHelper;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.ZipHelper;
import com.queplix.core.utils.ejb.AbstractSessionEJB;
import com.queplix.core.utils.xml.TransletWrapper;
import com.queplix.core.utils.xml.XMLFactory;
import com.queplix.core.utils.xml.XMLHelper;

import javax.mail.MessagingException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedOutputStream;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

/**
 * Report Manager session EJB.
 *
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @author [ALB] Andrey Baranov
 * @version $Revision: 1.9 $ $Date: 2006/09/01 17:10:35 $
 */

public class ReportManagerEJB
        extends AbstractSessionEJB {

    // ----------------------------------------------------- constants

    // Set GR package size.
    private static final int MAX_RECORDS_PER_PAGE =
            Integer.parseInt(ReportTransletFactory.getProperty(
                    "records_per_page.param"));

    // ----------------------------------------------------- variables

    // TransletWrapper instance.
    private final TransletWrapper transletWrapper = XMLFactory
            .getTransletWrapper();

    // ----------------------------------------------------- public methods

    public void ejbCreate() {
        INFO("Report Manager EJB created - " + hashCode());
    }

    /**
     * Makes an HTML report and write it to <code>pw</code> stream.
     *
     * @param ls           user logon session object with security data
     * @param report       Report object
     * @param transletName translet name
     * @param pw           output stream
     * @param hnd          callback handler
     * @throws EQLException on all EQL errors
     */
    public void printReport(LogonSession ls,
                            Report report,
                            String transletName,
                            Writer pw,
                            ReportCallbackHnd hnd)
            throws EQLException {

        printReport(ls, report, transletName, null, pw, hnd);
    }

    /**
     * Makes an HTML report and write it to <code>pw</code> stream.
     *
     * @param ls             user logon session object with security data
     * @param report         Report object
     * @param transletName   translet name
     * @param transletParams parameters for the translet
     * @param pw             output stream
     * @param hnd            callback handler
     * @throws EQLException on all EQL errors
     */
    public void printReport(LogonSession ls,
                            Report report,
                            String transletName,
                            Map transletParams,
                            Writer pw,
                            ReportCallbackHnd hnd)
            throws EQLException {

        File tempFile = null;
        FileReader fr = null;

        if(getLogger().isDebugEnabled()) {
            DEBUG("Print report " + report.getProcessId() + " using translet '"
                    +
                    transletName + "'.");
        }

        try {
            // Try to find existing report.
            tempFile = ReportHelper.getDataFile(ls, report);

            if(!tempFile.isFile() || !tempFile.exists()
                    || transletParams != null) {
                // Generate new report.
                tempFile = doGenerateReport(ls, report, transletName,
                        transletParams, hnd);
            }

            // Write report.
            fr = new FileReader(tempFile);
            char[] buff = new char[1024];
            int len = 0;
            while((len = fr.read(buff)) != -1) {
                pw.write(buff, 0, len);
            }

        } catch (IOException ex) {
            ERROR(ex);
            throw new GenericSystemException("IO exception: " + ex.getMessage(),
                    ex);

        } finally {
            // Don't remove TMP file!!!
            // Close stream only.
            try {
                if(fr != null) {
                    fr.close();
                }
            } catch (Exception ex) {
            }
        }
    }

    /**
     * Makes an HTML report and write it to <code>os</code> byte stream.
     *
     * @param ls             user logon session object with security data
     * @param report         Report object
     * @param transletName   translet name
     * @param transletParams parameters for the translet
     * @param os             output stream
     * @param hnd            callback handler
     * @throws EQLException on all EQL errors
     */
    public void printReport(LogonSession ls,
                            Report report,
                            String transletName,
                            Map transletParams,
                            OutputStream os,
                            ReportCallbackHnd hnd)
            throws EQLException {

        File tempFile = null;
        FileInputStream in = null;
        BufferedOutputStream bufOutStream = null;

        if(getLogger().isDebugEnabled()) {
            DEBUG("Print report " + report.getProcessId() + " using translet '"
                    +
                    transletName + "'.");
        }

        try {
            // Try to find existing report.
            tempFile = ReportHelper.getDataFile(ls, report);

            if(!tempFile.isFile() || !tempFile.exists()
                    || transletParams != null) {
                // Generate new report.
                tempFile = doGenerateReport(ls, report, transletName,
                        transletParams, hnd);
            }

            // Write report.
            in = new FileInputStream(tempFile);
            bufOutStream = new BufferedOutputStream(new GZIPOutputStream(os));

            byte[] buf = new byte[1024];
            int len = 0;
            while((len = in.read(buf)) > 0) {
                bufOutStream.write(buf, 0, len);
            }

            // Flush buffered stream.
            bufOutStream.flush();

        } catch (IOException ex) {
            ERROR(ex);
            throw new GenericSystemException("IO exception: " + ex.getMessage(),
                    ex);

        } finally {
            // Don't remove TMP file!!!
            // Close stream only.
            try {
                if(bufOutStream != null) {
                    bufOutStream.close();
                }
                if(in != null) {
                    in.close();
                }
            } catch (Exception ex) {
            }
        }
    }

    //  NRA code: method to send report in specified format
    /* 
     * No javadoc
     * @see #sendReportByMail( long, String, String )
     */
    public void sendFormattedReportByMail(long reportSchedulerId,
                                          String reportFormat)
            throws EQLException, MessagingException {
        sendReportByMail(reportSchedulerId, null, reportFormat);
    }

    // end NRA code
    /*
     * No javadoc
     * @see #sendReportByMail( long, String, String )
     */
    public void sendReportByMail(long reportSchedulerId)
            throws EQLException, MessagingException {
        sendReportByMail(reportSchedulerId, null);
    }

    /*
     * No javadoc
     * @see #sendReportByMail( long, String, String )
     */
    public void sendReportByMail(long reportSchedulerId, String transletName)
            throws EQLException, MessagingException {
        sendReportByMail(reportSchedulerId, transletName, null);
    }

    /**
     * Makes the scheduled report (by report schedule ID), transforms it to
     * HTML and sends by e-mail.
     *
     * @param reportSchedulerId report schedule ID to use
     * @param transletName      XSL translet name
     * @throws EQLException       on all EQL errors
     * @throws MessagingException
     */
    public void sendReportByMail(long reportSchedulerId, String transletName,
                                 String reportFormat)
            throws EQLException, MessagingException {

        // Initialization.
        LogonSession ls;
        ls = AccessRightsManager.getSystemLogonSession();

        ReportSchedulerObject scheduleObj = loadReportSchedulerObject(ls,
                reportSchedulerId);

        // Get logon session to use.
        Long createdBy = scheduleObj.getCreated_by();
        if(createdBy != null) {
            try {
                ls = AccessRightsManager.getLogonSessionForUser(
                        AccessRightsManager.getUser(createdBy.longValue()),
                        AccessRightsManager.SYSTEM_SESSION_ID);
            } catch (SecurityException ex) {
                throw new GenericSystemException(ex);
            }
        }

        // Get the report object.
        Long reportID = scheduleObj.getReport_id();
        ReportObject reportObj = loadReportObject(ls, reportID.longValue());
        String name = reportObj.getName();

        // Build Report object.
        //Report report = ReportHelper.valueOf(reportObj);
        ActionContext context = new EJBCacheActionContext(this);
        Report report = ReportHelper.serializedValueOf(reportObj, ls, context);

        //todo
        //StringWriter writer = new StringWriter();
        //printReport(ls, report, transletName, writer, new ReportCallbackAdapter());
        //String reportString = writer.toString();

        // Get the e-mail addresses, subject and body to use.
        String to = scheduleObj.getTo_addr();
        String cc = scheduleObj.getCc_addr();
        String subject = scheduleObj.getSubject();
        String body;
        if(name != null) {
            body = "\n\nSee report '" + name + "' in the attached file.";
        } else {
            body = "\n\nSee report in the attached file.";
        }

        if(getLogger().isDebugEnabled()) {
            DEBUG("Send report: " + reportObj.getReport_id() + " with name '" +
                    name + "' to '" + to + "'");
        }

        // Do the job.
        doSendReportByMail(ls, report, transletName, to, cc, subject, body,
                reportFormat);
    }

    // ----------------------------------------------------- private methods

    //
    // Load ReportObject by its ID.
    //

    private ReportObject loadReportObject(LogonSession ls, long reportId)
            throws EQLException {

        JEOManagerLocal jeoManager = (JEOManagerLocal)
                getLocalObject(JNDINames.JEOManager, JEOManagerLocalHome.class);

        JEObjectHandler reportHnd = ReportObjectHandler.selectByID(jeoManager,
                ls, reportId);
        if(reportHnd == null) {
            throw new NullPointerException(
                    "Report: " + reportId + " not found.");
        }

        return (ReportObject) reportHnd.getJEObject();
    }

    //
    // Load ReportSchedulerObject by its ID.
    //
    private ReportSchedulerObject loadReportSchedulerObject(LogonSession ls,
                                                            long reportSchedulerId)
            throws EQLException {

        JEOManagerLocal jeoManager = (JEOManagerLocal)
                getLocalObject(JNDINames.JEOManager, JEOManagerLocalHome.class);

        JEObjectHandler scheduleHnd = ReportSchedulerObjectHandler.selectByID(
                jeoManager, ls, reportSchedulerId);
        if(scheduleHnd == null) {
            throw new NullPointerException(
                    "Report schedule: " + reportSchedulerId + " not found.");
        }
        return (ReportSchedulerObject) scheduleHnd.getJEObject();
    }

    // 
    // Makes an HTML report and sends it by e-mail.
    //
    // comment by NRA due to redundancy
    /*private void doSendReportByMail( LogonSession ls,
            Report report,
            String transletName,
            String to,
            String cc,
            String subject,
            String body )
    throws EQLException, MessagingException {
    	doSendReportByMail( ls, report, transletName, to, cc, subject, body, null);
    }*/

    //
    // Makes an formatted report and sends it by e-mail.
    //

    private void doSendReportByMail(LogonSession ls,
                                    Report report,
                                    String transletName,
                                    String to,
                                    String cc,
                                    String subject,
                                    String body,
                                    String reportFormat)
            throws EQLException, MessagingException {

        // Get the creator 'from' address.
        String from = ls.getUser().getEmail();

        File tempFile = null;
        File zipFile = null;
        try {
            // Prepare temporary file.
            tempFile = doGenerateReport(ls, report, transletName, reportFormat,
                    null);

            // Zip report.
            zipFile = getZipFile();
            ZipHelper.zip(tempFile, zipFile);

            // Send.
            Attachment att = new Attachment(zipFile);
            sendByMail(ls, from, to, cc, subject, body, new Attachment[]{att});

        } catch (IOException ex) {
            ERROR(ex);
            throw new GenericSystemException("IO exception: " + ex.getMessage(),
                    ex);

        } finally {
            // Remove files.
            ReportHelper.clearDataFile(ls, report, reportFormat);
            try {
                if(zipFile != null && zipFile.exists()) {
                    zipFile.delete();
                }
            } catch (Exception ex) {
            }
        }
    }

    //
    // Makes an HTML report and save it as a temporary file.
    //
    private File doGenerateReport(LogonSession ls,
                                  Report report,
                                  String transletName,
                                  String reportFormat,
                                  ReportCallbackHnd hnd)
            throws EQLException {

        return doGenerateReport(ls, report, transletName, reportFormat, null,
                hnd);
    }

    private File doGenerateReport(LogonSession ls,
                                  Report report,
                                  String transletName,
                                  Map transletParams,
                                  ReportCallbackHnd hnd)
            throws EQLException {
        return doGenerateReport(ls, report, transletName, null, transletParams,
                hnd);
    }

    //  NRA code: core method with extra parameter to generated report file in specified format
    private File doGenerateReport(LogonSession ls,
                                  Report report,
                                  String transletName,
                                  String reportFormat,
                                  Map transletParams,
                                  ReportCallbackHnd hnd)
            throws EQLException {

        // Init.
        File tempFile = null;
        PrintWriter out = null;
        Reqs[] reqss = report.getReqs();
        GetRecordsLocal getRecords = (GetRecordsLocal)
                getLocalObject(JNDINames.GetRecords, GetRecordsLocalHome.class);

        // Get translet.
        String transletClassName = null;
        if(transletName != null) {
            transletClassName = ReportTransletFactory.getTransletClassName(
                    transletName);
        }
        if(transletClassName == null) {
            transletClassName = ReportTransletFactory.getDefTransletClassName();
        }
        if(transletClassName == null) {
            throw new NullPointerException(
                    "Cannot find translet by the name '" + transletName + "'.");
        }

        // Construct callback event.
        ReportCallbackHndEvent ev = new ReportCallbackHndEvent(ls, report,
                transletName);

        try {

            // Prepare temporary file.
            tempFile = ReportHelper.getDataFile(ls, report, reportFormat);
            out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(
                    tempFile), "UTF-8"));

            // Iteration counter.
            int iterId = 0;

            int reqsSize = reqss.length;

            // Create cached entity map.
            Map entityMap = new Hashtable();

            UserPropertyManagerLocal userPropManager
                    = (UserPropertyManagerLocal)
                    getLocalObject(JNDINames.UserPropertyManager,
                            UserPropertyManagerLocalHome.class);

            for(int i = 0; i < reqsSize; i++) {
                // Get next Reqs object.
                Reqs reqs = reqss[i];

                ReqEntity entity = reqs.getReq().getReqEntity();

                String entityName = null;
                List<String> fieldsForGrid = null;
                if(entity != null) {
                    entityName = entity.getName();

                    if(report.getIgnoreIncludeToReport()) {
                        String[] fieldsArray = userPropManager.getFieldsForGrid(
                                ls.getUser(), entityName);
                        if(fieldsArray != null) {
                            fieldsForGrid = Arrays.asList(fieldsArray);
                        }
                    }
                }

                // Set counter.
                reqs.setDocount(Boolean.TRUE);

                // Set package size if it necessary.
                boolean printPage = report.getPrintPage().booleanValue();
                if(!printPage) {
                    reqs.setPagesize(new Integer(MAX_RECORDS_PER_PAGE));
                }

                // Get Form object.
                Form form = getForm(ls, reqs);

                // Make the report page(s).
                boolean hasNextPage = !printPage;
                int page = (hasNextPage) ? 0:reqs.getPage().intValue();
                do {
                    // Set next page.
                    reqs.setPage(new Integer(page));

                    // Get the data for <code>page</code>.
                    Ress ress = getRecords.process(reqs, ls).getRess();

                    if(entityName
                            != null) {//check, can we know "include-to-report" option

                        if(!report.getIgnoreIncludeToReport()) {

                            //remove dataset headers, even if dataset is empty
                            ResHeaderDataset[] resHeaderDataSets = ress
                                    .getResHeader().getResHeaderDataset();
                            if(resHeaderDataSets != null) {
                                for(int j = 0; j < resHeaderDataSets.length;
                                    j++) {
                                    ResHeaderDataset resHeaderDataset
                                            = resHeaderDataSets[j];
                                    if(!isEntityDataSetToBeIncludedToReport(
                                            entityMap, entityName,
                                            resHeaderDataset.getName())) {
                                        ress.getResHeader()
                                                .removeResHeaderDataset(
                                                        resHeaderDataset);
                                    }
                                }
                            }
                            ResHeaderField[] resHeaderFields = ress
                                    .getResHeader().getResHeaderField();
                            if(resHeaderFields != null) {
                                for(int y = 0; y < resHeaderFields.length; y++)
                                {
                                    ResHeaderField resHeaderField
                                            = resHeaderFields[y];
                                    if(!isEntityFieldToBeIncludedToReport(
                                            entityMap, entityName,
                                            resHeaderField.getName())) {
                                        ress.getResHeader()
                                                .removeResHeaderField(
                                                        resHeaderField);
                                    }
                                }
                            }

                        } else if(fieldsForGrid != null) {
                            // Process grid columns customized by user, bugfix #12272
                            ResHeader resHeader = ress.getResHeader();
                            ResHeaderField[] resHeaderFields = resHeader
                                    .getResHeaderField();
                            List<ResHeaderField> resHeaderFieldsForGrid
                                    = new ArrayList<ResHeaderField>();
                            for(String fieldForGrid : fieldsForGrid) {
                                for(int x = 0; x < resHeaderFields.length; x++)
                                {
                                    ResHeaderField resHeaderField
                                            = resHeaderFields[x];
                                    if(fieldForGrid.equals(
                                            resHeaderField.getName())) {
                                        resHeaderField.setGrid(true);
                                        resHeaderFieldsForGrid.add(
                                                resHeaderField);
                                        resHeader.removeResHeaderField(
                                                resHeaderField);
                                    }
                                }
                            }
                            // reset grid attribute value
                            for(ResHeaderField resHeaderField : resHeader
                                    .getResHeaderField()) {
                                resHeaderField.setGrid(false);
                            }

                            resHeaderFieldsForGrid.addAll(Arrays.asList(
                                    resHeader.getResHeaderField()));
                            resHeader.clearResHeaderField();
                            for(ResHeaderField resHeaderFieldForGrid : resHeaderFieldsForGrid) {
                                resHeader.addResHeaderField(
                                        resHeaderFieldForGrid);
                            }

                        }

                    }

                    //NRA code: define ress field datatype from header array
                    //			and determine type member lists (currently TIMESTAMP and DATE)
                    ResHeaderField[] resHeaderFields = null;
                    boolean isReformatDate = false;
                    String user_date_pattern = null, user_time_pattern = null;
                    ArrayList dateMembers = null, timestampMembers = null;
                    String rep_date_pattern = null;
                    try {
                        rep_date_pattern = SysPropertyManager.getProperty(
                                "ReportDatePattern");
                    } catch (NoSuchPropertyException exc) {
                    }
                    try { // test date format obtained from System property
                        DateHelper.formatDate(new Date(), rep_date_pattern);
                    } catch (Throwable exc) {
                        rep_date_pattern = null;
                    }
                    if(rep_date_pattern != null && !StringHelper.isEmpty(
                            rep_date_pattern)) {
                        isReformatDate = true;
                    }
                    if(isReformatDate) {
                        resHeaderFields = ress.getResHeader()
                                .getResHeaderField();
                        dateMembers = new ArrayList();
                        timestampMembers = new ArrayList();
                        for(int k = 1; k < resHeaderFields.length; k++) {
                            switch(resHeaderFields[k].getDatatype().getType()) {
                                case DataSType.DATE_TYPE:
                                    dateMembers.add(new Integer(k));
                                    break;
                                case DataSType.TIMESTAMP_TYPE:
                                    timestampMembers.add(new Integer(k));
                                    break;
                            }
                        }
                        if(dateMembers.size() == 0) {
                            dateMembers = null;
                        }
                        if(timestampMembers.size() == 0) {
                            timestampMembers = null;
                        }
                        user_date_pattern = ls.getUser().getDatePattern();
                        user_time_pattern = ls.getUser().getTimePattern();
                    }
                    // NRA code end
                    //code by NRA to filter HTML tags for TEXT fields
                    ResRecord resRecords[] = ress.getRes().getResRecord();
                    if(resRecords != null) {
                        for(int r = 0; r < resRecords.length; r++) {
                            ResRecord currentRecord = resRecords[r];

                            if(entityName
                                    != null) {//check, can we know "include-to-report" option

                                if(!report.getIgnoreIncludeToReport()) {
                                    //remove datasets
                                    ResDataset[] dataSets = currentRecord
                                            .getResDataset();
                                    if(dataSets != null) {
                                        for(int j = 0; j < dataSets.length; j++)
                                        {
                                            ResDataset resDataSet = dataSets[j];
                                            if(!isEntityDataSetToBeIncludedToReport(
                                                    entityMap, entityName,
                                                    resDataSet.getName())) {
                                                currentRecord.removeResDataset(
                                                        resDataSet);
                                            }
                                        }
                                    }
                                    // remove fields
                                    ResField resFields[] = currentRecord
                                            .getResField();
                                    if(resFields != null) {
                                        for(int y = 0; y < resFields.length;
                                            y++) {
                                            ResField resField = resFields[y];
                                            if(!isEntityFieldToBeIncludedToReport(
                                                    entityMap, entityName,
                                                    resField.getName())) {
                                                currentRecord.removeResField(
                                                        resField);
                                            }
                                        }
                                    }

                                } else if(fieldsForGrid != null) {
                                    // Process grid columns customized by user, bugfix #12272
                                    ResField[] resFlds = currentRecord
                                            .getResField();
                                    List<ResField> resFieldsForGrid
                                            = new ArrayList<ResField>();
                                    for(String fieldForGrid : fieldsForGrid) {
                                        for(int x = 0; x < resFlds.length; x++)
                                        {
                                            ResField resField = resFlds[x];
                                            if(fieldForGrid.equals(
                                                    resField.getName())) {
                                                resFieldsForGrid.add(resField);
                                                currentRecord.removeResField(
                                                        resField);
                                            }
                                        }
                                    }
                                    resFieldsForGrid.addAll(Arrays.asList(
                                            currentRecord.getResField()));
                                    currentRecord.clearResField();
                                    for(ResField resFieldForGrid : resFieldsForGrid) {
                                        currentRecord.addResField(
                                                resFieldForGrid);
                                    }

                                }
                            }

                            //iterate fields
                            ResField resFields[] = currentRecord.getResField();
                            if(resFields != null) {
                                for(int f = 0; f < resFields.length; f++) {
                                    ResField currentField = resFields[f];

                                    //NRA code: check if current field is member of date or timestamp fields
                                    //          and try to reformat date if yes
                                    if(dateMembers != null && dateMembers
                                            .contains(new Integer(f))) {
                                        reformatDate(currentField,
                                                user_date_pattern, "",
                                                rep_date_pattern);
                                    }
                                    if(timestampMembers != null
                                            && timestampMembers.contains(
                                            new Integer(f))) {
                                        reformatDate(currentField,
                                                user_date_pattern,
                                                user_time_pattern,
                                                rep_date_pattern);
                                    }
                                    // NRA code end

                                    String textFld = currentField
                                            .getResFieldText();
                                    if(textFld != null && StringHelper.isHTML(
                                            textFld)) {
                                        currentField.setResFieldText(
                                                StringHelper.html2text(
                                                        textFld));
                                    }

                                }

                            }
                        }
                    }
                    //end code NRA

                    // Check for a next page.
                    if(hasNextPage) {
                        hasNextPage = ress.getNext().booleanValue();
                    }

                    // Check first and last iteration attrs.
                    boolean isFirst = (iterId == 0);
                    boolean isLast = !hasNextPage && (i == (reqsSize - 1));

                    // Get number of pages
                    if(isFirst) {
                        // Initialize request number and pages attributes of event object.
                        ev.setNewReq(i, ress.getCount().intValue() / ress
                                .getPagesize().intValue());
                        // Unset counter.
                        reqs.setDocount(Boolean.FALSE);
                    }

                    // Write the page to file.
                    // NRA code: clear HTML tags if report format is EXcel
                    String tmpstr =
                            makeHTML(ls,
                                    report,
                                    iterId,
                                    isFirst,
                                    isLast,
                                    form,
                                    ress,
                                    transletClassName,
                                    transletParams);

                    if(reportFormat != null && reportFormat.equals("xls")) {
                        tmpstr = StringHelper.clearHtml(tmpstr);
                    }

                    out.println(tmpstr);

                    /*out.println(
                        	makeHTML( ls,
                                    report,
                                    iterId,
                                    isFirst,
                                    isLast,
                                    form,
                                    ress,
                                    transletClassName,
                                    transletParams ) );*/
                    // -- end NRA code --

                    // Call callback handler
                    if(hnd != null) {
                        ev.setLast(isLast);
                        ev.setPage(page);
                        ev.setIterId(iterId);
                        hnd.call(ev);
                    }

                    // Get next page.
                    page++;

                    // Get next iteration.
                    iterId++;

                } while(hasNextPage);
            }

            out.flush();
            out.close();

            if(getLogger().isDebugEnabled()) {
                DEBUG("All pages of report saved to file '" + tempFile + "'.");
            }

        } catch (IOException ex) {
            ERROR(ex);
            throw new GenericSystemException("IO exception: " + ex.getMessage(),
                    ex);

        } finally {
            try {
                if(out != null) {
                    out.close();
                }
            } catch (Exception ex) {
            }
        }

        return tempFile;
    }

    /**
     * Replace value of given ress field (Date or Timestamp) by re-formatted date.
     *
     * @param ressField   ress field
     * @param new_pattern desired Date pattern (from System property)
     */
    private void reformatDate(ResField ressField, String old_pattern_date,
                              String old_pattern_time, String new_pattern) {
        String oldDate = ressField.getResFieldValue();
        String old_pattern = null;
        if(oldDate != null && old_pattern_date != null && new_pattern != null) {
            String newDate = null;
            boolean hasTimePart = (oldDate.indexOf(':') >= 0);
            if(hasTimePart) {
                old_pattern = old_pattern_date + " " + old_pattern_time;
                new_pattern += " " + old_pattern_time;
            } else {
                old_pattern = old_pattern_date;
            }
            try {
                newDate = DateHelper.formatDate(DateHelper.parseDate(oldDate,
                        old_pattern), new_pattern);
            } catch (ParseException e) {
            }
            if(newDate != null) {
                ressField.setResFieldValue(newDate);
                DEBUG(" Date was re-formatted to: " + newDate);
            }
        }
    }

    /**
     * Tells us is entity dataset to be included to report.
     * Returns true, if any error occured.
     *
     * @param entityMap   cached per each method request entities. Entity has the type {@link com.queplix.core.jxb.entity.Entity}
     * @param entityName  entity name. Entity has the type {@link com.queplix.core.jxb.entity.Entity}
     * @param datasetName dataset name. Should be represented in entity
     * @return is entity dataset to be included to report
     */
    private boolean isEntityDataSetToBeIncludedToReport(Map entityMap,
                                                        String entityName,
                                                        String datasetName) {
        boolean ret
                = true;//due to the fact, that we inject new functionality, by default if some problem
        // is occured and we have unknown field, just include it to report

        Entity entityView;

        if(datasetName != null && entityName != null) {
            entityView = retrieveEntity(entityMap, entityName);

            Dataset dataset = getDatasetFromEntity(entityView, datasetName);
            if(dataset != null) {
                ret = dataset.getIncludeToReport().booleanValue();
            }
        }

        return ret;
    }

    /**
     * Retrieves entity by it's name from the cache, or if it's not there get it from EntityViewConfigManager ejb.
     * Could be null
     *
     * @param entityMap  cache map
     * @param entityName name of the entity
     * @return entity.
     */
    private Entity retrieveEntity(Map entityMap, String entityName) {
        Entity entityView;
        if(entityMap.containsKey(entityName)) {
            entityView = (Entity) entityMap.get(entityName);
        } else {
            try {
                EntityViewConfigManagerLocal configManager
                        = (EntityViewConfigManagerLocal)
                        getLocalObject(JNDINames.EntityViewConfigManager,
                                EntityViewConfigManagerLocalHome.class);

                entityView = configManager.getEntityViewConfig(entityName);
                entityMap.put(entityName, entityView);
            } catch (Exception e) {
                //in case of we haven't link to ejb, or entity is null.
                ERROR("Report manager EJB should has link to EntityViewConfigManager EJB, and entity \""
                        + entityName + "\" should exists");
                entityView = null;
            }
        }
        return entityView;
    }

    /**
     * Tells us is entity field to be included to report.
     * Returns true, if any error occured.
     *
     * @param entityMap  cached per each method request entities. Entity has the type {@link com.queplix.core.jxb.entity.Entity}
     * @param entityName entity name. Entity has the type {@link com.queplix.core.jxb.entity.Entity}
     * @param fieldName  field name. Should be represented in entity
     * @return is entity field to be included to report
     */
    private boolean isEntityFieldToBeIncludedToReport(Map entityMap,
                                                      String entityName,
                                                      String fieldName) {
        boolean ret
                = true;//due to the fact, that we inject new functionality, by default if some problem
        // is occured or we have unknown field, just include it to report

        Entity entityView;

        if(fieldName != null && entityName != null) {
            entityView = retrieveEntity(entityMap, entityName);

            Efield entityField = getFieldFromEntity(entityView, fieldName);

            if(entityField != null) {
                ret = entityField.getIncludeToReport().booleanValue();
            }
        }

        return ret;
    }

    /**
     * Retrieves field from the entity. Compare them by the fields names.
     * Returns null if any error occured.
     *
     * @param entityView entity from which field should be found.
     * @param fieldName  name of the field.
     * @return field.
     */
    private Efield getFieldFromEntity(Entity entityView, String fieldName) {
        Efield ret = null;
        if(fieldName != null && entityView != null) {
            Efield[] fields = entityView.getEfield();
            for(int i = 0; i < fields.length; i++) {
                String currentFieldName = fields[i].getName();
                if(fieldName.equalsIgnoreCase(currentFieldName)) {
                    ret = fields[i];
                    break;
                }
            }
        }
        return ret;
    }

    /**
     * Retrieves dataset from the entity. Compare them by the dataset names.
     * Returns null if any error occured.
     *
     * @param entityView  entity from which field should be found.
     * @param datasetName name of the dataset.
     * @return dataset.
     */
    private Dataset getDatasetFromEntity(Entity entityView,
                                         String datasetName) {
        Dataset ret = null;
        if(datasetName != null && entityView != null) {
            Dataset[] datasets = entityView.getDataset();
            for(int i = 0; i < datasets.length; i++) {
                String currentDatasetName = datasets[i].getName();
                if(datasetName.equalsIgnoreCase(currentDatasetName)) {
                    ret = datasets[i];
                    break;
                }
            }
        }
        return ret;
    }

    //
    // Gets the temporary zip file .
    //
    private File getZipFile()
            throws IOException {

        String path = SysPropertyManager.getProperty("REPORT_ZIP_DIR");
        String name = ReportHelper.REPORT_FILE_NAME + "_"
                + System.currentTimeMillis() + ZipHelper.ZIP_EXT;

        // Check parent directory.
        File dir = new File(path);
        if(!dir.exists() && !dir.mkdirs()) {
            throw new FileNotFoundException(
                    "Can't create data directory '" + path + "'.");
        }
        if(!dir.isDirectory()) {
            throw new IOException("Not a directory: '" + path + "'.");
        }

        // Ok.
        return new File(path, name);
    }

    //
    // Makes a report HTML view by XSLT.
    //
    private String makeHTML(LogonSession ls,
                            Report report,
                            int iterId,
                            boolean isFirst,
                            boolean isLast,
                            Form form,
                            Ress ress,
                            String transletClassName) {

        return makeHTML(ls,
                report,
                iterId,
                isFirst,
                isLast,
                form,
                ress,
                transletClassName,
                null);
    }

    private String makeHTML(LogonSession ls,
                            Report report,
                            int iterId,
                            boolean isFirst,
                            boolean isLast,
                            Form form,
                            Ress ress,
                            String transletClassName,
                            Map transletParams) {

        // Construct Reportiter object.
        Reportiter reportIteration = new Reportiter();
        reportIteration.setIterId(new Long(iterId));
        reportIteration.setIsFirst(new Boolean(isFirst));
        reportIteration.setIsLast(new Boolean(isLast));
        if(form != null) {
            reportIteration.setIterTitle(form.getCaption());
        }
        report.setReportiter(reportIteration);

        // Set report data and serialize Report object.
        reportIteration.setRess(ress);
        char[] data = XMLHelper.writeObject(report);

        // Do XSLT.
        StreamSource source = new StreamSource(new CharArrayReader(data));
        CharArrayWriter out = new CharArrayWriter();
        try {
            transletWrapper.transform(source, new StreamResult(out),
                    transletClassName, transletParams);
        } catch (GenericSystemException ex) {
            ERROR("Cannot transform source: \n" + String.valueOf(data) + "\n\n",
                    ex);
            throw ex;
        }

        // Ok.
        return out.toString();

    }

    //
    // Makes the e-mail message and sends it.
    //
    private void sendByMail(LogonSession ls,
                            String from,
                            String to,
                            String cc,
                            String subject,
                            String body,
                            Attachment[] attachments)
            throws MessagingException {

        // Prepare the e-mail message:
        MailAddress[] toAddr = MailAddress.parse(to);
        MailMessage message = new MailMessage(toAddr, subject, body);
        if(!StringHelper.isEmpty(cc)) {
            MailAddress[] ccAddr = MailAddress.parse(cc);
            message.setCc(ccAddr);
        }
        if(!StringHelper.isEmpty(from)) {
            MailAddress fromAddr = new MailAddress(from);
            message.setFrom(fromAddr);
        }

        // Send the message.
        MailManagerLocal mailManager = (MailManagerLocal)
                getLocalObject(JNDINames.MailManager,
                        MailManagerLocalHome.class);

        mailManager.sendMessage(ls, message, null, attachments);
    }

    //
    // Gets Form object
    //
    private Form getForm(LogonSession ls, Reqs reqs) {
        ReqEntity reqEntity = reqs.getReq().getReqEntity();
        String formId = (reqEntity == null) ? null:reqEntity.getFormid();
        if(formId == null) {
            return null;
        }

        FocusConfigManagerLocal local = (FocusConfigManagerLocal)
                getLocalObject(JNDINames.FocusConfigManager,
                        FocusConfigManagerLocalHome.class);

        return local.getLocalizedForm(ls.getUser().getLangID(), formId);
    }

    private static class ReportCallbackAdapter implements ReportCallbackHnd {
        public void call(ReportCallbackHndEvent ev) throws EQLException {
            //do nothing, don't need get status here
        }
    }
}
