/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.mps.build;

import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.BuildNumber;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.io.ZipUtil;
import jetbrains.mps.InternalFlag;
import jetbrains.mps.RuntimeFlags;
import jetbrains.mps.build.SamplesExtractor.MyState;
import jetbrains.mps.samples.SamplesInfo;
import jetbrains.mps.util.PathManager;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;

@State(
    name = "LastBuildNumber",
    storages = {
        @Storage(value = "sampleProjects.xml", roamingType = RoamingType.DISABLED),
        @Storage(value = "other.xml", deprecated = true)
    }
)
public class SamplesExtractor implements ApplicationComponent, PersistentStateComponent<MyState>, SamplesInfo {
  private static final Logger LOG = LogManager.getLogger(SamplesExtractor.class);

  private static final String SAMPLES_IN_MPS_HOME_DIR = "samples";

  public static SamplesExtractor getInstance() {
    return ((SamplesExtractor) ApplicationManager.getApplication().getComponent(SamplesInfo.class));
  }

  private MyState myState;
  private final ApplicationInfo myApplicationInfo;
  private boolean myIsSamplesInMPSHome;

  public SamplesExtractor(ApplicationInfo applicationInfo) {
    myApplicationInfo = applicationInfo;
  }

  @NotNull
  public String getComponentName() {
    return "Samples Extractor";
  }

  public void initComponent() {
    if (myState == null) {
      myState = new MyState();
    }
    updateSamplesLocation();

    if (RuntimeFlags.isTestMode()) {
      return;
    }

    checkSamplesAndUpdateIfNeeded();
  }

  private void checkSamplesAndUpdateIfNeeded() {
    int currentBuildNumber = currentBuildNumberString();
    if (myState.myLastBuildNumber < currentBuildNumber) {

      if (!myIsSamplesInMPSHome) {
        extractSamples();
      }
    }
  }

  private int currentBuildNumberString() {
    BuildNumber buildNumber = myApplicationInfo.getBuild();
    String currentBuildNumberString = buildNumber.asString();
    if (!currentBuildNumberString.matches("MPS[-\\.\\d]*.*") || InternalFlag.isInternalMode()) {
      // "Normal" build number starts with MPS, then goes some actual build number with numbers and dots and dashes, then goes some suffix like M1.
      // If build number looks like "11.snapshot", we consider it developers build and do not to extract samples.
      return MyState.DEFAULT;
    }
    return buildNumber.getBuildNumber();
  }

  private void updateSamplesLocation() {
    File samplesDirectory = new File(getSamplesPathInMPSHome());
    myIsSamplesInMPSHome = samplesDirectory.exists() && samplesDirectory.isDirectory();
  }

  public void disposeComponent() {
  }

  public MyState getState() {
    return myState;
  }

  public void loadState(MyState state) {
    myState = state;
  }

  public String getSamplesPath() {
    return myIsSamplesInMPSHome ? getSamplesPathInMPSHome() : getSamplesPathInUserHome();
  }

  private String getSamplesPathInUserHome() {
    return System.getProperty("user.home") + File.separator + SAMPLES_IN_USER_HOME_DIR + "." + getSuffix();
  }

  private String getSuffix() {
    String majorVersion = myApplicationInfo.getMajorVersion();
    assert majorVersion != null;
    String minorVersion = myApplicationInfo.getMinorVersion();
    assert minorVersion != null;
    return majorVersion + "." + minorVersion;
  }

  private String getSamplesPathInMPSHome() {
    return PathManager.getHomePath() + File.separator + SAMPLES_IN_MPS_HOME_DIR;
  }

  public void extractSamples() {
    final File samplesZipFile = new File(PathManager.getHomePath() + File.separator + SAMPLES_IN_MPS_HOME_ZIP);
    if (samplesZipFile.exists()) {
      final File samplesDir = new File(getSamplesPathInUserHome());

      if (samplesDir.exists()) {
        ApplicationManager.getApplication().invokeLater(() -> {
          int answer = Messages.showYesNoDialog(
              "Do you want to replace directory\n" + samplesDir + "\n with version " + myApplicationInfo.getBuild().asString() +
                  " (old directory contents will be deleted)?", "Replace MPS Samples?", Messages.getQuestionIcon());
          if (answer == Messages.YES) {
            FileUtil.delete(samplesDir);
            actuallyExtractSamples(samplesZipFile);
          }
        });
      } else {
        actuallyExtractSamples(samplesZipFile);
      }
    }

    myState.myLastBuildNumber = currentBuildNumberString();
  }

  private void actuallyExtractSamples(File samplesZipFile) {
    try {
      File tmpDir = FileUtil.createTempDirectory("MPSSamples", "");
      ZipUtil.extract(samplesZipFile, tmpDir, null);
      File from = new File(tmpDir + File.separator + SAMPLES_IN_USER_HOME_DIR);
      File to = new File(getSamplesPathInUserHome());
      if (!FileUtil.moveDirWithContent(from, to) && !to.exists()) {
        FileUtil.copyDir(from, to);
      }
      FileUtil.delete(tmpDir);
    } catch (IOException e) {
      LOG.error(null, e);
    }
  }

  public static class MyState {
    public int myLastBuildNumber = DEFAULT;
    private static final int DEFAULT = -1;
  }
}
