/*
 * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     07/16/2009 Andrei Ilitchev
//       - Bug 282553: JPA 2.0 JoinTable support for OneToOne and ManyToOne
package org.eclipse.persistence.testing.models.onetoonejointable;

import java.util.*;
import java.io.*;
import java.math.BigDecimal;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import org.eclipse.persistence.indirection.*;
import org.eclipse.persistence.descriptors.changetracking.*;

/**
 * <p><b>Purpose</b>: Represent a employee of an organization.
 * <p><b>Description</b>: An Employee is a root object in the Employee Demo.
 * It maintains relationships to all of the other objects in the system.
 * The employee shows usage of 1-1, 1-m, m-m, aggregate and transformation mappings.
 * The employee also shows usage of value holder to implement indirection for its relationships
 * (note, it is strongly suggested to always use value holders for relationships).
 */
public class Employee implements Serializable, ChangeTracker {
    // implements ChangeTracker for testing

    /** Primary key, mapped as a direct-to-field, BigDecimal -{@literal >} NUMBER, that makes use of sequence numbers to generate the id. */
    public BigDecimal id;

    /** Direct-to-field mapping, String -{@literal >} VARCHAR. */
    public String firstName;

    /** Direct-to-field mapping, String -{@literal >} VARCHAR. */
    public String lastName;

    /** Object-type mapping, maps "Male" -{@literal >} "M", "Female" -{@literal >} "F". */
    public String gender;

    /** One-to-one mapping, employee references its address through a foreign key. */
    public ValueHolderInterface address;

    /** One-to-one mapping (same class relationship), employee references its manager through a foreign key. */
    public ValueHolderInterface manager;

    public List<Employee> managedEmployees;

    /** Many-to-many mapping, employee references its projects through an intermediate join table. */
    public List<Project> projects;

    /** Direct-collection mapping, employee stores its collection of plain Strings in an intermediate table. */
    public List<String> responsibilitiesList;

    /** Direct-to-field mapping, int -{@literal >} NUMBER, salary of the employee in dollars. */
    public int salary;

    /** One-to-many mapping, employee references its collection of children arranged by age.
     * This relationship uses transparent indirection */
    public List<Child> children;

    /** One-to-one mapping , inverse relationship to Project.teamLeader */
    public ValueHolderInterface projectLed;

    /** For performance testing, how many times primary key extracted. */
    public static int getIdCallCount = 0;
    public PropertyChangeListener listener;

    @Override
    public PropertyChangeListener _persistence_getPropertyChangeListener() {
        return listener;
    }

    @Override
    public void _persistence_setPropertyChangeListener(PropertyChangeListener listener) {
        this.listener = listener;
    }

