/*
 * Copyright 2003-2015 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.nodefs;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SRepository;

/**
 * Provide nice reference presentation for nodes/models/modules using names, be careful - can be not persistent in case of node/model/module changes.
 * Format: {module_name}/{model_name}/{node_presentation}. In case of names ambiguity format becomes name~{id} like DOS fs.
 *
 * FIXME works with root nodes only, references to children nodes would never get de-serialized.
 */
/*package*/ class NiceReferenceSerializer {
  private static final String SEP = "/";
  private final SRepository myRepository;

  public NiceReferenceSerializer(SRepository repository) {
    myRepository = repository;
  }

  // module
  public String serializeModule(SModule module) {
    return serialize(module, myRepository.getModules(), MODULE_TARGET);
  }

  @Nullable
  public SModule deserializeModule(String s) {
    return deserialize(s, myRepository.getModules(), MODULE_TARGET);
  }

  // model
  public String serializeModel(SModel model) {
    return serializeModule(model.getModule()) + SEP + serialize(model, model.getModule().getModels(), MODEL_TARGET);
  }

  @Nullable
  public SModel deserializeModel(String s) {
    if (!s.contains("/")) {
      return null;
    }
    SModule module = deserializeModule(s.substring(0, s.lastIndexOf(SEP)));
    if (module == null) {
      return null;
    }
    return deserialize(s.substring(s.lastIndexOf(SEP) + 1), module.getModels(), MODEL_TARGET);
  }

  // node
  public String serializeNode(SNode node) {
    return serializeModel(node.getModel()) + SEP + serialize(node, node.getModel().getRootNodes(), NODE_TARGET);
  }

  @Nullable
  public SNode deserializeNode(String s) {
    if (!s.contains(SEP)) {
      return null;
    }
    SModel model = deserializeModel(s.substring(0, s.lastIndexOf(SEP)));
    if (model == null) {
      return null;
    }
    return deserialize(s.substring(s.lastIndexOf(SEP) + 1), model.getRootNodes(), NODE_TARGET);
  }

  // common

  /**
   * @return serialization without SEP character
   */
  @NotNull
  private static <T> String serialize(T e, Iterable<T> contextElements, SerializerTarget<T> target) {
    // todo: implement name~2 format
    return target.getName(e);
  }

  @Nullable
  private static <T> T deserialize(@NotNull String s, Iterable<T> contextElements, SerializerTarget<T> target) {
    // todo: implement name~2 format
    for (T e : contextElements) {
      if (s.equals(target.getName(e))) {
        return e;
      }
    }
    return null;
  }

  // serialization targets
  private static final SerializerTarget<SNode> NODE_TARGET = new SerializerTarget<SNode>() {
    @Override
    public String getName(SNode e) {
      return String.valueOf(e.getPresentation());
    }
  };
  private static final SerializerTarget<SModel> MODEL_TARGET = new SerializerTarget<SModel>() {
    @Override
    public String getName(SModel e) {
      return String.valueOf(e.getModelName());
    }
  };
  private static final SerializerTarget<SModule> MODULE_TARGET = new SerializerTarget<SModule>() {
    @Override
    public String getName(SModule e) {
      return String.valueOf(e.getModuleName());
    }
  };

  private interface SerializerTarget<T> {
    @NotNull
    String getName(T e);
  }
}
