/*
 * 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.eql.history.generic;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.SecurityHelper;
import com.queplix.core.integrator.security.User;
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.CustomConfigManagerLocal;
import com.queplix.core.modules.config.ejb.CustomConfigManagerLocalHome;
import com.queplix.core.modules.config.jxb.CustomField;
import com.queplix.core.modules.eql.EQLDRes;
import com.queplix.core.modules.eql.EQLObject;
import com.queplix.core.modules.eql.EQLReqDataset;
import com.queplix.core.modules.eql.EQLReqEntity;
import com.queplix.core.modules.eql.EQLReqField;
import com.queplix.core.modules.eql.EQLResCell;
import com.queplix.core.modules.eql.EQLResRecord;
import com.queplix.core.modules.eql.ejb.EQLManagerLocal;
import com.queplix.core.modules.eql.ejb.EQLManagerLocalHome;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eql.history.HistoryBuilder;
import com.queplix.core.modules.eql.jxb.history.HistoryData;
import com.queplix.core.modules.eql.jxb.history.HistoryField;
import com.queplix.core.modules.eql.jxb.history.HistoryHeader;
import com.queplix.core.modules.eql.jxb.history.HistoryTopic;
import com.queplix.core.modules.eql.jxb.history.Ifg;
import com.queplix.core.modules.eql.jxb.history.IfgData;
import com.queplix.core.modules.eql.jxb.history.IfgRecord;
import com.queplix.core.modules.eql.jxb.history.PkeyField;
import com.queplix.core.modules.eql.utils.EQLUtils;
import com.queplix.core.modules.eqlext.error.EfieldTransformException;
import com.queplix.core.modules.eqlext.utils.FieldConverter;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocal;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocalHome;
import com.queplix.core.modules.jeo.gen.HistoryTableFieldObject;
import com.queplix.core.modules.jeo.gen.HistoryTableFieldObjectHandler;
import com.queplix.core.utils.DateHelper;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.StringHelper;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 * History builder generic implementation.
 *
 * @author [ALB] Baranov Andrey
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:26 $
 */
