package name.admitriev.jhelper.components;

import com.intellij.notification.NotificationType;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.util.text.StringTokenizer;
import com.jetbrains.cidr.lang.psi.OCFile;
import name.admitriev.jhelper.IDEUtils;
import name.admitriev.jhelper.network.SimpleHttpServer;
import name.admitriev.jhelper.task.TaskData;
import name.admitriev.jhelper.task.TaskUtils;
import name.admitriev.jhelper.ui.Notificator;
import name.admitriev.jhelper.ui.UIUtils;
import net.egork.chelper.parser.*;
import net.egork.chelper.task.Task;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * A Component to monitor request from CHelper Chrome Extension and parse them to Tasks
 */
public class ChromeParser implements ProjectComponent {
	private static final int PORT = 4243;
	private static final Map<String, Parser> PARSERS;

	static {
		Map<String, Parser> taskParsers = new HashMap<>();
		taskParsers.put("yandex", new YandexParser());
		taskParsers.put("codeforces", new CodeforcesParser());
		taskParsers.put("hackerrank", new HackerRankParser());
		taskParsers.put("facebook", new FacebookParser());
		taskParsers.put("usaco", new UsacoParser());
		taskParsers.put("gcj", new GCJParser());
		taskParsers.put("bayan", new BayanParser());
		taskParsers.put("kattis", new KattisParser());
		taskParsers.put("codechef", new CodeChefParser());
		taskParsers.put("hackerearth", new HackerEarthParser());
		taskParsers.put("atcoder", new AtCoderParser());
		taskParsers.put("csacademy", new CSAcademyParser());
		taskParsers.put("new-gcj", new NewGCJParser());
		taskParsers.put("json", new JSONParser());
		PARSERS = Collections.unmodifiableMap(taskParsers);
	}

	private SimpleHttpServer server = null;
	private Project project;

	public ChromeParser(Project project) {
		this.project = project;
	}

	@Override
	public void projectOpened() {
		try {
			server = new SimpleHttpServer(
					new InetSocketAddress("localhost", PORT),
					request -> {
						StringTokenizer st = new StringTokenizer(request);
						String type = st.nextToken();
						Parser parser = PARSERS.get(type);
						if (parser == null) {
							Notificator.showNotification(
									"Unknown parser",
									"Parser " + type + " unknown, request ignored",
									NotificationType.INFORMATION
							);
							return;
						}
						String page = request.substring(st.getCurrentPosition());
						Collection<Task> tasks = parser.parseTaskFromHTML(page);
						if (tasks.isEmpty()) {
							Notificator.showNotification(
									"Couldn't parse any task",
									"Maybe format changed?",
									NotificationType.WARNING
							);
						}

						Configurator configurator = project.getComponent(Configurator.class);
						Configurator.State configuration = configurator.getState();
						String path = configuration.getTasksDirectory();
						for (Task rawTask : tasks) {
							TaskData task = new TaskData(
									rawTask.name,
									rawTask.taskClass,
									String.format("%s/%s.cpp", path, rawTask.taskClass),
									rawTask.input,
									rawTask.output,
									rawTask.testType,
									rawTask.tests
							);
							PsiElement generatedFile = TaskUtils.saveNewTask(task, project);
							UIUtils.openMethodInEditor(project, (OCFile) generatedFile, "solve");
						}

						IDEUtils.reloadProject(project);
					}
			);

			new Thread(server, "ChromeParserThread").start();
		}
		catch (IOException ignored) {
			Notificator.showNotification(
					"Could not create serverSocket for Chrome parser",
					"Probably another CHelper or JHelper project is running?",
					NotificationType.ERROR
			);
		}
	}

	@Override
	public void projectClosed() {
		if (server != null) {
			server.stop();
		}
	}
}