package com.jetbrains.edu.learning;

import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Transient;
import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.jetbrains.edu.learning.StudySerializationUtils.Xml.REMOTE_COURSE;

/**
 * Implementation of class which contains all the information
 * about study in context of current project
 */

@State(name = "StudySettings", storages = @Storage("study_project.xml"))
public class StudyTaskManager implements PersistentStateComponent<Element>, DumbAware {
  private static final Logger LOG = Logger.getInstance(StudyTaskManager.class);
  public static final int CURRENT_VERSION = 5;
  private Course myCourse;
  public int VERSION = 5;

  public Map<Task, List<UserTest>> myUserTests = new HashMap<>();

  public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx();
  private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT;
  private boolean myTurnEditingMode = false;

  @Transient private final Project myProject;

  public StudyTaskManager(Project project) {
    myProject = project;
  }

  public StudyTaskManager() {
    this(null);
  }

  public void setCourse(Course course) {
    myCourse = course;
  }

  @Nullable
  public Course getCourse() {
    return myCourse;
  }

  public void setStatus(AnswerPlaceholder placeholder, StudyStatus status) {
    placeholder.setStatus(status);
  }

  public void addUserTest(@NotNull final Task task, UserTest userTest) {
    List<UserTest> userTests = myUserTests.get(task);
    if (userTests == null) {
      userTests = new ArrayList<>();
      myUserTests.put(task, userTests);
    }
    userTests.add(userTest);
  }

  public void setUserTests(@NotNull final Task task, @NotNull final List<UserTest> userTests) {
    myUserTests.put(task, userTests);
  }

  @NotNull
  public List<UserTest> getUserTests(@NotNull final Task task) {
    final List<UserTest> userTests = myUserTests.get(task);
    return userTests != null ? userTests : Collections.emptyList();
  }

  public void removeUserTest(@NotNull final Task task, @NotNull final UserTest userTest) {
    final List<UserTest> userTests = myUserTests.get(task);
    if (userTests != null) {
      userTests.remove(userTest);
    }
  }

  public JBColor getColor(@NotNull final AnswerPlaceholder placeholder) {
    if (!placeholder.getUseLength() && placeholder.isActive() && placeholder.getActiveSubtaskInfo().isNeedInsertText()) {
      return JBColor.LIGHT_GRAY;
    }
    final StudyStatus status = placeholder.getStatus();
    if (status == StudyStatus.Solved) {
      return JBColor.GREEN;
    }
    if (status == StudyStatus.Failed) {
      return JBColor.RED;
    }
    return JBColor.BLUE;
  }

  public boolean hasFailedAnswerPlaceholders(@NotNull final TaskFile taskFile) {
    return taskFile.getActivePlaceholders().size() > 0 && taskFile.hasFailedPlaceholders();
  }

  @Nullable
  @Override
  public Element getState() {
    if (myCourse == null) {
      return null;
    }

    return serialize();
  }

  @NotNull
  private Element serialize() {
    Element el = new Element("taskManager");
    Element taskManagerElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT);
    XmlSerializer.serializeInto(this, taskManagerElement);

    if (myCourse instanceof RemoteCourse) {
      try {
        Element course = new Element(REMOTE_COURSE);
        //noinspection RedundantCast
        XmlSerializer.serializeInto((RemoteCourse)myCourse, course);

        final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE);
        xmlCourse.removeContent();
        xmlCourse.addContent(course);
      }
      catch (StudySerializationUtils.StudyUnrecognizedFormatException e) {
        LOG.error("Failed to serialize remote course");
      }
    }

    el.addContent(taskManagerElement);
    return el;
  }

  @Override
  public void loadState(Element state) {
    try {
      int version = StudySerializationUtils.Xml.getVersion(state);
      if (version == -1) {
        LOG.error("StudyTaskManager doesn't contain any version:\n" + state.getValue());
        return;
      }
      switch (version) {
        case 1:
          state = StudySerializationUtils.Xml.convertToSecondVersion(state);
        case 2:
          state = StudySerializationUtils.Xml.convertToThirdVersion(state, myProject);
        case 3:
          state = StudySerializationUtils.Xml.convertToForthVersion(state);
        case 4:
          state = StudySerializationUtils.Xml.convertToFifthVersion(state);
        //uncomment for future versions
        //case 5:
        //  state = StudySerializationUtils.Xml.convertToSixthVersion(state, myProject);
      }
      deserialize(state);
      VERSION = CURRENT_VERSION;
      if (myCourse != null) {
        myCourse.initCourse(true);
      }
    }
    catch (StudySerializationUtils.StudyUnrecognizedFormatException e) {
      LOG.error("Unexpected course format:\n", new XMLOutputter().outputString(state));
    }
  }

  private void deserialize(Element state) throws StudySerializationUtils.StudyUnrecognizedFormatException {
    final Element taskManagerElement = state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT);
    XmlSerializer.deserializeInto(this, taskManagerElement);
    final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE);
    final Element remoteCourseElement = xmlCourse.getChild(REMOTE_COURSE);
    if (remoteCourseElement != null) {
      final RemoteCourse remoteCourse = new RemoteCourse();
      XmlSerializer.deserializeInto(remoteCourse, remoteCourseElement);
      myCourse = remoteCourse;
    }
  }

  public static StudyTaskManager getInstance(@NotNull final Project project) {
    return ServiceManager.getService(project, StudyTaskManager.class);
  }

  public boolean shouldUseJavaFx() {
    return myShouldUseJavaFx;
  }

  public void setShouldUseJavaFx(boolean shouldUseJavaFx) {
    this.myShouldUseJavaFx = shouldUseJavaFx;
  }

  public StudyToolWindow.StudyToolWindowMode getToolWindowMode() {
    return myToolWindowMode;
  }

  public void setToolWindowMode(StudyToolWindow.StudyToolWindowMode toolWindowMode) {
    myToolWindowMode = toolWindowMode;
  }

  public boolean isTurnEditingMode() {
    return myTurnEditingMode;
  }

  public void setTurnEditingMode(boolean turnEditingMode) {
    myTurnEditingMode = turnEditingMode;
  }

}

@@ -6064,32 +6064,144 @@
 .MAIN_ELEMENT);%0A
+    if (taskManagerElement == null)%0A      throw new StudySerializationUtils.StudyUnrecognizedFormatException();%0A
     XmlSerialize

remove warning from deserialize