// This file is part of Babel. For more information, see
// http://www.llnl.gov/CASC/components/. Please read the COPYRIGHT file
// for Our Notice and the LICENSE file for the GNU Lesser General Public
// License.
// 
// This program 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) version 2.1 dated February 1999.
// 
// 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 terms and
// conditions of the GNU Lesser General Public License for more details.
// 
// You should have recieved a copy of the GNU Lesser General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package gov.llnl.babel.backend;

import gov.llnl.babel.Context;
import gov.llnl.babel.backend.FileListener;
import gov.llnl.babel.symbols.Metadata;
import gov.llnl.babel.symbols.Symbol;
import gov.llnl.babel.symbols.SymbolID;
import gov.llnl.babel.symbols.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
Make depends info calculator for all languages.
We have a nested map structure:
d_dirMap contains elements indexed by dirname
and with hashmaps as values. The hashmap values
are themselves hashmaps keyed by filename with
a value the vector of filenames of prerequisites.

Note on terminology: this is about makefile dependency rules of the form
output : input
where input and output may be lists and may be path qualified or not.

It turns out that without a more restrictive definition of the C/C++/fortran
code generation, we cannot determine the directory in which the included
headers will exist because, for example, the IOR headers may be kept
canonically elsewhere and generated by an alternate invocation of babel.

Similarly, headers from other sidl packages not included in the same
generation step be be anywhere in the filesystem. Thus, the best dependency
we can generate is by filenames only on the rhs of the : in a make rule and
the rest of the work must be provided by VPATH in gnu make or equivalent.

On the whole, the difficulties stem from a deep inconsistency in how various
languages are handled. E.g. xml files can always determine the full path to
other xml files at babel invocation time. In python and java there is some
package structure, but not enough to guarantee anything.  In C, etc, the
user can pretty much do anything they want.

 */
public class Dependencies implements FileListener
{

  boolean debug = false;

  private HashMap d_dirMap = new HashMap();
  private ArrayList d_currentDepList = null;
  private Context d_context;

  public Dependencies(Context context) {
    d_context = context;
  }

  public void setOutputDir(String outdir) {
    if (outdir == null) {
      outdir = "";
    }
    addDir(outdir);
  }

  /** return (possibly with the side effect of creating) the
      fileMap for a given directory name.
   */
  private HashMap addDir(String dirName) {
    if (! d_dirMap.containsKey(dirName) ) {
      HashMap fileMap = new HashMap();
      d_dirMap.put(dirName, fileMap);
      return fileMap;
    }
    return (HashMap)d_dirMap.get(dirName);
  }

  public void addTargetFile(String dirName, String fileName) {
    HashMap fileMap = addDir(dirName);
    ArrayList depList;
    if (! fileMap.containsKey(fileName) ) {
      depList = new ArrayList(10);
      fileMap.put(fileName, depList);
    } else {
      // This is not ever expected to happen, but brokenness
      // in ucxx or ior generation might provoke it.
      depList = (ArrayList)fileMap.get(fileName);
    }
    // so we aren't always looking it up.
    d_currentDepList = depList;
  }

  public void addInput(String input) {
    if (d_currentDepList == null) {
      if (debug) {
        System.err.println("Babel: Dependencies.addInput called unexpectedly!");
      }
    }
    else {
      d_currentDepList.add(input);
    }
  }

  public HashMap getDirectoryData(String dirName)
  {
    return (HashMap)d_dirMap.get(dirName);
  }
  
  public void dumpData() {
    if (debug) {
      if (d_dirMap != null) {
        Set entryset= d_dirMap.entrySet();
        Iterator iter = entryset.iterator();
        while (iter.hasNext()) {
          Map.Entry ent = (Map.Entry)iter.next();
          String lhsDir = (String)ent.getKey();
          HashMap fileMap = (HashMap)ent.getValue();
          Set entryset2= fileMap.entrySet();
          Iterator iter2 = entryset2.iterator();
          while (iter2.hasNext()) {
            Map.Entry ent2 = (Map.Entry)iter2.next();
            String lhsFile = (String) ent2.getKey();
            ArrayList v = (ArrayList) ent2.getValue();
            System.out.println("LHS = " + lhsDir + lhsFile);
            for (int i = 0; i < v.size(); i++) {
              String inputFile = (String)v.get(i);
              System.out.println("RHS (" + i +")" + " = " + inputFile);
            }
          }
        }
        System.out.println();
      }
    }
  }  

  /** 
   * Method for recording makefile dependencies. Do not 
   * use unless you know exactly what you are doing.
   */
  public void recordDependency(SymbolID id) {
    if (debug)
      System.out.println("BRN: symbol name: " + id.getSymbolName());
    Symbol symbol = d_context.getSymbolTable().lookupSymbol(id);
    recordDependency(symbol);
  }

  public void recordDependency(Symbol symbol) {
    if (symbol.getUserSpecified()) {
      Metadata metadata = symbol.getMetadata();
      String sourceUrl = metadata.getMetadataValue("xml-url");
      if ( sourceUrl == null ) {
        sourceUrl = metadata.getMetadataValue("source-url");
      }
      if ( sourceUrl != null ) { 
        int idx = sourceUrl.indexOf('/');
        if(idx >= 0) {
          sourceUrl = sourceUrl.substring(idx);
          addInput(sourceUrl);
        } 
      }
    } // end of dependency recording
  }

  public void newFile(SymbolID id, 
                      int    type, 
                      String role, 
                      String dir, 
                      String name){
    if (debug) {
      System.out.println("New File in dir= "+ dir + ": " +name+ "  Role: "+ role);
    }
    if(role.equals("PYTHONADMIN"))
        return;
        
    if (type == Type.ENUM || type == Type.CLASS || type == Type.INTERFACE || type == Type.PACKAGE){
        addTargetFile(dir, name);
        recordDependency(id);
    }
  }
}
