// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
//
// 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 org.apache.cloudstack.network.contrail.model;

import java.io.IOException;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.TreeSet;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import com.cloud.exception.InternalErrorException;

/**
 * ModelObject
 *
 * A model object represents the desired state of the system.
 *
 * The object constructor should set the uuid and the internal id of the cloudstack objects.
 *
 * The build method reads the primary database (typically cloudstack mysql) and derives the state that
 * we wish to reflect in the contrail API. This method should not modify the Contrail API state.
 *
 * The verify method reads the API server state and compares with cached properties.
 *
 * The update method pushes updates to the contrail API server.
 */
public interface ModelObject {

    public static class ModelReference implements Comparable<ModelReference>, Serializable {

        private static final long serialVersionUID = -2019113974956703526L;
    protected Logger logger = LogManager.getLogger(getClass());

        /*
         * WeakReference class is not serializable by definition. So, we cannot enforce its serialization unless we write the implementation of
         * methods writeObject() and readObject(). Since the code was already not serializing it, it's been marked as transient.
         */
        transient WeakReference<ModelObject> reference;

        ModelReference(ModelObject obj) {
            reference = new WeakReference<ModelObject>(obj);
        }

        @Override
        public int compareTo(ModelReference other) {
            ModelObject lhs = reference.get();
            ModelObject rhs = other.reference.get();
            if (lhs == null) {
                if (rhs == null) {
                    return 0;
                }
                return -1;
            }

            return lhs.compareTo(rhs);
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((reference == null) ? 0 : reference.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other)
                return true;
            if (other == null)
                return false;
            try {
                ModelReference rhs = (ModelReference)other;
                return compareTo(rhs) == 0;
            } catch (ClassCastException ex) {
                // not this class , so
                return false;
            }
        }

        public ModelObject get() {
            return reference.get();
        }

    };

    public void addSuccessor(ModelObject child);

    public TreeSet<ModelReference> ancestors();

    public void clearSuccessors();

    public int compareTo(ModelObject o);

    /**
     * Delete the object from the API server.
     * @param controller
     * @throws IOException
     */
    public void delete(ModelController controller) throws IOException;

    /**
     * Deletes the object from the data model graph.
     *
     * @param controller
     * @throws IOException
     */
    public void destroy(ModelController controller) throws IOException;

    public void removeSuccessor(ModelObject child);

    public TreeSet<ModelObject> successors();

    /**
     * Push updates to Contrail API server. This API is only valid for objects in the database.
     * @param controller
     * @throws IOException
     * @throws InternalErrorException
     */
    public void update(ModelController controller) throws InternalErrorException, IOException;

    /**
     * Check that the state of the current object matches the state of the API server.
     * @param controller
     * @return
     */
    public boolean verify(ModelController controller);

    /*
     * Compare the state of existing model object with latest model object
     */
    public boolean compare(ModelController controller, ModelObject current);
}
