/*
 * Copyright 2014 volk.
 *
 * 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 com.va.jscp.session;

import com.va.jscp.io.FileInfo;
import com.va.jscp.io.FileInfo.FileType;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;

import static com.va.jscp.session.FileSystemEvent.newPwdSetEvent;

public abstract class FileSystemAccessor {

    private static final HashMap<UUID, WeakReference<FileSystemAccessor>> uuidMap = new HashMap<>();

    public static class Interaction<ResultT> {

        public void foundResult(ResultT result)
        {
        }

        public boolean isArborted()
        {
            return false;
        }
    }

    public static final Interaction NO_INTERACTION = new Interaction();

    public static FileSystemAccessor find(UUID uuid)
    {
        FileSystemAccessor f;
        synchronized (uuidMap) {
            WeakReference<FileSystemAccessor> ref = uuidMap.get(uuid);
            if (ref == null)
                return null;
            f = ref.get();
            if (f == null)
                uuidMap.remove(uuid);
        }
        return f;
    }

    private final UUID uuid;
    private final ArrayList<FileSystemEventListener> eventListeners = new ArrayList<>();

    public FileSystemAccessor()
    {
        this.uuid = UUID.randomUUID();
        synchronized (uuidMap) {
            uuidMap.put(uuid, new WeakReference<>(this));
        }
    }

    public void addFileSystemEventListener(FileSystemEventListener listener)
    {
        eventListeners.add(listener);
    }

    public void removeFileSystemEventListener(FileSystemEventListener listener)
    {
        eventListeners.remove(listener);
    }

    private void fireFileSystemEvent(FileSystemEvent event)
    {
       for (FileSystemEventListener listener : eventListeners) {
            if (event.getType() == FileSystemEvent.Type.PWD_SET) {
                listener.pwdSet(event);
            }
        }
    }

    protected void firePwdSetEvent(boolean pwdChanged)
    {
        fireFileSystemEvent(newPwdSetEvent(this, pwdChanged));
    }

    public void destroy()
    {
        synchronized (uuidMap) {
            uuidMap.remove(uuid);
        }
    }

    public UUID getUUID()
    {
        return uuid;
    }

    @Override
    public int hashCode()
    {
        int hash = 7;
        hash = 234 * hash + uuid.hashCode();
        return hash;
    }

    @Override
    public boolean equals(Object obj)
    {
        if ((obj == null) || (getClass() != obj.getClass())) {
            return false;
        }
        return uuid.equals(((FileSystemAccessor)obj).uuid);
    }

    public abstract void list(FileInfo path, boolean recursive, boolean directoriesOnly,
        Interaction<FileInfo> interaction) throws IOException;

    public abstract void make(FileInfo base, FileType type, String newName) throws IOException;

    public abstract boolean isChmodOwnSupported();

    public abstract void chmod(String mode, FileInfo fileInfo, boolean recursive, Interaction<Void> interaction)
        throws IOException;

    public abstract void chown(String ownership, FileInfo fileInfo, boolean recursive, Interaction<Void> interaction)
        throws IOException;

    public abstract void delete(FileInfo fileInfo, Interaction<Void> interaction) throws IOException;

    public abstract void move(FileInfo from, FileInfo to, Interaction<Void> interaction) throws IOException;

    public abstract boolean isFindSupported();

    public abstract void find(FileInfo startPoint, String pattern, Interaction<FileInfo> interaction)
        throws IOException;

    public abstract FileInfo getPwd() throws IOException;

    public abstract boolean setPwd(FileInfo directory) throws IOException;

    public abstract FileSink getFileSink(FileInfo fileInfo) throws IOException;

    public abstract FileSource getFileSource(FileInfo fileInfo) throws IOException;
}
