/**
 * Copyright (C) 2009 STMicroelectronics
 *
 * This file is part of "Mind Compiler" is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public License 
 * as published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact: mind@ow2.org
 *
 * Authors: Matthieu Leclercq
 * Contributors: Julien Tous
 */

package org.ow2.mind.adl;

import static org.ow2.mind.PathHelper.fullyQualifiedNameToPath;
import static org.ow2.mind.PathHelper.replaceExtension;

import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.adl.CompilerError;
import org.objectweb.fractal.adl.error.GenericErrors;
import org.ow2.mind.adl.compilation.CompilationCommandFactory;
import org.ow2.mind.adl.graph.ComponentGraph;
import org.ow2.mind.adl.implementation.ImplementationLocator;
import org.ow2.mind.adl.implementation.SharedImplementationDecorationHelper;
import org.ow2.mind.compilation.AssemblerCommand;
import org.ow2.mind.compilation.CompilationCommand;
import org.ow2.mind.compilation.CompilerCommand;
import org.ow2.mind.compilation.CompilerContextHelper;
import org.ow2.mind.compilation.LinkerCommand;
import org.ow2.mind.inject.InjectDelegate;
import org.ow2.mind.io.OutputFileLocator;

import com.google.inject.Inject;

public class BasicGraphLinker implements GraphCompiler {

  @InjectDelegate
  protected GraphCompiler             clientCompilerItf;

  @Inject
  protected OutputFileLocator         outputFileLocatorItf;

  @Inject
  protected ImplementationLocator     implementationLocatorItf;

  @Inject
  protected CompilationCommandFactory compilationCommandFactory;

  // ---------------------------------------------------------------------------
  // Implementation of the Visitor interface
  // ---------------------------------------------------------------------------

  public Collection<CompilationCommand> visit(final ComponentGraph graph,
      final Map<Object, Object> context) throws ADLException {
    final List<CompilationCommand> result = new ArrayList<CompilationCommand>();
    final Collection<CompilationCommand> compilationTasks = clientCompilerItf
        .visit(graph, context);

    compileSharedImplementation(graph, compilationTasks, context);

    String outputPath = CompilerContextHelper.getExecutableName(context);
    if (outputPath == null) {
      outputPath = fullyQualifiedNameToPath(graph.getDefinition().getName(),
          null);
    }
    final File outputFile = outputFileLocatorItf.getCExecutableOutputFile(
        outputPath, context);

    final LinkerCommand command = compilationCommandFactory.newLinkerCommand(
        graph, outputFile, context);

    for (final CompilationCommand compilationCommand : compilationTasks) {
      result.add(compilationCommand);
      if (compilationCommand instanceof CompilerCommand) {
        command.addInputFiles(((CompilerCommand) compilationCommand)
            .getOutputFile());
      }
      if (compilationCommand instanceof AssemblerCommand) {
        command.addInputFiles(((AssemblerCommand) compilationCommand)
            .getOutputFile());
      }
    }
    result.add(command);

    return result;
  }

  protected void compileSharedImplementation(final ComponentGraph graph,
      final Collection<CompilationCommand> result,
      final Map<Object, Object> context) throws ADLException {
    final Set<String> sharedImplementations = new HashSet<String>();

    findSharedImplementations(graph, sharedImplementations);

    for (final String sharedImplementation : sharedImplementations) {
      File sharedImpl;
      final URL input = implementationLocatorItf.findSource(
          sharedImplementation, context);
      if (input == null) {
        // try to find shared implementation using output locator
        sharedImpl = outputFileLocatorItf.getCSourceOutputFile(
            sharedImplementation, context);
        if (sharedImpl == null) {
          throw new CompilerError(GenericErrors.INTERNAL_ERROR,
              "Can't find file " + sharedImplementation);
        }
      } else {
        try {
          sharedImpl = new File(input.toURI());
        } catch (final URISyntaxException e) {
          throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
              "Can't find file " + sharedImplementation);
        }
      }

      final File outFile = outputFileLocatorItf.getCCompiledOutputFile(
          replaceExtension(sharedImplementation, ".o"), context);
      final File depFile = outputFileLocatorItf.getCCompiledOutputFile(
          replaceExtension(sharedImplementation, ".d"), context);
      final CompilerCommand command = compilationCommandFactory
          .newCompilerCommand(null, null, sharedImpl, false, null, depFile,
              outFile, context);

      result.add(command);
    }
  }

  protected void findSharedImplementations(final ComponentGraph graph,
      final Set<String> sharedImplementations) {
    sharedImplementations.addAll(SharedImplementationDecorationHelper
        .getSharedImplementation(graph.getDefinition()));

    for (final ComponentGraph subComp : graph.getSubComponents()) {
      findSharedImplementations(subComp, sharedImplementations);
    }
  }
}
