/********************************************************************
 * Copyright © 2007 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.esr.common.util;

import java.math.BigDecimal;

import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.ee.EGTSetting;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType;
import gov.va.med.esr.common.model.lookup.Gender;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.common.model.workload.WorkflowCaseInfo;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.SystemParameterService;
import gov.va.med.esr.service.trigger.BulletinTrigger;
import gov.va.med.esr.service.trigger.BulletinTriggerEvent;
import gov.va.med.esr.service.trigger.ClockTriggerEvent;
import gov.va.med.esr.service.trigger.CommonTrigger;
import gov.va.med.esr.service.trigger.CommonTriggerEvent;
import gov.va.med.esr.service.trigger.CommonTriggerIdentity;
import gov.va.med.esr.service.trigger.EGTTriggerEvent;
import gov.va.med.esr.service.trigger.FilterSitesPersonTriggerEvent;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;
import gov.va.med.esr.service.trigger.LetterTrigger;
import gov.va.med.esr.service.trigger.LetterTriggerEvent;
import gov.va.med.esr.service.trigger.MessageCaseTriggerEvent;
import gov.va.med.esr.service.trigger.PartialPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;
import gov.va.med.esr.service.trigger.RetransmitTriggerEvent;
import gov.va.med.esr.service.trigger.TargetedPersonTriggerEvent;
import gov.va.med.fw.model.AuditInfo;
import gov.va.med.fw.scheduling.ScheduledProcessInvocationContext;
import gov.va.med.fw.scheduling.ScheduledProcessTriggerEvent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.BaseTriggerIdentity;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerIdentity;
import gov.va.med.fw.util.ObjectVersioningUtil;

/**
 * Verifies integrity of object evolution for serialized instances in Common.
 * 
 * <p>
 * General purpose test driver to provide safe guards for object versioning for
 * a finite set of Classes. This requires full graphs of objects to be created
 * and serialized to properly catch all types of exceptions. This approach will
 * catch all structural violations (does not cover logic issues) for class
 * evolution.
 * 
 * <p>
 * All classes that need to verify deserialization should exist in
 * TARGET_CLASSES. If those classes actually need their .ser file created (or
 * re-created in cases of evolved classes that do NOT need to support backwards
 * compatability) then they should also be in TARGET_CLASSES_NEEDING_SERIALIZING.
 * 
 * <p>
 * For each Class in TARGET_CLASSES_NEEDING_SERIALIZING, a "generate" method will
 * be invoked to create the object, where the full method name is "generate" +
 * short Class name (no package). This uses convention over configuration.
 * 
 * <p>
 * If all entries in TARGET_CLASSES need their .ser file created (or re-created
 * in cases of evolved classes that do NOT need to support backwards
 * compatability), a convenience (as opposed to entering all of them also in
 * TARGET_CLASSES_NEEDING_SERIALIZING) could be to toggle the "serializeAllClasses"
 * to true. This will create .ser files for all classes listed in
 * TARGET_CLASSES.
 * 
 * <p>
 * This class should never be version controlled with values in
 * TARGET_CLASSES_NEEDING_SERIALIZING or "serializeAllClasses" set to true. Those
 * entries should be reset after generating the necessary .ser files.
 * 
 * Created Apr 18, 2007 3:58:18 PM
 * 
 * @author DNS   BOHMEG
 */
public class ObjectVersioningTest extends AbstractCommonTestCase {
    private Class[] TARGET_CLASSES = { TriggerEvent.class,
            ScheduledProcessTriggerEvent.class, CommonTriggerEvent.class,
            PersonTriggerEvent.class, EGTTriggerEvent.class,
            RetransmitTriggerEvent.class, BulletinTriggerEvent.class,
            ClockTriggerEvent.class, LetterTriggerEvent.class,
            MessageCaseTriggerEvent.class, TargetedPersonTriggerEvent.class,
            IncomeYearTriggerEvent.class, PartialPersonTriggerEvent.class,
            FilterSitesPersonTriggerEvent.class };

    protected static PersonEntityKey PERSON_ENTITY_KEY = CommonEntityKeyFactory
            .createPersonIdEntityKey("123");

    protected static VPIDEntityKey PERSON_VPID = CommonEntityKeyFactory
            .createVPIDEntityKey("vpid");

    protected boolean serializeAllClasses = false;

    private Class[] TARGET_CLASSES_NEEDING_SERIALIZING = {};

    private SystemParameterService systemParameterService;
    
    /**
     * Standard test entry point
     * @throws Exception
     */
    public final void testVersioning() throws Exception {
        Class[] classesToSerialize = getClassesToSerialize();
        if(classesToSerialize != null && classesToSerialize.length > 0) {
            ObjectVersioningUtil.generateSerializedFiles(
                    classesToSerialize, getESRVersion(), this);
        }
        ObjectVersioningUtil.deserializeFiles(getClassesToDeserialize());
    }
    
    /* Can be overriden by subclasses */
    protected Class[] getClassesToDeserialize() {
        return TARGET_CLASSES;
    }
    
