/*
 * Copyright 20078 the original author or authors.
 * 
 * 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.xbib.files.ftp.mock.core.command;

import org.xbib.files.ftp.mock.core.CommandSyntaxException;
import org.xbib.files.ftp.mock.core.util.Assert;

import java.util.Arrays;
import java.util.List;
import org.xbib.files.ftp.mock.core.util.AssertFailedException;

/**
 * Represents a command received from an FTP client, containing a command name and parameters.
 * Objects of this class are immutable.
 *
 * @author Chris Mair
 */
public final class Command {

    private String name;
    private String[] parameters;

    /**
     * Construct a new immutable instance with the specified command name and parameters
     *
     * @param name       - the command name; may not be null
     * @param parameters - the command parameters; may be empty; may not be null
     */
    public Command(String name, String[] parameters) {
        Assert.notNull(name, "name");
        Assert.notNull(parameters, "parameters");
        this.name = name;
        this.parameters = copy(parameters);
    }

    /**
     * Construct a new immutable instance with the specified command name and parameters
     *
     * @param name       - the command name; may not be null
     * @param parameters - the command parameters; may be empty; may not be null
     */
    public Command(String name, List parameters) {
        this(name, (String[]) parameters.toArray(new String[parameters.size()]));
    }

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

    /**
     * @return the parameters
     */
    public String[] getParameters() {
        return copy(parameters);
    }

    /**
     * Get the String value of the parameter at the specified index
     *
     * @param index - the index
     * @return the parameter value as a String
     * @throws AssertFailedException
     *          if the parameter index is invalid or the value is not a valid String
     */
    public String getRequiredParameter(int index) {
        assertValidIndex(index);
        return parameters[index];
    }

    /**
     * Get the String value of the parameter at the specified index; return null if no parameter exists for the index
     *
     * @param index - the index
     * @return the parameter value as a String, or null if this Command does not have a parameter for that index
     */
    public String getParameter(int index) {
        return (parameters.length > index) ? parameters[index] : null;
    }

    /**
     * Get the String value of the parameter at the specified index; return null if no
     * parameter exists for the index. This is an alias for {@link #getParameter(int)}.
     *
     * @param index - the index
     * @return the parameter value as a String, or null if this Command does not have a parameter for that index
     */
    public String getOptionalString(int index) {
        return getParameter(index);
    }

    /**
     * @see Object#equals(Object)
     */
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || !(obj instanceof Command)) {
            return false;
        }
        return this.hashCode() == obj.hashCode();
    }

    /**
     * @see Object#hashCode()
     */
    public int hashCode() {
        String str = name + Arrays.asList(parameters);
        return str.hashCode();
    }

    /**
     * Return the String representation of this object
     *
     * @see Object#toString()
     */
    public String toString() {
        return "Command[" + name + ":" + Arrays.asList(parameters) + "]";
    }

    /**
     * Return the name, normalized to a common format - convert to upper case.
     * @param name - the command name
     * @return the name converted to upper case
     */
    public static String normalizeName(String name) {
        return name.toUpperCase();
    }

    /**
     * Construct a shallow copy of the specified array
     *
     * @param array - the array to copy
     * @return a new array with the same contents
     */
    private static String[] copy(String[] array) {
        String[] newArray = new String[array.length];
        System.arraycopy(array, 0, newArray, 0, array.length);
        return newArray;
    }

    /**
     * Assert that the index is valid
     *
     * @param index - the index
     * @throws CommandSyntaxException
     *          - if the parameter index is invalid
     */
    private void assertValidIndex(int index) {
        if (index < 0 || index >= parameters.length) {
            throw new CommandSyntaxException("The parameter index " + index + " is not valid for " + this);
        }
    }

}
