// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.config;

import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.JDOMExternalizable;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import java.util.Iterator;

public interface Externalizer<T> {
  @NonNls String VALUE_ATTRIBUTE = "value";
  Externalizer<String> STRING = new BaseExternalizer<>() {
    @Override
    public String readValue(Element dataElement) {
      return dataElement.getAttributeValue(VALUE_ATTRIBUTE);
    }
  };
  Externalizer<Integer> INTEGER = new BaseExternalizer<>() {
    @Override
    public Integer readValue(Element dataElement) {
      try {
        return Integer.valueOf(dataElement.getAttributeValue(VALUE_ATTRIBUTE));
      }
      catch (NumberFormatException e) {
        return null;
      }
    }
  };
  Externalizer<Storage> STORAGE = new StorageExternalizer();

  abstract class BaseExternalizer<T> implements Externalizer<T> {
    @Override
    public void writeValue(Element dataElement, T value) {
      dataElement.setAttribute(VALUE_ATTRIBUTE, value.toString());
    }
  }
  Externalizer<Boolean> BOOLEAN = new BaseExternalizer<>() {
    @Override
    public Boolean readValue(Element dataElement) {
      return Boolean.valueOf(dataElement.getAttributeValue(VALUE_ATTRIBUTE));
    }
  };

  T readValue(Element dataElement);

  void writeValue(Element dataElement, T value);

  interface SkippableValue {
  }

  final class FactoryBased<T extends JDOMExternalizable> implements Externalizer<T> {
    private final Factory<? extends T> myFactory;

    public FactoryBased(@NotNull Factory<? extends T> factory) {
      myFactory = factory;
    }

    @Override
    public T readValue(Element dataElement) {
      T data = myFactory.create();
      data.readExternal(dataElement);
      return data;
    }

    @Override
    public void writeValue(Element dataElement, T value) {
      value.writeExternal(dataElement);
    }
  }

  final class StorageExternalizer implements Externalizer<Storage> {
    private static final @NonNls String ITEM_TAG = "item";
    private static final @NonNls String KEY_ATTR = "key";
    private static final @NonNls String VALUE_ATTR = "value";

    @Override
    public Storage readValue(Element dataElement) {
      Storage.MapStorage storage = new Storage.MapStorage();
      for (Element element : dataElement.getChildren(ITEM_TAG)) {
        storage.put(element.getAttributeValue(KEY_ATTR), element.getAttributeValue(VALUE_ATTR));
      }
      return storage;
    }

    @Override
    public void writeValue(Element dataElement, Storage storage) {
      Iterator<String> keys = ((Storage.MapStorage)storage).getKeys();
      while (keys.hasNext()) {
        String key = keys.next();
        String value = storage.get(key);
        Element element = new Element(ITEM_TAG);
        element.setAttribute(KEY_ATTR, key);
        if (value != null) {
          element.setAttribute(VALUE_ATTR, value);
        }
        dataElement.addContent(element);
      }
    }
  }
}
