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

import com.queplix.core.client.app.rpc.DisplayableException;
import com.queplix.core.client.app.rpc.DisplayableStackTraceException;
import com.queplix.core.client.app.vo.GridData;
import com.queplix.core.client.app.vo.RowData;
import com.queplix.core.client.app.vo.SearchGridRecordsResponseObject;
import com.queplix.core.error.LocalizedAppException;
import com.queplix.core.integrator.entity.EntityFacade;
import com.queplix.core.integrator.entity.EntityOperationsHelper;
import com.queplix.core.integrator.entity.EntityViewHelper;
import com.queplix.core.integrator.entity.IntegratedRecordSet;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.modules.config.ejb.CaptionManagerLocal;
import com.queplix.core.modules.eql.error.EQLConstraintViolationException;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eqlext.jxb.gr.Reqs;
import com.queplix.core.modules.eqlext.jxb.gr.Ress;
import com.queplix.core.modules.eqlext.www.GetReportServlet;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.log.AbstractLogger;
import com.queplix.core.utils.log.Log;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * That class collect all functions which help developers to connect new integrator level to old 2.6 core, which is server part.
 * Note all operations, that should be performed with Entity as whole object, should be placed at {@link EntityFacade}
 *
 * @author Sergey Kozmin
 * @since 20.11.2006, 17:08:13
 */
public class IntegratorHelper {
    private static final String REPORT_BUILDER_CACHE_PARAM
            = "__reportBuilderCache";
    private static final String EJB_CACHE_PARAM = "__ejbs_action_context";

    private static final AbstractLogger logger = Log.getLog(
            IntegratorHelper.class);

    /**
     * Get report builder cache.
     *
     * @param session http session
     * @return report builder cache
     */
    public static GetReportServlet.ReportBuilderCache getReportBuilderCache(
            HttpSession session) {
        GetReportServlet.ReportBuilderCache cache
                = (GetReportServlet.ReportBuilderCache) session.getAttribute(
                REPORT_BUILDER_CACHE_PARAM);
        if(cache == null) {
            cache = new GetReportServlet.ReportBuilderCache();
            session.setAttribute(REPORT_BUILDER_CACHE_PARAM, cache);
        }
        return cache;
    }

    /**
     * This method creates action context basing servlet context on cache.
     * To be used when client has only ServletContext object (for narrowing interfaces puropse).
     *
     * @param ctx servlet context
     * @return action context
     */
    public static ActionContext getActionContext(ServletContext ctx) {
        ServletCacheActionContext cache = (ServletCacheActionContext) ctx
                .getAttribute(EJB_CACHE_PARAM);
        if(cache == null) {
            cache = new ServletCacheActionContext(ctx);
            ctx.setAttribute(EJB_CACHE_PARAM, cache);
        }
        return cache;
    }

    /**
     * This method is usefull when you creates action context near by direct
     * servler call.
     *
     * @param request servlet request
     * @return action context
     * @see #getActionContext(javax.servlet.ServletContext)
     */
    public static ActionContext getActionContext(HttpServletRequest request) {
        return getActionContext(request.getSession().getServletContext());
    }

    public static SearchGridRecordsResponseObject getNotEmptyResult(
            int preferredPage,
            RequestBuilder builder,
            String requestingEntity,
            EntityViewHelper.FieldsModificator mode,
            LogonSession ls, ActionContext actx) throws EQLException {

        Reqs req = builder.buildRequest(preferredPage);

        IntegratedRecordSet integratedRecordSet = EntityFacade.performRequest(
                req, requestingEntity, ls, actx, mode);

        Ress ress = integratedRecordSet.getInitialResultSet().getRess();

        int totalRecordsCount = ress.getCount();
        int recordsCount = ress.getRes().getResRecordCount();
        int currentPage = ress.getPage();

        IntegratedRecordSet correctResp = integratedRecordSet;
        if(shouldReRequest(recordsCount,
                currentPage)) {//when get empty result, then request first page
            Reqs prevReq = builder.buildRequest(0);

            IntegratedRecordSet prevPageResp = EntityFacade.performRequest(
                    prevReq, requestingEntity, ls, actx, mode);

            Ress prevPageRess = prevPageResp.getInitialResultSet().getRess();

            totalRecordsCount = prevPageRess.getCount();
            currentPage = prevPageRess.getPage();
            correctResp = prevPageResp;
        }

        RowData[] records = EntityOperationsHelper
                .retrieveGridRowsDataFromResult(correctResp,
                        ls, actx);
        return new SearchGridRecordsResponseObject(new GridData(records,
                requestingEntity), totalRecordsCount, currentPage);
    }

    private static boolean shouldReRequest(int recordsCount, int currentPage) {
        return recordsCount < 1 && currentPage != 0;
    }

    public static void reThrowException(Exception ex)
            throws DisplayableStackTraceException {
        logger.ERROR(ex);
        throw new DisplayableStackTraceException(ex.getMessage(),
                StringHelper.getExceptionStacktrace(ex));
    }

    public static void throwException(String message, Throwable causedException)
            throws DisplayableException {
        logger.ERROR(message, causedException);
        throw new DisplayableStackTraceException(message,
                StringHelper.getExceptionStacktrace(causedException));
    }

    public static void throwException(String message)
            throws DisplayableException {
        logger.ERROR(message);
        throw new DisplayableException(message);
    }

    public static void throwException(Throwable t, String langID,
                                      ServletContext ctx)
            throws DisplayableException {
        if(t instanceof LocalizedAppException) {
            LocalizedAppException ex = (LocalizedAppException) t;
            String formattedMessage = EntityOperationsHelper
                    .getLocalizedServerMessage(t.getMessage(), ex.getArgs(),
                            langID, getActionContext(ctx));

            throwException(formattedMessage, t);
        } else {
            String s = t.getMessage();
            s = (s != null) ? s:t.getClass().getName();
            throwException(s, t);
        }
    }

    public static void throwConstraintException(
            EQLConstraintViolationException e, LogonSession ls,
            ServletContext ctx) throws DisplayableException {
        String message = e.getMessage();
        Set<Character> unallowedChars = new HashSet<Character>();
        Collections.addAll(unallowedChars, '\'', '"', '.', ',');
        StringTokenizer st = new StringTokenizer(e.returnCause().getMessage());
        while(st.hasMoreTokens()) {
            String token = extractForeignKeyName(st.nextToken().toUpperCase(),
                    unallowedChars);
            if(token.startsWith("FK_")) {
                CaptionManagerLocal cml = getActionContext(ctx)
                        .getCaptionManager();
                String langId = com.queplix.core.integrator.security
                        .WebLoginManager.getLogonLanguage(ls);
                message = cml.getServerMessage(langId, token);
                break;
            }
        }
        throw new DisplayableException(message);
    }

    private static String extractForeignKeyName(String text,
                                                Set<Character> unallowedChars) {
        if(text == null) {
            return "";
        } else {
            StringBuffer result = new StringBuffer();
            for(int i = 0; i < text.length(); i++) {
                if(!unallowedChars.contains(text.charAt(i))) {
                    result.append(text.charAt(i));
                }
            }
            return result.toString();
        }
    }

}
