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

import com.queplix.core.integrator.ActionContext;
import com.queplix.core.integrator.entity.EntityFacade;
import com.queplix.core.integrator.entity.RequestProperties;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.util.AdhocReport;
import com.queplix.core.integrator.util.ReportSerializeHelper;
import com.queplix.core.modules.config.utils.SysPropertyManager;
import com.queplix.core.modules.eqlext.jxb.gr.Report;
import com.queplix.core.modules.eqlext.jxb.gr.Req;
import com.queplix.core.modules.eqlext.jxb.gr.Reqs;
import com.queplix.core.modules.jeo.gen.ReportObject;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.xml.XMLHelper;

import java.io.CharArrayReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

/**
 * Helper class for reports operations.
 *
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @author [ALB] Andrey Baranov
 * @version $Revision: 1.2 $ $Date: 2006/08/24 16:21:58 $
 */

public final class ReportHelper {

    // ============================================================ Constants

    // Report file name (w/o extension).
    public final static String REPORT_FILE_NAME = "report";

    // ============================================================ Period units

    public final static int MINUTE = 1;
    public final static int HOUR = 2;
    public final static int DAY = 3;
    public final static int WEEK = 4;
    public final static int MONTH = 5;
    public final static int YEAR = 6;
    public final static int ONE_TIME = 7;

    // ========================================================== Initialization

    // Private constructor - blocks instantiation.

    private ReportHelper() {
    }

    // ========================================================== Public methods

    /**
     * Generates the unique process ID.
     *
     * @return report process ID
     */
    public static Long getUniqueProcessID() {
        long id = System.currentTimeMillis();
        return new Long(id);
    }

    /**
     * Creates Report object.
     *
     * @param reportObj ReportObject JE object.
     * @return new instance of Report.
     */
    public static Report valueOf(ReportObject reportObj) {

        if(reportObj == null) {
            throw new NullPointerException("ReportObject is NULL");
        }
        if(reportObj.getBody() == null) {
            throw new NullPointerException("Report body is NULL");
        }

        // Get the escaped XML report request and XSL style parameters.
        String request = new String(reportObj.getBody());
        request = StringHelper.clearXml(StringHelper.unEscape(
                StringHelper.unEscapeUnicode(request)), false);
        String style = new String(reportObj.getStyle());
        style = StringHelper.clearXml(StringHelper.unEscape(
                StringHelper.unEscapeUnicode(style)), false);

        // Deserialize Report object.
        String s =
                "<report processId=\"" + getUniqueProcessID()
                        + "\" printPage=\"false\">" +
                        request + style + "</report>";

        Report report = (Report) XMLHelper.getParsedObject(Report.class,
                new CharArrayReader(s.toCharArray()));

        // Ok.
        return report;
    }

    /**
     * Creates Report object.
     *
     * @param reportObj ReportObject JE object.
     * @return new instance of Report.
     */
    public static Report serializedValueOf(ReportObject reportObj,
                                           LogonSession ls,
                                           ActionContext actx) {

        if(reportObj == null) {
            throw new NullPointerException("ReportObject is NULL");
        }
        if(reportObj.getBody() == null) {
            throw new NullPointerException("Report body is NULL");
        }

        // Get the escaped XML report request and XSL style parameters.
        String request = new String(reportObj.getBody());

        AdhocReport adhocReport = ReportSerializeHelper.deSerializeReport(
                request, ls, actx);

        Report report = new Report();
        report.setProcessId(System.currentTimeMillis());
        Reqs rs = new Reqs();
        Req r = new Req();
        RequestProperties props = new RequestProperties();
        r.setReqField(EntityFacade.getReqFields(adhocReport.getReportFields(),
                props));
        rs.setReq(r);
        rs.setReqFilters(EntityFacade.getReqFilters(adhocReport.getFilters(),
                props, ls, actx));
        report.setReqs(new Reqs[]{rs});
        report.setPrintPage(Boolean.FALSE);

        // Ok.
        return report;
    }

    /**
     * Gets the temporary data file.
     *
     * @param ls     logon session
     * @param report Report object
     * @return File object
     * @throws IOException
     */
    public static File getDataFile(LogonSession ls, Report report)
            throws IOException {
        return getDataFile(ls, report, null);
    }