public class HistoryBuilderGenericImpl
        extends HistoryBuilder {

    // ================================================================= Fields

    // User logon session.
    private LogonSession ls;
    private String INSERT_ACTION_TYPE = "insert";
    private String UPDATE_ACTION_TYPE = "update";
    private String DELETE_ACTION_TYPE = "delete";

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

    //
    // Constructor.
    //

    public HistoryBuilderGenericImpl() {
        ls = AccessRightsManager.getSystemLogonSession();
    }

    /* (non-javadoc)
     * @see HistoryBuilder#createHistoryTopic
     */
    public HistoryTopic createHistoryTopic(EQLResRecord resRecord,
                                           List historyFieldNames) {

        // Initialization.
        User user = getSession().getLogonSession().getUser();
        HistoryTopic topic = new HistoryTopic();

        // Create the topic header.
        HistoryHeader header = new HistoryHeader();
        header.setTimestamp(getHistoryTimestapm());
        header.setUser_id(new Long(user.getUserID()));
        header.setUser_type(new Integer(user.getAuthenticationType()));
        header.setUser_loginname(user.getLoginName());
        header.setUser_fullname(user.getFullName());
        topic.setHistoryHeader(header);

        // Create the topic data.
        HistoryData data = new HistoryData();
        for(int i = 0; i < resRecord.size(); i++) {
            HistoryField field = getHistoryField(i, resRecord,
                    historyFieldNames);
            if(field != null) {
                data.addHistoryField(field);
            }
        } // for

        int dResSize = resRecord.getDResSize();
        IfgData ifgData = new IfgData();

        for(int i = 0; i < dResSize; i++) {
            EQLDRes eqlDRes = resRecord.getDRes(i);
            EQLReqDataset reqDataSet = eqlDRes.getReqDataset();
            Dataset dataSet = eqlDRes.getReqDataset().getDataset();

            Entity dsEntity = reqDataSet.getDatasetEntity();
            List dsLogFieldNames = getLogFieldNames(dsEntity.getDbobject());
            if(dsLogFieldNames == null) {
                continue;
            }

            Ifg ifg = null;

            for(int k = 0; k < eqlDRes.getRecords().size(); k++) {
                EQLResRecord rRecord = eqlDRes.getRecord(k);
                EQLResRecord aRecord = getAdditRecord(eqlDRes.getRecords(),
                        rRecord);
                boolean doInsert = rRecord.isNew();
                boolean doUpdate = rRecord.doUpdate();
                boolean doDelete = rRecord.doDelete();

                IfgRecord ifgRecord = new IfgRecord();
                if(doInsert) {
                    ifgRecord.setAction(INSERT_ACTION_TYPE);
                } else if(doUpdate) {
                    ifgRecord.setAction(UPDATE_ACTION_TYPE);
                } else if(doDelete) {
                    ifgRecord.setAction(DELETE_ACTION_TYPE);
                    rRecord = aRecord;
                } else {
                    continue;
                }
                CustomField customField =
                        getCustomConfigManager().getLocalizedCustomField(
                                ls.getUser().getLangID(),
                                dataSet.getEntityName(),
                                dataSet.getName());

                if(ifg == null) {
                    ifg = new Ifg();
                }
                ifg.setCaption(customField.getCaption());

                for(int j = 0; j < rRecord.size(); j++) {

                    EQLResCell resCell = rRecord.getResCell(j);
                    EQLReqField reqField = resCell.getReqField();
                    EQLReqEntity reqEntity = reqField.getReqEntity();
                    Efield efield = reqField.getField();
                    Entity entity = reqEntity.getEntity();

                    HistoryField fld = null;
                    boolean isPkey = efield.getPkey().booleanValue();
                    if(isPkey) {
                        PkeyField pkeyFld = new PkeyField();
                        pkeyFld.setPkeyCaption(getFieldCaption(entity, efield));
                        pkeyFld.setPkeyName(efield.getName());
                        pkeyFld.setPkeyValue(
                                resCell.getEQLObject().getObject().toString());
                        dsLogFieldNames.remove(efield.getName());
                        ifgRecord.addPkeyField(pkeyFld);

                    } else if(doDelete && dsLogFieldNames.contains(
                            efield.getName())) {
                        String value;
                        if(efield.getListref() != null) {
                            try {
                                value = EQLUtils.getListFieldValue(rRecord,
                                        resCell,
                                        resCell.getListField().getReqField(),
                                        false, ls, getEQLManager()).toString();
                            } catch (EQLException ex) {
                                throw new GenericSystemException(ex);
                            }
                        } else {
                            try {
                                value = FieldConverter.EQLObject2String(ls,
                                        entity, efield, resCell.getEQLObject());
                            } catch (EfieldTransformException ex) {
                                throw new GenericSystemException(ex);
                            }
                        }
                        if(value != null) {
                            fld = new HistoryField();
                            fld.setName(efield.getName());
                            fld.setCaption(getFieldCaption(entity, efield));

                            fld.setHistoryValueOld(value);
                            fld.setHistoryValueNew(new String(value));
                        }

                    } else {
                        fld = getHistoryField(j, rRecord, dsLogFieldNames);
                    }

                    if(fld != null) {
                        ifgRecord.addHistoryField(fld);
                    }
                }

                if(ifgRecord.getHistoryFieldCount() > 0) {
                    ifg.addIfgRecord(ifgRecord);
                }
            }

            if((ifg != null) && (ifg.getIfgRecordCount() > 0)) {
                ifgData.addIfg(ifg);
            }

        } // for

        topic.setHistoryData(data);
        topic.setIfgData(ifgData);

        // Ok.
        return topic;

    }

    /* (non-javadoc)
     * @see HistoryBuilder#getLogFieldNames
     */
    public List getLogFieldNames(String dbObject) {

        List fieldNames = new ArrayList();
        JEOManagerLocal jeoManager = getJEOManager();
        List hndList = null;
        try {
            hndList = HistoryTableFieldObjectHandler.selectByTableName(
                    jeoManager, ls, dbObject);
        } catch (EQLException ex) {
            throw new GenericSystemException(ex);
        }

        if(hndList != null) {
            for(int i = 0; i < hndList.size(); i++) {
                HistoryTableFieldObjectHandler hnd
                        = (HistoryTableFieldObjectHandler) hndList.get(i);
                HistoryTableFieldObject o = (HistoryTableFieldObject) hnd
                        .getJEObject();
                fieldNames.add(o.getHis_field_name());
            }
        }

        return (fieldNames.size() == 0) ? null:fieldNames;

    }

    // ======================================================== Private methods

    //
    // Makes a HistoryField object with old and new values of a field from i-th resCell of resRecord.
    //

    private HistoryField getHistoryField(int i, EQLResRecord resRecord,
                                         List historyFieldNames) {

        // Get EQL data cell.
        EQLResCell resCell = resRecord.getResCell(i);
        if(!resCell.isValueChanged()) {
            return null;
        }

        // Get EQL objects for old and new field values.
        Entity entity = resCell.getReqField().getReqEntity().getEntity();
        Efield efield = resCell.getReqField().getField();
        EQLObject newObj = resCell.getEQLObject();
        EQLObject oldObj = resCell.getOldEQLObject();

        // Is this field must be logged?
        if(!historyFieldNames.contains(efield.getName())) {
            return null;
        }

        // Is its value changed?
        if(newObj.equals(oldObj)) {
            return null;
        }

        // Transform field, if it's not a link to another entity.
        String newValue, oldValue;
        if(efield.getListref() != null) {
            try {
                newValue = EQLUtils.getListFieldValue(resRecord, resCell,
                        resCell.getListField().getReqField(), true, ls,
                        getEQLManager()).toString();
                oldValue = EQLUtils.getListFieldValue(resRecord, resCell,
                        resCell.getListField().getReqField(), false, ls,
                        getEQLManager()).toString();
            } catch (EQLException ex) {
                throw new GenericSystemException(ex);
            }

        } else {
            try {
                newValue = FieldConverter.EQLObject2String(ls, entity, efield,
                        newObj);
                oldValue = FieldConverter.EQLObject2String(ls, entity, efield,
                        oldObj);
            } catch (EfieldTransformException ex) {
                throw new GenericSystemException(ex);
            }
        }

        // Convert null values into empty strings.
        newValue = (newValue == null ? StringHelper.EMPTY_VALUE:newValue);
        oldValue = (oldValue == null ? StringHelper.EMPTY_VALUE:oldValue);

        // Make the field object.
        HistoryField field = new HistoryField();
        field.setName(efield.getName());
        field.setCaption(getFieldCaption(entity, efield));
        field.setHistoryValueNew(newValue);
        field.setHistoryValueOld(oldValue);
        return field;
    }

    //
    // Returns a record from the list of record where the first resCell is equal to the first resCell of resRecord.
    //
    private EQLResRecord getAdditRecord(List records, EQLResRecord resRecord) {
        for(Iterator itr = records.iterator(); itr.hasNext();) {
            EQLResRecord record = (EQLResRecord) itr.next();
            if(((!record.doDelete()) && (!record.doUpdate()) && (!record
                    .isNew()))
                    && ((resRecord.getResCell(0).getReqField().equals(
                    record.getResCell(0).getReqField()))
                    && (resRecord.getResCell(0).getEQLObject().equals(
                    record.getResCell(0).getEQLObject())))) {

                return record;
            }
        }
        return resRecord;
    }

    //
    // Field caption.
    //
    private String getFieldCaption(Entity entity, Efield field) {
        String langID = ls.getUser().getLangID();
        String entityName = entity.getName();
        String efieldName = field.getName();
        CustomField customField = getCustomConfigManager()
                .getLocalizedCustomField(langID, entityName, efieldName);

        if(customField == null) {
            return "<" + field.getId() + ">";
        } else {
            return customField.getCaption();
        }
    }

    //
    // Gets the current date (formatted for the active user).
    //
    private String getHistoryTimestapm() {
        User user = getSession().getLogonSession().getUser();
        String datePattern = user.getDatePattern();
        String timePattern = user.getTimePattern();
        boolean isDatePosFirst = user.isDatePositionFirst();
        Locale locale = SecurityHelper.getJavaLocale(user.getCountryID(),
                user.getLangID());
        TimeZone tz = SecurityHelper.getJavaTimezone(user.getTimeZoneID());
        Date now = new Date(DateHelper.toUser(DateHelper.currentTimeMillis(),
                tz));

        return DateHelper.formatDate(now, datePattern, timePattern, locale);
    }

    // ============================================================ EJB getters

    // EQL Manager EJB reference getter.

    private EQLManagerLocal getEQLManager() {
        return (EQLManagerLocal) getSession().getCOM().
                getLocalObject(JNDINames.EQLManager, EQLManagerLocalHome.class);
    }

    // Custom Config Manager EJB reference getter.
    private CustomConfigManagerLocal getCustomConfigManager() {
        return (CustomConfigManagerLocal) getSession().getCOM().
                getLocalObject(JNDINames.CustomConfigManager,
                        CustomConfigManagerLocalHome.class);
    }

    // JEOManager EJB reference getter.
    private JEOManagerLocal getJEOManager() {
        return (JEOManagerLocal) getSession().getCOM().
                getLocalObject(JNDINames.JEOManager, JEOManagerLocalHome.class);
    }
}
