package be.iminds.ilabt.jfed.rspec.model;

import be.iminds.ilabt.jfed.rspec.parser.ExtraXml;
import be.iminds.ilabt.jfed.util.GeniUrn;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.List;

/**
 * RspecNode
 */
public interface RspecNode {
    ModelRspec getRspec();

    /* get a unique ID, composed of client_id+component_manager_id or component_id+component_manager_id */
    String getUniqueId();

    String getClientId();

    void setClientId(String value);

    double getEditorX();

    void setEditorX(double value);

    double getEditorY();

    void setEditorY(double value);

    double getFlackEditorX();

    void setFlackEditorX(double flackEditorX);

    double getFlackEditorY();

    void setFlackEditorY(double flackEditorY);

    List<HardwareType> getHardwareTypes();

    /**
     * @return null if exclusive property not specified
     */
    Boolean getExclusive();

    /**
     * @param value exclusive property, or null to leave it unspecified
     */
    void setExclusive(Boolean value);

    /**
     * @return null if available property not specified
     */
    Boolean getAvailable();

    /**
     * @param available available property, or null to leave it unspecified
     */
    void setAvailable(Boolean available);

    //////////////////////////////////////////////////////////////////////////////////
    // These methods are for convenience, when there is only a single sliver type //
    //////////////////////////////////////////////////////////////////////////////////

    /** throws exception when multiple sliver types.
     * @return null if no sliver type, the name of the sliver type otherwise */
    String getSliverTypeName();
    /**
     * if there is a single sliver type, this ONLY changes the sliver type name, and does not modify the other parameters
     * @param name may not be null
     */
    void setSliverTypeName(String name);
    /**
     * if there is a single sliver type, this ONLY changes the sliver type disk image, and does not modify the other parameters
     * throw exception if there is no sliver type
     * if there are multiple sliver type, this changes the first one or throw an exception (so you should not use it in that case!)
     * @param diskImageName may be null to remove disk image
     */
    void setSliverTypeDiskImage(String diskImageName);
    /** throws exception when multiple sliver types.
     * @return null if no sliver type */
    SliverType getSliverType();

    /**
     * removes any sliver types set, puts the specified sliver type as only element in the list.
     * @param singleType the single sliver type to set, overwriting any existing. If this is null, all sliver types are removed instead.
     */
    void setSliverType(SliverType singleType);

    String getOsImageUrn();
//    public void setOsImageUrn(String value);

//    public String getOsImageUrl();
//    public void setOsImageUrl(String value);

    //////////////////////////////////////////////////////////////////////////////////

    List<SliverType> getSliverTypes();

    NodeLocation getLocation();

    void setLocation(NodeLocation value);

    GeniUrn getComponentManagerId();

    void setComponentManagerId(GeniUrn value);

    void setComponentManagerId(String value);

    GeniUrn getComponentId();

    void setComponentId(GeniUrn value);

    void setComponentId(String value);

    String getComponentName();

    void setComponentName(String value);

    GeniUrn getSliverId();

    void setSliverId(String sliverId);

    void setSliverId(GeniUrn sliverId);

    String getHostName();

    void setHostName(String value);

    boolean getRoutableControlIp();
    void setRoutableControlIp(boolean hasRoutableControlIp);

    List<? extends ExecuteService> getExecuteServices();

    void setExecuteServices(List<? extends ExecuteService> value);

    void addExecuteService(ExecuteService executeService);

    List<? extends InstallService> getInstallServices();

    void setInstallServices(List<? extends InstallService> value);

    void addInstallService(InstallService installService);

    List<LoginService> getLoginServices();

    void setLoginServices(List<LoginService> value);

    List<ProxyService> getProxyServices();

    void setProxyServices(List<ProxyService> value);

    List<? extends AnsibleService> getAnsibleServices();

    void setAnsibleServices(List<? extends AnsibleService> value);

    void addAnsibleService(AnsibleService as);

    List<? extends RspecInterface> getInterfaces();

    /**
     * Returns the interface with the given client_id if it exists on this node.
     *
     * @param clientId -
     * @return -
     */
    RspecInterface getInterfaceByClientId(String clientId);
    RspecInterface getInterfaceByUniqueId(String uniqueId);

    List<? extends RspecLink> getLinks();

    void addLeaseIdRef(String leaseIdRef);

    /**
     * The Lease ID Ref should only be set in NITOS Reservation broker rspecs.
     *
     * This ID refers to the lease-id in the advertisements and manifest-rspecs, and to the lease-client-id in a request
     *
     * There can be multiple Lease ID references, for example, in the advertisement RSpec
     * @return -
     */
    List<String> getLeaseIdRefs();

    List<String> getAnsibleGroups();

    /** the returned list is modifiable, and modifying it changes the modelRspec. */
    @Nonnull
    List<ExtraXml> getExtraXml();
    List<ExtraXml> getExtraServicesXml();



    /*
     * certain RSpec tags can be present, even if they are empty. To reconstruct the RSpec exactly as in the input, we need to reconstruct these empty tags as well.
     * This list contains all tags that should be in the rspec, EVEN if they are empty.
     * Note that non-empty tags will always be added.
     *
     * Typically, only the StaxRspecParser and StaxRspecWriter need to bother with this.
     * For other users of the RSpec, this is never important, as this doesn't change the data stored in the RSpec.
     * */
    enum EmptyTag { SERVICES }

    void addEmptyTag(EmptyTag e);
    boolean hasEmptyTag(EmptyTag e);
    /** direct access to the collection, so that manipulations are stored. */
    Collection<EmptyTag> getEmptyTags();

    /**
     * replace all properties with the properties of another node
     * this assumes the relevant id's are the same.
     *
     * Useful when merging manifests.
     *
     * @param otherNode the other node, from which the properties have to be used
     * @param overwriteWithNull if true, set properties from this node to null, if they are null at otherNode and remove from list if not in otherNode's list.
     *                          If false, keep properties from this node unchanged, if they are null at otherNode and only add to lists.
     */
    void overwritePropertiesWith(RspecNode otherNode, boolean overwriteWithNull);

    enum InterfaceCopyMethod { NO_COPY, CREATE_NEW_INTERFACE, STEAL_INTERFACE }
}
