package com.macuyiko.minecraftpyserver.jython;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.util.InteractiveInterpreter;

public class JyInterpreter extends InteractiveInterpreter {

	private static final AtomicInteger sequence = new AtomicInteger();
	private static final ConcurrentHashMap<Integer,JyInterpreter> interpreters = new ConcurrentHashMap<Integer,JyInterpreter>();
	
	private final int id;
	private long lastCall;
	private boolean permanent;
	private int timeout;
	private List<String> buffer = new ArrayList<String>();
	
	private static final int DEFAULT_IDLE_TIMEOUT = 60 * 15;
	
	public JyInterpreter() {
		this(false, DEFAULT_IDLE_TIMEOUT);
	}
	
	public JyInterpreter(boolean permanent) {
		this(permanent, DEFAULT_IDLE_TIMEOUT);
	}
	
	public JyInterpreter(int timeout) {
		this(false, timeout);
	}
	
	public JyInterpreter(boolean permanent, int timeout) {
		super(null, getPythonSystemState());
		this.id = sequence.incrementAndGet();
		interpreters.put(this.id, this);
		this.lastCall = System.currentTimeMillis();
		this.permanent = permanent;
		this.timeout = timeout;
		
		this.setOut(System.out);
		this.setErr(System.err);
	}
	
	public static void cleanIdle() {
		Iterator<JyInterpreter> it = interpreters.values().iterator();
		while (it.hasNext()) {
			JyInterpreter interpreter = it.next();
			if (!interpreter.isPermanent() && 
					interpreter.getTimeout() > 0 && 
					interpreter.getSecondsPassedSinceLastCall() >= interpreter.getTimeout()) {
				interpreter.close();
			}
		}
	}
	
	public boolean isAlive() {
		cleanIdle();
		return interpreters.containsKey(this.id);
	}
	
	public boolean isPermanent() {
		return permanent;
	}
	
	public void resetbuffer() {
		this.buffer.clear();
		super.resetbuffer();
	}

	public void close() {
		interpreters.remove(this.id);
		this.cleanup();
		super.close();
	}
	
	public int getSecondsPassedSinceLastCall() {
		return (int) ((System.currentTimeMillis() - this.lastCall) / 1000D);
	}
	
	public int getTimeout() {
		return timeout;
	}
	
	@Override
	public int hashCode() {
		return id;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		JyInterpreter other = (JyInterpreter) obj;
		if (id != other.id) return false;
		return true;
	}

	public boolean push(String line) {
		lastCall = System.currentTimeMillis();
		// The line should not have a trailing newline; it may have internal newlines.
		buffer.add(line);
		String source = String.join("\n", buffer);
		boolean more = this.runsource(source);
		if (!more)
			this.resetbuffer();
		return more;
	}
	
	public void exec(String code) {
		lastCall = System.currentTimeMillis();
		super.exec(code);
	}

	public void execfile(File script) {
		lastCall = System.currentTimeMillis();
		try {
			super.execfile(script.getAbsolutePath());
		} catch (PyException exc) {
            if (exc.match(Py.SystemExit)) {
                // Suppress this: we don't want clients to stop the whole JVM!
            	// We do stop this interpreter, however
            	this.close();
            	return;
            }
            showexception(exc);
        }
	}
	
	public void runcode(PyObject code) {
        try {
            exec(code);
        } catch (PyException exc) {
            if (exc.match(Py.SystemExit)) {
                // Suppress this: we don't want clients to stop the whole JVM!
            	// We do stop this interpreter, however
            	this.close();
            	return;
            }
            showexception(exc);
        }
    }
	
	public static PySystemState getPythonSystemState() {
		PySystemState sys = new PySystemState();
		addPathToPySystemState(sys, "./");
		addPathToPySystemState(sys, "./python/");
		addPathToPySystemState(sys, "./python-plugins/");
		File dependencyDirectory = new File("./");
		File[] files = dependencyDirectory.listFiles();
		for (int i = 0; i < files.length; i++) {
		    if (files[i].getName().toLowerCase().contains("spigot") && files[i].getName().toLowerCase().endsWith(".jar")) {
		    	addPathToPySystemState(sys, files[i].getAbsolutePath());
		    }
		}
		return sys;
	}
	
	public static void addPathToPySystemState(PySystemState sys, String path) {
		try {
			sys.path.append(new PyString(path));
		} catch (Exception e){}
	}
	
}