package com.earth2me.essentials.storage;

import com.earth2me.essentials.IConf;
import java.io.File;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import net.ess3.api.IEssentials;
import net.ess3.api.IReload;
import org.bukkit.Bukkit;

public abstract class AsyncStorageObjectHolder<T extends StorageObject> implements IConf, IStorageObjectHolder<T>, IReload {

    private transient T data;
    private final transient ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final transient Class<T> clazz;
    protected final transient IEssentials ess;

    public AsyncStorageObjectHolder(final IEssentials ess, final Class<T> clazz) {
        this.ess = ess;
        this.clazz = clazz;
        try {
            this.data = clazz.newInstance();
        } catch (IllegalAccessException ex) {
            Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
        } catch (InstantiationException ex) {
            Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
        }
    }

    @Override
    public T getData() {
        return data;
    }

    @Override
    public void acquireReadLock() {
        rwl.readLock().lock();
    }

    @Override
    public void acquireWriteLock() {
        while (rwl.getReadHoldCount() > 0) {
            rwl.readLock().unlock();
        }
        rwl.writeLock().lock();
        rwl.readLock().lock();
    }

    @Override
    public void close() {
        unlock();
    }

    @Override
    public void unlock() {
        if (rwl.isWriteLockedByCurrentThread()) {
            rwl.writeLock().unlock();
            new StorageObjectDataWriter();
        }
        while (rwl.getReadHoldCount() > 0) {
            rwl.readLock().unlock();
        }
    }

    @Override
    public void reloadConfig() {
        new StorageObjectDataReader();
    }

    @Override
    public void onReload() {
        new StorageObjectDataReader();
    }

    public abstract void finishRead();

    public abstract void finishWrite();

    public abstract File getStorageFile();

    private class StorageObjectDataWriter extends AbstractDelayedYamlFileWriter {

        StorageObjectDataWriter() {
            super(ess, getStorageFile());
        }

        @Override
        public StorageObject getObject() {
            acquireReadLock();
            return getData();
        }

        @Override
        public void onFinish() {
            unlock();
            finishWrite();
        }
    }

    private class StorageObjectDataReader extends AbstractDelayedYamlFileReader<T> {

        StorageObjectDataReader() {
            super(ess, getStorageFile(), clazz);
        }

        @Override
        public void onStart() {
            rwl.writeLock().lock();
        }

        @Override
        public void onSuccess(final T object) {
            if (object != null) {
                data = object;
            }
            rwl.writeLock().unlock();
            finishRead();
        }

        @Override
        public void onException() {
            if (data == null) {
                try {
                    data = clazz.newInstance();
                } catch (IllegalAccessException ex) {
                    Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
                } catch (InstantiationException ex) {
                    Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
                }
            }
            rwl.writeLock().unlock();
        }
    }
}
