/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed 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.jboss.arquillian.container.spi.client.protocol.metadata;

import java.net.URI;

/**
 * Servlet
 *
 * @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
 * @author <a href="mailto:ian@ianbrandt.com">Ian Brandt</a>
 */
public class Servlet {
    static final String HTTP_SCHEME = "http://";
    static final String ROOT_CONTEXT = "/";

    private final String name;

    private final String contextRoot;

    private String scheme;
    private String host;

    private int port;

    public Servlet(String name, String contextRoot) {
        if (name == null) {
            throw new IllegalArgumentException("name must not be null");
        }
        if (contextRoot == null) {
            throw new IllegalArgumentException("contextRoot must not be null");
        }

        this.name = name;
        this.contextRoot = cleanContextRoot(contextRoot);
    }

    /**
     * Set the {@link HTTPContext} for this servlet.  This is required for
     * <code>Servlet</code> to be fully initialized.  Don't use
     * {@link #equals(Object)} prior to setting the <code>HTTPContext</code>,
     * which most likely implies not adding this to a collection beforehand
     * either.
     *
     * @param context
     *     the context to set
     *
     * @throws IllegalArgumentException
     *     if the context host is null
     * @see {@link HTTPContext#add(Servlet)}
     */
    void setParent(HTTPContext context) {
        if (context.getHost() == null) {
            throw new IllegalArgumentException(context.getClass().getSimpleName() + " host must not be null");
        }
        this.scheme = context.getScheme();
        this.host = context.getHost();
        this.port = context.getPort();
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @return the contextRoot
     */
    public String getContextRoot() {
        return contextRoot;
    }

    /**
     * Get the URI to the Servlet's context.
     *
     * @return the base URI, e.g. "http://localhost:8888/"
     */
    public URI getBaseURI() {
        return URI.create(getBaseURIAsString());
    }

    /**
     * Get the URI to the Servlet.
     *
     * @return the base URI, e.g. "http://localhost:8888/Test"
     */
    public URI getFullURI() {
        return URI.create(getBaseURIAsString() + name);
    }

    private String getBaseURIAsString() {
        return scheme + "://" + host + ":" + port + contextRoot + "/";
    }

    /**
     * @throws IllegalStateException
     *     if host is null
     * @see {@link #setParent(HTTPContext)}
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        assertHostState();

        final int prime = 31;
        int result = 1;
        result = prime * result + ((contextRoot == null) ? 0 : contextRoot.hashCode());
        result = prime * result + ((host == null) ? 0 : host.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + port;
        return result;
    }

    /**
     * @throws IllegalStateException
     *     if host is null
     * @see {@link #setParent(HTTPContext)}
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        assertHostState();

        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Servlet)) {
            return false;
        }
        Servlet other = (Servlet) obj;
        if (contextRoot == null) {
            if (other.contextRoot != null) {
                return false;
            }
        } else if (!contextRoot.equals(other.contextRoot)) {
            return false;
        }
        if (host == null) {
            if (other.host != null) {
                return false;
            }
        } else if (!host.equals(other.host)) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        if (port != other.port) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Servlet [name=" + name + ", contextRoot=" + contextRoot + "]";
    }

    private String cleanContextRoot(String contextRoot) {
        if (!contextRoot.startsWith("/")) {
            contextRoot = "/" + contextRoot;
        }

        // ARQ-554
        if (contextRoot.equals(ROOT_CONTEXT)) {
            contextRoot = "";
        }

        return contextRoot;
    }

    private void assertHostState() {
        if (host == null) {
            throw new IllegalStateException("host must not be null (see setParent(...))");
        }
    }
}
