package com.jscape.inet.ftp;

import com.jscape.util.Assert;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Machine listing entry as appears in MLST and MLSD commands.
 *
 * @author Alex
 */
@SuppressWarnings("unchecked")
public final class MLSEntry {

    private String pathname;
    private Long size;
    private Date lastModificationDate;
    private Date creationDate;
    private Type type;
    private String uniqueId;
    private Permissions permissions;
    private String language;
    private String mediaType;
    private String charset;
    private Map facts;

    /**
     * Creates a new entity instance.
     *
     * @param pathname pathname
     */
    public MLSEntry(String pathname) {
        Assert.notNull(pathname);
        this.pathname = pathname;
        this.facts = new HashMap();
    }

    /**
     * Returns pathname.
     *
     * @return pathname
     */
    public String getPathname() {
        return this.pathname;
    }

    /**
     * Returns size.
     *
     * @return size in bytes
     */
    public Long getSize() {
        return this.size;
    }

    /**
     * Sets size.
     *
     * @param size size in bytes
     */
    public void setSize(Long size) {
        this.size = size;
    }

    /**
     * Returns last modification date.
     *
     * @return last modification date
     */
    public Date getLastModificationDate() {
        return this.lastModificationDate;
    }

    /**
     * Sets last modification date.
     *
     * @param time last modification date
     */
    public void setLastModificationDate(Date time) {
        this.lastModificationDate = time;
    }

    /**
     * Returns creation date.
     *
     * @return creation date
     */
    public Date getCreationDate() {
        return this.creationDate;
    }

    /**
     * Sets creation date.
     *
     * @param creationDate creaion date
     */
    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    /**
     * Returns entry type.
     *
     * @return entry type
     */
    public Type getType() {
        return this.type;
    }

    /**
     * Sets entry type.
     *
     * @param type entry type
     */
    public void setType(Type type) {
        this.type = type;
    }

    /**
     * Returns entry unquie ID.
     *
     * @return entry unique ID
     */
    public String getUniqueId() {
        return this.uniqueId;
    }

    /**
     * Sets entry unquie ID.
     *
     * @param uniqueId entry unquie ID
     */
    public void setUniqueId(String uniqueId) {
        this.uniqueId = uniqueId;
    }

    /**
     * Returns entry permissions.
     *
     * @return entry permissions
     */
    public Permissions getPermissions() {
        return (this.permissions != null) ? new Permissions(this.permissions) : null;
    }

    /**
     * Sets entry permissions.
     *
     * @param permissions entry permissions
     */
    public void setPermissions(Permissions permissions) {
        this.permissions = (permissions != null) ? new Permissions(permissions) : null;
    }

    /**
     * Returns pathname language.
     *
     * @return pathname language
     */
    public String getLanguage() {
        return this.language;
    }

    /**
     * Sets pathname language.
     *
     * @param language pathname language
     */
    public void setLanguage(String language) {
        this.language = language;
    }

    /**
     * Returns entry media type.
     *
     * @return entry media type
     */
    public String getMediaType() {
        return this.mediaType;
    }

    /**
     * Sets entry media type.
     *
     * @param mediaType entry media type
     */
    public void setMediaType(String mediaType) {
        this.mediaType = mediaType;
    }

    /**
     * Returns entry charset.
     *
     * @return entry charset
     */
    public String getCharset() {
        return this.charset;
    }

    /**
     * Sets entry charset.
     *
     * @param charset entry charset
     */
    public void setCharset(String charset) {
        this.charset = charset;
    }

    /**
     * Returns fact value for the specified name.
     *
     * @param factname fact name
     *
     * @return fact value or <code>null</code> if value is not found
     */
    public String getAdditionalFact(String factname) {
        return (String) this.facts.get(factname);
    }

    /**
     * Sets fact value.
     *
     * @param factname fact name
     * @param value    fact value
     */
    public void setAdditionalFact(String factname, String value) {
        this.facts.put(factname, value);
    }

    /**
     * Returns OS facts.
     *
     * @return OS facts
     */
    public Map getAdditionalFacts() {
        return new HashMap(this.facts);
    }

    /**
     * Entry type.
     */
    public static final class Type {

        /**
         * File type.
         */
        public static final Type FILE = new Type("file");

        /**
         * Current directory.
         */
        public static final Type CURRENT_DIRECTORY = new Type("cdir");

        /**
         * Parent directory.
         */
        public static final Type PARENT_DIRECTORY = new Type("pdir");

        /**
         * Directory.
         */
        public static final Type DIRECTORY = new Type("dir");

        private String osName;
        private String name;

        /**
         * Creates a new type instance.
         *
         * @param osName OS name
         * @param name   type name
         */
        public Type(String osName, String name) {
            Assert.notNull(osName);
            Assert.isValidString(name);

            this.osName = osName;
            this.name = name;
        }

