/**
  *
  *                      OOAS Compiler
  *
  *       Copyright 2015, AIT Austrian Institute of Technology.
  * This code is based on the C# Version of the OOAS Compiler, which is
  * copyright 2015 by the Institute of Software Technology, Graz University
  * of Technology with portions copyright by the AIT Austrian Institute of
  * Technology. All rights reserved.
  *
  * SEE THE "LICENSE" FILE FOR THE TERMS UNDER WHICH THIS FILE IS PROVIDED.
  *
  * If you modify the file please update the list of contributors below to in-
  * clude your name. Please also stick to the coding convention of using TABs
  * to do the basic (block-level) indentation and spaces for anything after
  * that. (Enable the display of special chars and it should be pretty obvious
  * what this means.) Also, remove all trailing whitespace.
  *
  * Contributors:
  *               Willibald Krenn (AIT)
  *               Stephan Zimmerer (AIT)
  *               Markus Demetz (AIT)
  *               Christoph Czurda (AIT)
  *
  */


package org.momut.ooas.visitors;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;

import org.momut.ooas.CompilerConfiguration.LabelCompression;
import org.momut.ooas.ast.identifiers.AttributeIdentifier;
import org.momut.ooas.ast.identifiers.Identifier;
import org.momut.ooas.ast.identifiers.IdentifierKind;
import org.momut.ooas.ast.identifiers.MethodIdentifier;
import org.momut.ooas.ast.identifiers.NamedActionIdentifier;
import org.momut.ooas.ast.types.FunctionType;
import org.momut.ooas.ast.types.OoActionSystemInstance;
import org.momut.ooas.ast.types.FunctionType.FunctionTypeEnum;
import org.momut.ooas.parser.ParserState;
import org.momut.ooas.utils.exceptions.ArgumentException;
import org.momut.ooas.utils.exceptions.NotImplementedException;

public class OoaIdentifierCompressVisitor extends OoaCompleteAstTraversalVisitor {

	public static final String s_methodPrefix    = "m_";
	public static final String s_actionPrefix    = "a_";
	public static final String s_attributePrefix = "at_";
	public static final String s_objectPrefix    = "o_";
	public static final String s_typePrefix      = "t_";

	private final EnumMap<IdentifierKind, TreeMap<String, String> > m_identifierMappings = new EnumMap<>(IdentifierKind.class);
	private final TreeMap<String, String> m_objectMappings = new TreeMap<>();
	private final LabelCompression m_compression;

	private void putMapping(String label, Identifier identifier) {
		TreeMap<String, String> map = m_identifierMappings.get(identifier.kind());
		if (map == null) {
			map = new TreeMap<>();
			m_identifierMappings.put(identifier.kind(), map);
		}
		map.put(label, identifier.tokenText());
	}

	public String mappingFor(Identifier identifier)	{
		final IdentifierKind kind = identifier.kind();
		final TreeMap<String, String> map = m_identifierMappings.get(kind);
		return map.get(identifier.tokenText());
	}

	@Override
	public /*override*/ void visit(AttributeIdentifier attributeIdentifier)	{
		if (m_compression != LabelCompression.All)
			return;
		final String attributeName = labelFor(attributeIdentifier);
		attributeIdentifier.SetTokenText(attributeName);
	}

	@Override
	public /*override*/ void visit(MethodIdentifier methodIdentifier)	{
		final String renamedName = labelFor(methodIdentifier);
		methodIdentifier.SetTokenText(renamedName);
	}

	@Override
	public /*override*/ void visit(NamedActionIdentifier namedActionIdentifier)	{
		final FunctionType functionType = (FunctionType) namedActionIdentifier.type();
		if (functionType.functionType() == FunctionTypeEnum.Internal) {
				final String renamedName = labelFor(namedActionIdentifier);
				namedActionIdentifier.SetTokenText(renamedName);
		}
	}

	public String labelFor(Identifier identifier) {
		String label = identifier.tokenText();

		switch(identifier.kind()) {
			case MethodIdentifier:
				label = s_methodPrefix + convert(methodCount++);
				break;
			case NamedActionIdentifier:
				label = s_actionPrefix + convert(actionCount++);
				break;
			case AttributeIdentifier:
				label = s_attributePrefix + convert(attributeCount++);
				break;
			default:
				throw new NotImplementedException();
		}
		putMapping(label, identifier);
		return label;
	}

	public String labelFor(OoActionSystemInstance instance) {
		final String label = s_objectPrefix + convert(objectCount++);
		m_objectMappings.put(label, instance.Name);
		return label;
	}

	public String originalName(String key) {
		if(m_objectMappings.get(key) != null) {
			return m_objectMappings.get(key);
		}
		return key;
	}

	public List<String> mappingInformation() {
		final List<String> lines = new ArrayList<>();
		final Set<IdentifierKind> kinds = m_identifierMappings.keySet();

		for (final IdentifierKind kind: kinds) {
			final TreeMap<String, String> map = m_identifierMappings.get(kind);
			final Set<String> keys = map.keySet();
			lines.add(String.format("[ Mappings for '%s' ]", kind.name()));
			for(final String key: keys) {
				lines.add(String.format("'%s' => '%s'", key, map.get(key)));
			}
			lines.add("");
		}

		if (m_objectMappings.isEmpty())
			return lines;

		lines.add(String.format("[ Object mappings ]"));
		final Set<String> keys = m_objectMappings.keySet();
		for (final String key: keys) {
			lines.add(String.format("'%s' => '%s'", key, originalName(key)));
		}
		lines.add("");
		return lines;
	}

	public LabelCompression compression() {
		return m_compression;
	}

	static final String CHARS = "abcdefghijklmnopqrstuvwxyz"; /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
	static final int CHARS_LENGTH = CHARS.length();

	int actionCount = 0;
	int methodCount = 0;
	int attributeCount = 0;
	int objectCount = 0;

	private String convert(int id) {
		final StringBuilder sb = new StringBuilder();
		do {
			sb.append(CHARS.charAt(id % CHARS_LENGTH));
			id = id / CHARS_LENGTH;
		} while(id != 0);
		return sb.toString();
	}

	public OoaIdentifierCompressVisitor(ParserState aState, LabelCompression compression)
	{
		super (aState);
		if (aState == null || compression == null)
			throw new ArgumentException();
		m_compression = compression;
	}
}
