package com.error22.moleserver.storage;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.error22.moleserver.GsonUtil;
import com.error22.moleserver.events.EventManager;
import com.error22.moleserver.storage.LocationModel.LocationModelV1;
import com.error22.moleserver.storage.StorageManagerModel.StorageManagerModelV1;

public class MasterStorageManager {
	private ReentrantReadWriteLock lock;
	private File storageFile;
	private StorageManagerModelV1 model;
	private Map<String, LocationStatistics> locationStatistics;

	public MasterStorageManager(File baseDirectory) {
		lock = new ReentrantReadWriteLock(true);
		storageFile = new File(baseDirectory, "storage.json");
	}

	public void load() {
		lockWrite();
		try {
			EventManager.subscribe(this);

			LOGGER.info("Loading storage");

			if (storageFile.exists()) {
				LOGGER.debug("Found storage file");
				StorageManagerModel loadedModel = GsonUtil.loadJsonFile(storageFile, StorageManagerModel.class);

				if (loadedModel instanceof StorageManagerModelV1) {
					model = (StorageManagerModelV1) loadedModel;
				} else {
					throw new IllegalArgumentException("Unknown library manager model! " + loadedModel);
				}
			} else {
				model = new StorageManagerModelV1(new ArrayList<LocationModelV1>());
			}
			save();

			locationStatistics = new HashMap<String, LocationStatistics>();
		} finally {
			unlockWrite();
		}
	}

	public void save() {
		lockRead();
		try {
			GsonUtil.saveJsonFile(storageFile, model, StorageManagerModel.class);
		} finally {
			unlockRead();
		}
	}

	public List<LocationModelV1> getLocations() {
		try {
			lockRead();
			return model.getLocations();
		} finally {
			unlockRead();
		}
	}

	public List<LocationModelV1> getLocationsByHost(String host) {
		lockRead();
		try {
			return model.getLocations().stream().filter(s -> StringUtils.equals(s.getHost(), host))
					.collect(Collectors.toList());
		} finally {
			unlockRead();
		}
	}

	public LocationModelV1 getLocation(String id) {
		lockRead();
		try {
			for (LocationModelV1 location : model.getLocations()) {
				if (StringUtils.equals(location.getId(), id)) {
					return location;
				}
			}
			return null;
		} finally {
			unlockRead();
		}
	}

	public void setLocationStatistics(String id, LocationStatistics statistics) {
		lockWrite();
		try {
			locationStatistics.put(id, statistics);
		} finally {
			unlockWrite();
		}
	}

	public LocationStatistics getLocationStatistics(String id) {
		lockRead();
		try {
			return locationStatistics.get(id);
		} finally {
			unlockRead();
		}
	}

	public void lockRead() {
		lock.readLock().lock();
	}

	public void unlockRead() {
		lock.readLock().unlock();
	}

	public void lockWrite() {
		lock.writeLock().lock();
	}

	public void unlockWrite() {
		lock.writeLock().unlock();
	}

	private static final Logger LOGGER = LoggerFactory.getLogger(MasterStorageManager.class);
}
