/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile$
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef cmOrderLinkDirectories_h
#define cmOrderLinkDirectories_h

#include <cmStandardIncludes.h>
#include <map>
#include <vector>
#include "cmTarget.h"
#include "cmsys/RegularExpression.hxx"


/** \class cmOrderLinkDirectories
 * \brief Compute the best -L path order
 *
 * This class computes the best order for -L paths.
 * It tries to make sure full path specified libraries are 
 * used.  For example if you have /usr/mylib/libfoo.a on as
 * a link library for a target, and you also have /usr/lib/libbar.a
 * and you also have /usr/lib/libfoo.a, then you would
 * want -L/usr/mylib -L/usr/lib to make sure the correct libfoo.a is 
 * found by the linker.  The algorithm is as follows:
 * - foreach library create a vector of directories it exists in.
 * - foreach directory create a vector of directories that must come
 *   after it, put this in a map<dir, vector<dir>> mapping from a directory
 *   to the vector of directories that it must be before.
 * - put all directories into a vector
 * - sort the vector with a compare function CanBeBefore
 *   CanBeBefore returns true if a directory is OK to be before
 *   another directory.  This is determined by looking at the 
 *   map<dir vector<dir>> and seeing if d1 is in the vector for d2.
 */
class cmOrderLinkDirectories
{
public:
  cmOrderLinkDirectories();
  ///! set link information from the target
  void SetLinkInformation(const char* targetName,
                          const std::vector<std::string>& linkLibraries,
                          const std::vector<std::string>& linkDirectories,
                          const cmTargetManifest& manifest,
                          const char* configSubdir);
  ///! Compute the best order for -L paths from GetLinkLibraries
  bool DetermineLibraryPathOrder();
  ///! Get the results from DetermineLibraryPathOrder
  void GetLinkerInformation(std::vector<cmStdString>& searchPaths,
                            std::vector<cmStdString>& linkItems)
  {
    linkItems = this->LinkItems;
    searchPaths = this->SortedSearchPaths;
  }
  // should be set from CMAKE_STATIC_LIBRARY_SUFFIX,
  // CMAKE_SHARED_LIBRARY_SUFFIX
  // CMAKE_LINK_LIBRARY_SUFFIX
  enum LinkType { LinkUnknown, LinkStatic, LinkShared };
  void AddLinkExtension(const char* e, LinkType type = LinkUnknown)
    {
    if(e && *e)
      {
      if(type == LinkStatic)
        {
        this->StaticLinkExtensions.push_back(e);
        }
      if(type == LinkShared)
        {
        this->SharedLinkExtensions.push_back(e);
        }
      this->LinkExtensions.push_back(e);
      }
    }
  // should be set from CMAKE_STATIC_LIBRARY_PREFIX
  void AddLinkPrefix(const char* s)
    {
    if(s)
      {
      this->LinkPrefixes.insert(s);
      }
    }
  // Return any warnings if the exist
  std::string GetWarnings();
  // return a list of all full path libraries
  void GetFullPathLibraries(std::vector<cmStdString>& libs);

  // Provide flags for switching library link type.
  void SetLinkTypeInformation(LinkType start_link_type,
                              const char* static_link_type_flag,
                              const char* shared_link_type_flag);

  // structure to hold a full path library link item
  struct Library
  {
    cmStdString FullPath;
    cmStdString File;
    cmStdString Path;
  };
  friend struct cmOrderLinkDirectoriesCompare;
  void DebugOn() 
    {
      this->Debug = true;
    }
  
private:
  void CreateRegularExpressions();
  std::string CreateExtensionRegex(std::vector<cmStdString> const& exts);
  void DetermineLibraryPathOrder(std::vector<cmStdString>& searchPaths,
                                 std::vector<cmStdString>& libs,
                                 std::vector<cmStdString>& sortedPaths);
  void PrepareLinkTargets();
  bool LibraryInDirectory(const char* desiredLib,
                          const char* dir, const char* lib);
  void FindLibrariesInSearchPaths();
  void FindIndividualLibraryOrders();
  void PrintMap(const char* name,
                std::map<cmStdString, std::vector<cmStdString> >& m);
  void PrintVector(const char* name,
                   std::vector<std::pair<cmStdString, 
                   std::vector<cmStdString> > >& m);
  void OrderPaths(std::vector<cmStdString>& paths);
  bool FindPathNotInDirectoryToAfterList(cmStdString& path);
  std::string NoCaseExpression(const char* str);
  bool LibraryMayConflict(const char* desiredLib,
                          const char* dir, const char* fname);
private:
  // set of files that will exist when the build occurs
  std::set<cmStdString> ManifestFiles;
  // map from library to directories that it is in other than its full path
  std::map<cmStdString, std::vector<cmStdString> > LibraryToDirectories;
  // map from directory to vector of directories that must be after it
  std::vector<std::pair<cmStdString, std::vector<cmStdString> > > 
  DirectoryToAfterList;
  std::set<cmStdString> DirectoryToAfterListEmitted;
  // map from full path to a Library struct
  std::map<cmStdString, Library> FullPathLibraries;
  // libraries that are found in multiple directories
  std::vector<Library> MultiDirectoryLibraries;
  // libraries that are only found in one directory
  std::vector<Library> SingleDirectoryLibraries;
  // This is a vector of all the link objects -lm or m
  std::vector<cmStdString> LinkItems;
  // Unprocessed link items
  std::vector<cmStdString> RawLinkItems;
  // This vector holds the sorted -L paths
  std::vector<cmStdString> SortedSearchPaths;
  // This vector holds the -F paths
  std::set<cmStdString> EmittedFrameworkPaths;
  // This is the set of -L paths unsorted, but unique
  std::set<cmStdString> LinkPathSet;
  // the names of link extensions
  std::vector<cmStdString> StaticLinkExtensions;
  std::vector<cmStdString> SharedLinkExtensions;
  std::vector<cmStdString> LinkExtensions;
  // the names of link prefixes
  std::set<cmStdString> LinkPrefixes;
  // set of directories that can not be put in the correct order
  std::set<cmStdString> ImpossibleDirectories;
  // Name of target
  cmStdString TargetName;
  // Subdirectory used for this configuration if any.
  cmStdString ConfigSubdir;

  // Link type adjustment.
  LinkType StartLinkType;
  LinkType CurrentLinkType;
  cmStdString StaticLinkTypeFlag;
  cmStdString SharedLinkTypeFlag;
  bool LinkTypeEnabled;
  void SetCurrentLinkType(LinkType lt);

  // library regular expressions
  cmsys::RegularExpression RemoveLibraryExtension;
  cmsys::RegularExpression ExtractStaticLibraryName;
  cmsys::RegularExpression ExtractSharedLibraryName;
  cmsys::RegularExpression ExtractAnyLibraryName;
  cmsys::RegularExpression SplitFramework;
  bool Debug;
};

#endif