        /**
         * Creates a new type instance.
         *
         * @param name type name
         */
        private Type(String name) {
            this("", name);
        }

        /**
         * Returns OS name.
         *
         * @return OS name
         */
        public String getOsName() {
            return this.osName;
        }

        /**
         * Returns type name.
         *
         * @return type name
         */
        public String getName() {
            return this.name;
        }
    }

    /**
     * Entry properties.
     */
    public static final class Permissions {

        private boolean fileAppendingAllowed;
        private boolean fileCreationAllowed;
        private boolean deletionAllowed;
        private boolean directoryEnteringAllowed;
        private boolean renamingAllowed;
        private boolean listingAllowed;
        private boolean directoryCreationAllowed;
        private boolean directoryPurgingAllowed;
        private boolean fileReadingAllowed;
        private boolean fileWritingAllowed;

        /**
         * Creates a new permissions instance.
         */
        public Permissions() {
            //empty
        }

        /**
         * Copy constructor.
         *
         * @param that instance to copy
         */
        public Permissions(Permissions that) {
            this.fileAppendingAllowed = that.fileAppendingAllowed;
            this.fileCreationAllowed = that.fileCreationAllowed;
            this.deletionAllowed = that.deletionAllowed;
            this.directoryEnteringAllowed = that.directoryEnteringAllowed;
            this.renamingAllowed = that.renamingAllowed;
            this.listingAllowed = that.listingAllowed;
            this.directoryCreationAllowed = that.directoryCreationAllowed;
            this.directoryPurgingAllowed = that.directoryPurgingAllowed;
            this.fileReadingAllowed = that.fileReadingAllowed;
            this.fileWritingAllowed = that.fileWritingAllowed;
        }

        /**
         * Checks if file appending is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isFileAppendingAllowed() {
            return this.fileAppendingAllowed;
        }

        /**
         * Sets file appending option.
         *
         * @param value option value
         */
        public void setFileAppendingAllowed(boolean value) {
            this.fileAppendingAllowed = value;
        }

        /**
         * Checks if file creation is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isFileCreationAllowed() {
            return this.fileCreationAllowed;
        }

        /**
         * Sets file creation option.
         *
         * @param value option value
         */
        public void setFileCreationAllowed(boolean value) {
            this.fileCreationAllowed = value;
        }

        /**
         * Checks if file/directory option is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isDeletionAllowed() {
            return this.deletionAllowed;
        }

        /**
         * Sets file/directory deletion option.
         *
         * @param value option value
         */
        public void setDeletionAllowed(boolean value) {
            this.deletionAllowed = value;
        }

        /**
         * Checks if directory entering is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isDirectoryEnteringAllowed() {
            return this.directoryEnteringAllowed;
        }

        /**
         * Sets directory entering option.
         *
         * @param value option value
         */
        public void setDirectoryEnteringAllowed(boolean value) {
            this.directoryEnteringAllowed = value;
        }

        /**
         * Checks if renaming allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isRenamingAllowed() {
            return this.renamingAllowed;
        }

        /**
         * Sets renaming option value.
         *
         * @param value option value
         */
        public void setRenamingAllowed(boolean value) {
            this.renamingAllowed = value;
        }

        /**
         * Checks if listing is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isListingAllowed() {
            return this.listingAllowed;
        }

        /**
         * Sets listing option value.
         *
         * @param value option value
         */
        public void setListingAllowed(boolean value) {
            this.listingAllowed = value;
        }

        /**
         * Checks if directory creation is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isDirectoryCreationAllowed() {
            return this.directoryCreationAllowed;
        }

        /**
         * Sets directory creation option value.
         *
         * @param value option value
         */
        public void setDirectoryCreationAllowed(boolean value) {
            this.directoryCreationAllowed = value;
        }

        /**
         * Checks if directory purging is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isDirectoryPurgingAllowed() {
            return this.directoryPurgingAllowed;
        }

        /**
         * Sets directory purging option value.
         *
         * @param value option value
         */
        public void setDirectoryPurgingAllowed(boolean value) {
            this.directoryPurgingAllowed = value;
        }

        /**
         * Checks if file reading is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isFileReadingAllowed() {
            return this.fileReadingAllowed;
        }

        /**
         * Sets file reading option value.
         *
         * @param value option value
         */
        public void setFileReadingAllowed(boolean value) {
            this.fileReadingAllowed = value;
        }

        /**
         * Checks if file writing is allowed.
         *
         * @return <code>true</code> if action is allowed;
         *         <code>false</code> otherwise
         */
        public boolean isFileWritingAllowed() {
            return this.fileWritingAllowed;
        }

        /**
         * Sets file writing option value.
         *
         * @param value option value
         */
        public void setFileWritingAllowed(boolean value) {
            this.fileWritingAllowed = value;
        }

    }

}