    protected Class[] getClassesToSerialize() {
        if (serializeAllClasses)
            return getClassesToDeserialize();
        
        return TARGET_CLASSES_NEEDING_SERIALIZING;
    }
    
    
    /* These need to be public for reflection to find them */
    public TriggerEvent generateTriggerEvent() throws Exception {
        return new TriggerEvent("triggerEventWithPayload",
                new ScheduledProcessInvocationContext());
    }

    public ScheduledProcessTriggerEvent generateScheduledProcessTriggerEvent()
            throws Exception {
        return new ScheduledProcessTriggerEvent(
                new ScheduledProcessInvocationContext(),
                new BaseTriggerIdentity(TriggerIdentity.DestinationType.DEFAULT));
    }

    public CommonTriggerEvent generateCommonTriggerEvent() throws Exception {
        return new CommonTriggerEvent("name", new Person(),
                new CommonTriggerIdentity(CommonTrigger.DestinationType.COMMON,
                        CommonTrigger.TargetType.VBA,
                        CommonTrigger.DispatchType.CREATE,
                        CommonTrigger.EntityType.LETTER));
    }

    public PersonTriggerEvent generatePersonTriggerEvent() throws Exception {
        PersonTriggerEvent event = new PersonTriggerEvent(
                new PersonTriggerIdentity(PersonTrigger.DestinationType.COMMON,
                        PersonTrigger.TargetType.VBA,
                        PersonTrigger.DispatchType.CREATE,
                        PersonTrigger.DataType.ADDRESS));
        event.setPersonId(PERSON_ENTITY_KEY);
        PersonIdentityTraits traits = new PersonIdentityTraits();
        traits.addName(new Name());
        traits.setAuditInfo(new AuditInfo());
        traits.setBirthRecord(new BirthRecord());
        traits.setGender(this.getLookupService().getGenderByCode(
                Gender.MALE.getCode()));
        traits.setVpid(PERSON_VPID);
        event.setIdentityTraits(traits);
        return event;
    }

    public EGTTriggerEvent generateEGTTriggerEvent() throws Exception {
        return new EGTTriggerEvent(new EGTSetting());
    }

    public RetransmitTriggerEvent generateRetransmitTriggerEvent()
            throws Exception {
        return new RetransmitTriggerEvent(new BigDecimal(123), false);
    }

    public BulletinTriggerEvent generateBulletinTriggerEvent() throws Exception {
        return new BulletinTriggerEvent(
                BulletinTrigger.DataType.BAD_ADDRESS_NOTIFY);
    }

    public ClockTriggerEvent generateClockTriggerEvent() throws Exception {
        return new ClockTriggerEvent("name", PERSON_ENTITY_KEY);
    }

    public LetterTriggerEvent generateLetterTriggerEvent() throws Exception {
        return new LetterTriggerEvent(this.getLookupService()
                .getComLetterTemplateTypeByCode(
                        ComLetterTemplateType.FORM_NUMBER_290.getCode()),
                LetterTrigger.CategoryType.DEPENDENT_LETTER);
    }

    public MessageCaseTriggerEvent generateMessageCaseTriggerEvent()
            throws Exception {
        MessageCaseTriggerEvent event = new MessageCaseTriggerEvent();
        event.setWorkflowCaseInfo(new WorkflowCaseInfo());
        return event;
    }

    public TargetedPersonTriggerEvent generateTargetedPersonTriggerEvent()
            throws Exception {
        return new TargetedPersonTriggerEvent(
                PersonTrigger.DestinationType.COMMON,
                PersonTrigger.TargetType.VBA,
                PersonTrigger.DispatchType.CREATE,
                PersonTrigger.DataType.ADDRESS);
    }

    public IncomeYearTriggerEvent generateIncomeYearTriggerEvent()
            throws Exception {
        return new IncomeYearTriggerEvent(PersonTrigger.DestinationType.COMMON,
                PersonTrigger.TargetType.VBA,
                PersonTrigger.DispatchType.CREATE,
                PersonTrigger.DataType.ADDRESS);
    }

    public PartialPersonTriggerEvent generatePartialPersonTriggerEvent()
            throws Exception {
        return new PartialPersonTriggerEvent(
                PersonTrigger.DestinationType.COMMON,
                PersonTrigger.TargetType.VBA,
                PersonTrigger.DispatchType.CREATE,
                PersonTrigger.DataType.ADDRESS);
    }

    public FilterSitesPersonTriggerEvent generateFilterSitesPersonTriggerEvent()
            throws Exception {
        return new FilterSitesPersonTriggerEvent(
                PersonTrigger.DestinationType.COMMON,
                PersonTrigger.TargetType.VBA,
                PersonTrigger.DispatchType.CREATE,
                PersonTrigger.DataType.ADDRESS);
    }

    public SystemParameterService getSystemParameterService() {
        return systemParameterService;
    }

    public void setSystemParameterService(
            SystemParameterService systemParameterService) {
        this.systemParameterService = systemParameterService;
    }
    
    private String getESRVersion() throws ServiceException {
        return this.systemParameterService.getEsrVersion();
    }
}
