package org.xbib.files.webdav.fs;

import org.xbib.io.webdav.client.WebDavClient;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;

public class WebdavChannel implements SeekableByteChannel {

    private final WebDavClient client;

    private final WebdavPath path;

    private InputStream in;

    private ByteArrayOutputStream out;

    private int position = 0;

    public WebdavChannel(WebdavPath webdavPath) {
        this.client = ((WebdavFileSystem) webdavPath.getFileSystem()).getClient();
        this.path = webdavPath;
    }

    @Override
    public boolean isOpen() {
        return in != null || out != null;
    }

    @Override
    public void close() throws IOException {
        synchronized (client) {
            if (in != null) {
                in.close();
                in = null;
            }
            if (out != null) {
                try {
                    client.put(path.toUri(), new ByteArrayInputStream(out.toByteArray()));
                } catch (InterruptedException e) {
                    throw new IOException(e);
                }
                out.close();
                out = null;
            }
        }
    }

    @Override
    public int read(ByteBuffer dst) throws IOException {
        synchronized (client) {
            if (in == null) {
                try {
                    in = client.get(path.toUri());
                } catch (InterruptedException e) {
                    throw new IOException(e);
                }
            }
            if (dst.hasArray()) {
                int numBytesToRead = in.read(dst.array());
                position += numBytesToRead;
                return numBytesToRead;
            }
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public int write(ByteBuffer src) throws IOException {
        OutputStream os = getOutputStream();
        int len = src.remaining();
        byte[] buf = new byte[len];
        while (src.hasRemaining()) {
            src.get(buf);
            os.write(buf);
        }
        position += len;
        return len;
    }

    private ByteArrayOutputStream getOutputStream() {
        ByteArrayOutputStream os = out;
        if (os == null) {
            synchronized (client) {
                os = out;
                if (os == null) {
                    os = new ByteArrayOutputStream();
                    this.out = os;
                }
            }
        }
        return os;
    }

    @Override
    public long position() throws IOException {
        return position;
    }

    @Override
    public SeekableByteChannel position(long newPosition) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public long size() throws IOException {
        return 0;
    }

    @Override
    public SeekableByteChannel truncate(long size) throws IOException {
        throw new UnsupportedOperationException();
    }
}