    public void propertyChange(String propertyName, Object oldValue, Object newValue) {
        if (listener != null) {
            if (oldValue != newValue) {
                listener.propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue));
            }
        }
    }

    public void collectionChange(String propertyName, Object oldValue, Object newValue, int changeType, boolean isChangeApplied) {
        if (listener != null) {
            listener.propertyChange(new CollectionChangeEvent(this, propertyName, oldValue, newValue, changeType, isChangeApplied));
        }
    }

    /**
     * For fields that make use of indirection the constructor should build the value holders.
     */
    public Employee() {
        this.firstName = "";
        this.lastName = "";
        this.address = new ValueHolder();
        this.manager = new ValueHolder();
        this.managedEmployees = new ArrayList();
        this.projects = new ArrayList();
        this.responsibilitiesList = new ArrayList();
        this.children = new ArrayList();
        this.projectLed = new ValueHolder();
    }

    /**
     * For bi-directional relationships, it is important to maintain both sides of the relationship when changing it.
     */
    public void addManagedEmployee(Employee employee) {
        getManagedEmployees().add(employee);
        employee.setManager(this);
    }

    public void addManagedEmployee(int index, Employee employee) {
        getManagedEmployees().add(index, employee);
        employee.setManager(this);
    }

    public Employee setManagedEmployee(int index, Employee employee) {
        Employee removedEmp = getManagedEmployees().set(index, employee);
        removedEmp.setManager(null);
        employee.setManager(this);
        return removedEmp;
    }

    public void addChild(Child child) {
        getChildren().add(child);
        child.setParent(this);
    }

    public void addProject(Project project) {
        getProjects().add(project);
        project.getEmployees().add(this);
    }

    public void addResponsibility(String responsibility) {
        getResponsibilitiesList().add(responsibility);
    }

    public Address getAddress() {
        return (Address)address.getValue();
    }

    public String getFirstName() {
        return firstName;
    }

    public String getGender() {
        return gender;
    }

    /**
     * Used for performance testing.
     */
    public static int getGetIdCallCount() {
        return getIdCallCount;
    }

    /**
     * Return the persistent identifier of the receiver.
     */
    public BigDecimal getId() {
        getIdCallCount++;
        return id;
    }

    public List<Child> getChildren(){
        return children;
    }

    public String getLastName() {
        return lastName;
    }

    public List<Employee> getManagedEmployees() {
        return managedEmployees;
    }

    public Employee getManager() {
        return (Employee)manager.getValue();
    }

    public Project getProjectLed() {
        return (Project)projectLed.getValue();
    }

    public List<Project> getProjects() {
        return projects;
    }

    public List<String> getResponsibilitiesList() {
        return responsibilitiesList;
    }

    public int getSalary() {
        return salary;
    }

    public void removeManagedEmployee(Employee employee) {
        getManagedEmployees().remove(employee);
        employee.setManager(null);
    }

    public void removeProject(Project project) {
        getProjects().remove(project);
        project.getEmployees().remove(this);
    }

    public void removeResponsibility(String responsibility) {
        getResponsibilitiesList().remove(responsibility);
    }

    public void setAddress(Address address) {
        propertyChange("address", this.address.getValue(), address);
        this.address.setValue(address);
    }

    public void setChildren(Vector children){
        this.children = children;
    }

    public void setFemale() {
        propertyChange("gender", this.gender, "Female");
        setGender("Female");
    }

    public void setFirstName(String firstName) {
        propertyChange("firstName", getFirstName(), firstName);
        this.firstName = firstName;
    }

    public void setGender(String gender) {
        propertyChange("gender", this.gender, gender);
        this.gender = gender;
    }

    /**
     * For performance testing.
     */
    public static void setGetIdCallCount(int value) {
        Employee.getIdCallCount = value;
    }

    /**
     * Set the persistent identifier of the receiver.
     * Note this should never be changed.
     * Consider making the primary key set methods protected or not having them.
     * In this demo the setId is required for testing purposes.
     */
    public void setId(BigDecimal id) {
        propertyChange("id", this.id, id);
        this.id = id;
    }

    public void setLastName(String lastName) {
        propertyChange("lastName", this.lastName, lastName);
        this.lastName = lastName;
    }

    public void setMale() {
        propertyChange("gender", this.gender, "Male");
        setGender("Male");
    }

    /**
     * Notice that the usage of value holders does not effect the public interface or usage of the class.
     * The get/set methods must however be changed to wrap/unwrap the value holder.
     */
    public void setManagedEmployees(List<Employee> managedEmployees) {
        propertyChange("managedEmployees", this.managedEmployees, managedEmployees);
        this.managedEmployees = managedEmployees;
    }

    /**
     * For bi-directional relationships, it is important to maintain both sides of the relationship when changing it.
     * Notice that the usage of value holders does not effect the public interface or usage of the class.
     * The get/set methods must however be changed to wrap/unwrap the value holder.
     */
    public void setManager(Employee manager) {
        propertyChange("manager", this.manager.getValue(), manager);
        this.manager.setValue(manager);
    }

    public void setProjectLed(Project projectLed) {
        propertyChange("projectLed", this.projectLed.getValue(), projectLed);
        this.projectLed.setValue(projectLed);
    }

    public void setProjects(List<Project> projects) {
        propertyChange("projects", this.projects, projects);
        this.projects = projects;
    }

    /**
     * Notice that the usage of value holders does not effect the public interface or usage of the class.
     * The get/set methods must however be changed to wrap/unwrap the value holder.
     */
    public void setResponsibilitiesList(Vector responsibilitiesList) {
        propertyChange("responsibilitiesList", this.responsibilitiesList, responsibilitiesList);
        this.responsibilitiesList = responsibilitiesList;
    }

    public void setSalary(int salary) {
        propertyChange("salary", this.salary, salary);
        this.salary = salary;
    }

    /**
     * Print the first &amp; last name
     */
    public String toString() {
        StringWriter writer = new StringWriter();

        writer.write("Employee: ");
        writer.write(getFirstName());
        writer.write(" ");
        writer.write(getLastName());
        return writer.toString();
    }
}
