package org.squiddev.cctweaks.core.patch;

import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.PeripheralAPI;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.filesystem.FileSystem;
import org.squiddev.cctweaks.api.network.INetworkAccess;
import org.squiddev.cctweaks.api.network.INetworkedPeripheral;
import org.squiddev.cctweaks.api.network.Packet;
import org.squiddev.patcher.visitors.MergeVisitor;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class PeripheralAPI_Patch extends PeripheralAPI {
	@MergeVisitor.Stub
	private PeripheralWrapper[] m_peripherals;
	@MergeVisitor.Stub
	private FileSystem m_fileSystem;

	@MergeVisitor.Stub
	public PeripheralAPI_Patch(IAPIEnvironment _environment) {
		super(_environment);
	}


	private abstract class PeripheralWrapper implements INetworkAccess, IComputerAccess {
		@MergeVisitor.Stub
		private final String m_side;
		@MergeVisitor.Stub
		private final IPeripheral m_peripheral;
		@MergeVisitor.Stub
		private boolean m_attached;
		@MergeVisitor.Stub
		private Set<String> m_mounts;

		@MergeVisitor.Stub
		public PeripheralWrapper(IPeripheral peripheral, String side) {
			m_side = side;
			m_peripheral = peripheral;
		}

		@MergeVisitor.Stub
		@MergeVisitor.Rename(from = {"attach"})
		public void nativeAttach() {
		}

		public synchronized void attach() {
			nativeAttach();
			if (m_peripheral instanceof INetworkedPeripheral) {
				((INetworkedPeripheral) m_peripheral).attachToNetwork(this, m_side);
			}
		}

		public void detach() {
			if (m_peripheral instanceof INetworkedPeripheral) {
				((INetworkedPeripheral) m_peripheral).detachFromNetwork(this, m_side);
			}

			m_peripheral.detach(this);

			synchronized (this) {
				m_attached = false;
				for (String mount : m_mounts) {
					m_fileSystem.unmount(mount);
				}
				m_mounts.clear();
			}
		}

		@Nonnull
		@Override
		public Map<String, IPeripheral> getPeripheralsOnNetwork() {
			Map<String, IPeripheral> peripheralMap = new HashMap<String, IPeripheral>();
			for (int i = 0; i < 6; ++i) {
				if (m_peripherals[i] != null) {
					peripheralMap.put(Computer.s_sideNames[i], m_peripherals[i].m_peripheral);
				}
			}
			return peripheralMap;
		}

		@Override
		public void invalidateNetwork() {
		}

		@Override
		public boolean transmitPacket(@Nonnull Packet packet) {
			return false;
		}
	}
}

@@ -697,64 +697,332 @@
 t;%0A%0A
-public class PeripheralAPI_Patch extends PeripheralAPI %7B
+/**%0A * - Implement %7B@link INetworkAccess%7D on the peripheral wrapper and attach/detach networked peripherals.%0A * - Remove/move synchronisation in a couple of places to solve problems with multi-threading.%0A */%0Apublic class PeripheralAPI_Patch extends PeripheralAPI %7B%0A%09@MergeVisitor.Stub%0A%09private IAPIEnvironment m_environment;
 %0A%09@M
@@ -2833,14 +2833,596 @@
 lse;%0A%09%09%7D
+%0A%0A%09%09public int getID() %7B%0A%09%09%09if (!m_attached) %7B%0A%09%09%09%09throw new RuntimeException(%22You are not attached to this Computer%22);%0A%09%09%09%7D else %7B%0A%09%09%09%09return m_environment.getComputerID();%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09public void queueEvent(String event, Object%5B%5D arguments) %7B%0A%09%09%09if (!m_attached) %7B%0A%09%09%09%09throw new RuntimeException(%22You are not attached to this Computer%22);%0A%09%09%09%7D else %7B%0A%09%09%09%09m_environment.queueEvent(event, arguments);%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09public String getAttachmentName() %7B%0A%09%09%09if (!m_attached) %7B%0A%09%09%09%09throw new RuntimeException(%22You are not attached to this Computer%22);%0A%09%09%09%7D else %7B%0A%09%09%09%09return m_side;%0A%09%09%09%7D%0A%09%09%7D
 %0A%09%7D%0A%7D%0A

Remove additional synchronisation from PeripheralWrapper