//==================================================================================================
// J a v a                                                                                     Java
// R e f l e c t i o n
// M a k e f i l e
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library 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 Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides a class to generate the makefile of a Jirk++ hierarchy. */

// Package //---------------------------------------------------------------------------------------
package bpp.java.reflection;

// Importation //-----------------------------------------------------------------------------------
import bpp.standard.*;
import java.io.*;
import java.util.*;

@SuppressWarnings("unchecked")

// M a k e f i l e  Class //------------------------------------------------------------------------
/*CLASS Makefile */ /* Contains a method to generate the makefile of a Jirk++ hierarchy. */
public class Makefile {
 //-----------------------------------------------------------------------------------------Generate
 /*METHOD Makefile */ /* Generates the makefile of a Jirk++ hierarchy. */
 public static
 void generate(OutputTextStream agStream,String agSourcePath,String agObjectPath,
               Vector agStandardS,String agSeparator,boolean agShortCommand) throws Exception {
  Vector  lcContentS    = new Vector();
  TreeMap lcDependencyX = new TreeMap();
  Vector  lcModuleS     = new Vector();

  agStream.writeLine("# Part automatically generated by");
  agStream.writeLine("# B u i l d   J i r k");
  agStream.writeLine("# "+bpp.JavaInformation.copyright());
  agStream.writeLine("# Bruno Bachelet");
  agStream.writeLine("# bruno@nawouak.net");
  agStream.writeLine("# http://www.nawouak.net");

  buildModules(lcModuleS,lcContentS,agStandardS,agSourcePath+"/jirk",
               (agObjectPath.equals("") ? "" : agObjectPath+"/jirk"),agSeparator);

  buildFileDependency(lcDependencyX,lcModuleS,lcContentS,agSourcePath+"/jirk",agSeparator);
  writeObjectRules(agStream,lcDependencyX,agSeparator);
  writeStaticLibraryRules(agStream,lcModuleS,lcContentS,agSeparator,agShortCommand);
  writeDynamicLibraryRules(agStream,lcModuleS,lcContentS,agSeparator);

  agStream.writeLine();
  agStream.writeLine("# End of the part generated by");
  agStream.writeLine("# B u i l d   J i r k");
  agStream.writeLine("# "+bpp.JavaInformation.copyright());
  agStream.writeLine("# Bruno Bachelet");
  agStream.writeLine("# bruno@nawouak.net");
  agStream.writeLine("# http://www.nawouak.net");
  agStream.writeLine();
 }
 //-------------------------------------------------------------------------------------BuildModules
 // Builds the modules of the hierarchy.
 protected static void buildModules(Vector agModuleS,Vector agContentS,Vector agStandardS,
                                    String agSourcePath,String agObjectPath,
                                    String agSeparator) throws Exception {
  Vector    lcBulkContent;
  Vector    lcContent;
  int       lcCounter1;
  int       lcCounter2;
  File      lcFile;
  FileName  lcFileName;
  String    lcName;
  Vector    lcStandardContent;

  String  lcFileS[] = (new File(agSourcePath)).list();

  // Initialization //
  if (lcFileS==null) throw new Exception("Java/Reflection - Can't access the path.");
  agModuleS.add(new String("<bulk>"));
  agModuleS.add(new String("<standard>"));
  lcBulkContent=new Vector();
  lcStandardContent=new Vector();
  agContentS.add(lcBulkContent);
  agContentS.add(lcStandardContent);

  // Modules Content //
  lcCounter1=0;

  while (lcCounter1<lcFileS.length) {
   lcFile=new File(agSourcePath+"/"+lcFileS[lcCounter1]);

   if (lcFile.isDirectory()) {
    agModuleS.add(lcFile.getName());
    lcContent=new Vector();
    agContentS.add(lcContent);
    getModuleContent(lcContent,lcFile,"",agObjectPath,agSeparator);
   }
   else {
    lcFileName=new FileName(lcFile.getName());
    if (lcFileName.extension().equals("hpp")) lcBulkContent.add(lcFileName.body());
   }

   ++lcCounter1;
  }

  // Standard Module Building //
  lcCounter1=agModuleS.size();

  while (lcCounter1>1) {
   --lcCounter1;
   lcName=(String)agModuleS.elementAt(lcCounter1);
   lcCounter2=0;

   while (lcCounter2<agStandardS.size()) {
    if (((String)agStandardS.elementAt(lcCounter2)).equals(lcName)) {
     lcStandardContent.addAll((Vector)agContentS.elementAt(lcCounter1));
     agContentS.removeElementAt(lcCounter1);
     agModuleS.removeElementAt(lcCounter1);
     lcCounter2=agStandardS.size();
    }
    else ++lcCounter2;
   }
  }
 }
 //---------------------------------------------------------------------------------GetModuleContent
 // Gets the content of a module.
 protected static void getModuleContent(Vector agContentS,File agFolder,String agPath,
                                        String agObjectPath,String agSeparator) {
  File     lcFile;
  FileName lcFileName;

  int    lcCounter = 0;
  String lcFileS[] = agFolder.list();
  String lcPath    = (agPath.equals("") ? "" : agPath+agSeparator)+agFolder.getName();

  if (!agObjectPath.equals("")) {
   lcFile=new File(agObjectPath+"/"+lcPath);
   lcFile.mkdirs();
  }

  while (lcCounter<lcFileS.length) {
   lcFile=new File(agFolder.getAbsolutePath()+"/"+lcFileS[lcCounter]);

   if (lcFile.isDirectory())
    getModuleContent(agContentS,lcFile,lcPath,agObjectPath,agSeparator);
   else {
    lcFileName=new FileName(lcFile.getName());
    if (lcFileName.extension().equals("hpp")) agContentS.add(lcPath+agSeparator+lcFileName.body());
   }

   ++lcCounter;
  }
 }
 //------------------------------------------------------------------------------BuildFileDependency
 // Builds the dependency of the source files.
 protected static void buildFileDependency(TreeMap agDependencyX,Vector agModuleS,
                                           Vector agContentS,String agPath,
                                           String agSeparator) throws Exception {
  Vector    lcContent;
  int       lcCounter2;
  TreeSet   lcDependencyS;
  Map.Entry lcEntry;
  Iterator  lcIterator;
  String    lcName;

  TreeSet lcCompletedS = new TreeSet();
  int     lcCounter1   = 0;

  while (lcCounter1<agModuleS.size()) {
   lcContent=(Vector)agContentS.elementAt(lcCounter1);
   lcCounter2=0;

   while (lcCounter2<lcContent.size()) {
    lcName=(String)lcContent.elementAt(lcCounter2);
    lcDependencyS=new TreeSet();
    getFileDependency(lcDependencyS,agPath+"/"+lcName+".hpp",agSeparator);
    agDependencyX.put(lcName+".hpp",lcDependencyS);
    lcDependencyS=new TreeSet();
    getFileDependency(lcDependencyS,agPath+"/"+lcName+".cpp",agSeparator);
    agDependencyX.put(lcName+".cpp",lcDependencyS);
    ++lcCounter2;
   }

   ++lcCounter1;
  }

  lcIterator=agDependencyX.entrySet().iterator();

  while (lcIterator.hasNext()) {
   lcEntry=(Map.Entry)lcIterator.next();

   if (!lcCompletedS.contains(lcEntry.getKey()))
    buildFullFileDependency((String)lcEntry.getKey(),agDependencyX,lcCompletedS,
                            (TreeSet)lcEntry.getValue());
  }
 }
 //--------------------------------------------------------------------------------GetFileDependency
 // Gets the direct dependency of a file.
 protected static void getFileDependency(TreeSet agDependencyS,String agName,
                                         String agSeparator) throws Exception {
  String lcString;

  InputTextFile lcFile = new InputTextFile(agName);

  while (!lcFile.isEnded()) {
   lcString=lcFile.readString();

   if (lcString.equals("#include")) {
    lcString=lcFile.readString();
    lcString=lcString.substring(1,lcString.length()-1);

    if (lcString.startsWith("jirk")) {
     lcString=lcString.substring(lcString.indexOf('/')+1,lcString.length());
     lcString=lcString.replace('/',agSeparator.charAt(0));
     agDependencyS.add(lcString);
    }
   }
  }
 }
 //--------------------------------------------------------------------------BuildFullFileDependency
 // Builds the full dependency of a file.
 protected static void buildFullFileDependency(String agName,TreeMap agDependencyX,
                                               TreeSet agCompletedS,
                                               TreeSet agList) throws Exception {
  String lcName;

  Iterator lcIterator = agList.iterator();
  TreeSet  lcList     = new TreeSet();

  while (lcIterator.hasNext()) {
   lcName=(String)lcIterator.next();

   if (!agDependencyX.containsKey(lcName))
    throw new Exception("Java/Reflection - At least a Jirk++ file is missing.");

   if (!agCompletedS.contains(lcName))
    buildFullFileDependency(lcName,agDependencyX,agCompletedS,(TreeSet)agDependencyX.get(lcName));

   lcList.addAll((TreeSet)agDependencyX.get(lcName));
  }

  agList.addAll(lcList);
  if (agList.contains(agName)) agList.remove(agName);
  agCompletedS.add(agName);
 }
 //---------------------------------------------------------------------------------WriteObjectRules
 // Writes the building rules of the binary objects into a stream.
 protected static void writeObjectRules(OutputTextStream agStream,TreeMap agDependencyX,
                                        String agSeparator) {
  Map.Entry lcEntry;
  FileName  lcFileName;
  int       lcIndex;
  Iterator  lcIterator2;
  String    lcName;

  Iterator lcIterator1 = agDependencyX.entrySet().iterator();

  while (lcIterator1.hasNext()) {
   lcEntry=(Map.Entry)lcIterator1.next();
   lcName=(String)lcEntry.getKey();
   lcFileName=new FileName(lcName);
   lcName=lcName.substring(0,lcName.length()-4);

   if (lcFileName.extension().equals("cpp")) {
    agStream.writeLine();
    agStream.writeLine("$(OBJECTS_PATH)"+agSeparator+"jirk"+agSeparator+lcName+".$(OBJ) : \\");
    agStream.write(" $(INCLUDE_PATH)"+agSeparator+"jirk"+agSeparator+lcName+".cpp");
    lcIterator2=((TreeSet)lcEntry.getValue()).iterator();

    while (lcIterator2.hasNext()) {
     agStream.writeLine(" \\");
     agStream.write(" $(INCLUDE_PATH)"+agSeparator+"jirk"+agSeparator+(String)lcIterator2.next());
    }

    agStream.writeLine();
    agStream.writeLine("\t@$(ECHO) jirk"+agSeparator+lcName+".cpp");
    agStream.write("\t@$(COMPILE_OBJ) $(INCLUDE_PATH)"+agSeparator+"jirk");
    agStream.writeLine(agSeparator+lcName+".cpp");
    lcIndex=lcName.lastIndexOf(agSeparator.charAt(0));
    lcIndex=(lcIndex<0 ? 0 : lcIndex+1);
    agStream.write("\t@$(MOVE) "+lcName.substring(lcIndex,lcName.length())+".$(OBJ)");
    agStream.writeLine(" $(OBJECTS_PATH)"+agSeparator+"jirk"+agSeparator+lcName+".$(OBJ)");
   }
  }
 }
 //--------------------------------------------------------------------------WriteStaticLibraryRules
 // Writes the building rules of the static libraries into a stream.
 protected static
 void writeStaticLibraryRules(OutputTextStream agStream,Vector agModuleS,
                              Vector agContentS,String agSeparator,boolean agShortCommand) {
  Vector lcContent;
  int    lcCounter2;
  String lcLibraryName;
  String lcName;
  String lcUpperName;

  int lcCounter1=0;

  while (lcCounter1<agModuleS.size()) {
   lcName=(String)agModuleS.elementAt(lcCounter1);
   lcContent=(Vector)agContentS.elementAt(lcCounter1);

   if (lcName.equals("<standard>")) lcName="jirk";
   else if (lcName.equals("<bulk>")) lcName="jirkbulk";
   else lcName="jirk_"+lcName;

   lcUpperName=lcName.toUpperCase();
   lcLibraryName="$(LIBRARY_PATH)"+agSeparator+"lib"+lcName+".$(LIB)";
   agStream.writeLine();
   agStream.writeLine(lcName+" : "+lcLibraryName);
   agStream.writeLine();
   agStream.writeLine(lcLibraryName+" : \\");
   agStream.write(" $("+lcUpperName+"_LIB_NEED)");
   lcCounter2=0;

   while (lcCounter2<lcContent.size()) {
    agStream.writeLine(" \\");
    agStream.write(" $(OBJECTS_PATH)"+agSeparator+"jirk"+agSeparator);
    agStream.write((String)lcContent.elementAt(lcCounter2)+".$(OBJ)");
    ++lcCounter2;
   }

   agStream.writeLine();
   agStream.writeLine("\t@$(ECHO) lib"+lcName+".$(LIB)");
   agStream.writeLine("\t@$(REMOVE) "+lcLibraryName);

   if (agShortCommand) {
    lcCounter2=0;

    while (lcCounter2<lcContent.size()) {
     lcName=(String)lcContent.elementAt(lcCounter2);
     agStream.write("\t@$(STATIC) $(LIB_NAME)"+lcLibraryName+" $(ADD_LIB)$(OBJECTS_PATH)");
     agStream.writeLine(agSeparator+"jirk"+agSeparator+lcName+".$(OBJ)");
     ++lcCounter2;
    }
   }
   else {
    agStream.write("\t@$(STATIC) $(LIB_NAME)"+lcLibraryName);
    lcCounter2=0;

    while (lcCounter2<lcContent.size()) {
     lcName=(String)lcContent.elementAt(lcCounter2);
     agStream.writeLine(" \\");

     agStream.write("\t $(ADD_LIB)$(OBJECTS_PATH)"+agSeparator
                    +"jirk"+agSeparator+lcName+".$(OBJ)");

     ++lcCounter2;
    }

    agStream.writeLine();
   }

   agStream.writeLine("\t@$(RANLIB) "+lcLibraryName);
   ++lcCounter1;
  }
 }
 //-------------------------------------------------------------------------WriteDynamicLibraryRules
 // Writes the building rules of the dynamic libraries into a stream.
 protected static void writeDynamicLibraryRules(OutputTextStream agStream,Vector agModuleS,
                                                Vector agContentS,String agSeparator) {
  Vector lcContent;
  int    lcCounter2;
  String lcLibraryName;
  String lcName;
  String lcUpperName;

  int lcCounter1 = 0;

  while (lcCounter1<agModuleS.size()) {
   lcName=(String)agModuleS.elementAt(lcCounter1);
   lcContent=(Vector)agContentS.elementAt(lcCounter1);

   if (lcName.equals("<standard>")) lcName="jirk";
   else if (lcName.equals("<bulk>")) lcName="jirkbulk";
   else lcName="jirk_"+lcName;

   lcUpperName=lcName.toUpperCase();
   lcLibraryName="$(LIBRARY_PATH)"+agSeparator+"$(PRE)"+lcName+".$(DLL)";
   agStream.writeLine();
   agStream.writeLine(lcName+"_dll : "+lcLibraryName);
   agStream.writeLine();
   agStream.writeLine(lcLibraryName+" : \\");
   agStream.write(" $("+lcUpperName+"_DLL_NEED)");
   lcCounter2=0;

   while (lcCounter2<lcContent.size()) {
    agStream.writeLine(" \\");
    agStream.write(" $(OBJECTS_PATH)"+agSeparator+"jirk"+agSeparator);
    agStream.write((String)lcContent.elementAt(lcCounter2)+".$(OBJ)");
    ++lcCounter2;
   }

   agStream.writeLine();
   agStream.writeLine("\t@$(ECHO) $(PRE)"+lcName+".$(DLL)");
   agStream.writeLine("\t@$(REMOVE) "+lcLibraryName);
   agStream.write("\t@$(DYNAMIC) $(DLL_NAME)"+lcLibraryName+" $("+lcUpperName+"_DEPENDENCY)");
   lcCounter2=0;

   while (lcCounter2<lcContent.size()) {
    lcName=(String)lcContent.elementAt(lcCounter2);
    agStream.writeLine(" \\");
    agStream.write("\t $(OBJECTS_PATH)"+agSeparator+"jirk"+agSeparator+lcName+".$(OBJ)");
    ++lcCounter2;
   }

   agStream.writeLine();
   ++lcCounter1;
  }
 }
}

// End //-------------------------------------------------------------------------------------------