    /**
     * Gets the temporary data file.
     *
     * @param ls           logon session
     * @param report       Report object
     * @param reportFormat String object
     * @return File object
     * @throws IOException
     */
    // 	NRA code: core method with extra parameter to create report data file in specified format
    public static File getDataFile(LogonSession ls, Report report,
                                   String reportFormat)
            throws IOException {

        String repFormat = reportFormat;
        if(repFormat == null) {
            repFormat = "html";
        }

        String path = SysPropertyManager.getProperty("REPORT_ZIP_DIR");
        String name = REPORT_FILE_NAME + "_" + ls.getUser().getLoginName() + "_"
                + report.getProcessId() + "." + repFormat;

        // 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);
    }

    /**
     * Removes temporary data file.
     *
     * @param ls     logon session
     * @param report Report object
     */
    public static void clearDataFile(LogonSession ls, Report report) {
        clearDataFile(ls, report, null);
    }

    /**
     * Removes temporary data file.
     *
     * @param ls           logon session
     * @param report       Report object
     * @param reportFormat String object
     */
    // NRA code: core method with extra parameter to clear report data formatted file
    public static void clearDataFile(LogonSession ls, Report report,
                                     String reportFormat) {
        try {
            File tempFile = getDataFile(ls, report, reportFormat);
            if(tempFile != null && tempFile.exists()) {
                tempFile.delete();
            }
        } catch (Exception ex) {
        }
    }

    /**
     * Calculates the next date to send report.
     *
     * @param period     amount of time units
     * @param periodUnit time unit type
     * @param now        current timestamp
     * @return next send date
     */
    public static Date reschedule(int period, int periodUnit, Date now) {
        Date nextSend = new Date(now.getTime());
        switch(periodUnit) {
            case MINUTE:
                nextSend.setMinutes(nextSend.getMinutes() + period);
                break;
            case HOUR:
                nextSend.setHours(nextSend.getHours() + period);
                break;
            case DAY:
                nextSend.setDate(nextSend.getDate() + period);
                break;
            case WEEK:
                nextSend.setDate(nextSend.getDate() + period * 7);
                break;
            case MONTH:
                nextSend.setMonth(nextSend.getMonth() + period);
                break;
            case YEAR:
                nextSend.setYear(nextSend.getYear() + period);
                break;
            case ONE_TIME:
                if(nextSend.getTime() >= 0) {
                    nextSend.setTime(StringHelper.EMPTY_NUMBER);
                }
                break;
        }
        return nextSend;
    }

    /**
     * Calculates the next date to send report.
     *
     * @param period     amount of time units
     * @param periodUnit time unit type
     * @param lastSent   last sent timestamp
     * @param now        current timestamp
     * @return next send date
     */
    public static Date reschedule(int period, int periodUnit, Date lastSent,
                                  Date now) {

        Date nextSend = new Date(now.getTime());

        if(periodUnit == ONE_TIME) {
            nextSend.setTime(StringHelper.EMPTY_NUMBER);
            return nextSend;
        }

        long p = 0;
        long nextSendLong = 0;
        long nowLong = now.getTime();
        long lastSentLong = lastSent.getTime();
        switch(periodUnit) {
            case MINUTE:
                p = 60 * 1000;
                break;
            case HOUR:
                p = 60 * 60 * 1000;
                break;
            case DAY:
                p = 24 * 60 * 60 * 1000;
                break;
            case WEEK:
                p = 7 * 24 * 60 * 60 * 1000;
                break;
        }
        if(periodUnit == MINUTE || periodUnit == HOUR || periodUnit == DAY
                || periodUnit == WEEK) {
            nextSendLong = nowLong + (period * p - ((nowLong - lastSentLong) % (
                    period * p)));
            nextSend = new Date(nextSendLong);
        } else {
            Calendar c = Calendar.getInstance();
            c.setTime(lastSent);
            boolean b = true;
            while(b) {
                switch(periodUnit) {
                    case MONTH:
                        c.add(Calendar.MONTH, period);
                        break;
                    case YEAR:
                        c.add(Calendar.YEAR, period);
                        break;
                }
                if(c.getTimeInMillis() >= nowLong) {
                    b = false;
                }
            }
            nextSend = c.getTime();
        }

        return nextSend;
    }

}
