# This file is part of the graph-includes package
#
# (c) 2005 Yann Dirson <ydirson@altern.org>
# Distributed under version 2 of the GNU GPL.

package graphincludes::project;
use strict;
use warnings;

use graphincludes::params;
our @ISA;

use graphincludes::graph;

use File::Spec::Functions qw(catfile catpath splitdir splitpath);
use Hash::Util qw(lock_keys);

# set_language: class method that sets the language to be used when extracting deps.
# This is a hack, which does not allow to mix several languages in a single project.
# It is only a temporary measure that allows support for languages other than C/C++.
our $_language;
sub set_language {
  my $class = shift;
  $_language = shift;
  my $langmodule = "graphincludes::extractor::" . $_language;
  eval "require $langmodule" or die "cannot load $langmodule from " . join ':', @INC;
  push @ISA, $langmodule;
}

sub new {
  my $class = shift;
  my %args = @_;
  my $prefixstrip = $args{prefixstrip};
  my @files = @{$args{files}};	# take a copy of @ARGV
  my $self = {};

#   if (defined $_language) {
#     $self = ("graphincludes::extractor::" . $_language)->new;
#   }

  $self->{GRAPHS} = [ new graphincludes::graph ];	# stack of graphs for the different levels
  $self->{GRAPHS}[0]->set_nodes_from_names(\@files);

  $self->{PFXSTRIP} = $prefixstrip;
  $self->{SPECIALEDGES} = {};
  $self->{IGNOREDEDGES} = {};	# to be computed in getdeps
  $self->{REPORT} = { HDR => {},
		      SYS => {},
		    };

  bless ($self, $class);
  lock_keys (%$self);

  return $self;
}

sub init {
  my $self = shift;

  $self->getdeps($self->{GRAPHS}[0]);

  $self->{GRAPHS}[1] = new graphincludes::graph;
  $self->{GRAPHS}[1]->set_nodes ($self->_level1nodes($self->{GRAPHS}[0]->get_nodes()));

  # FIXME: level1 deps ?
}

sub filelabel {
  my $self = shift;
  my ($file,$level) = @_;

  return $file;
}

# FIXME: should be generalized and moved into ::graph class
# FIXME: should probably change internal representation of nodes to
# use a hash like in this function
sub _level1nodes {
  my $self = shift;
  my (@filenodes) = @_;
  my (%level1nodes);
  foreach my $filenode (@filenodes) {
    my $level1label = $self->filelabel($filenode->{LABEL});
    $level1nodes{$level1label} = new graphincludes::node($level1label)
      unless defined $level1nodes{$level1label};
    push @{$level1nodes{$level1label}{SUBNODES}}, $filenode;
  }
  return values %level1nodes;
}

sub locatefile {
  my $self = shift;
  my ($dst, @path) = @_;

  print STDERR "Trying to locate \`$dst'\n" if $graphincludes::params::debug;

  sub fullpath {
    my ($dstpath, $strip, $srcpath) = @_;
    catfile (@$srcpath[0..($#$srcpath-$strip)], @$dstpath);
  }

  (undef, my $dstdir, my $filename) = splitpath($dst);
  my @dstpath = (splitdir ($dstdir), $filename);
  # count number of leading "../" in the #include reference
  my $strip = 0;
  while ($dstpath[0] eq '..') {
    $strip++; shift @dstpath;
  }
  # find the file in @path
  my $dstfile;
  foreach my $dir (@path) {
    my @srcpath = splitdir ($dir);
    if (defined($dstfile = fullpath(\@dstpath,$strip,\@srcpath)) and
	grep { $_->{LABEL} eq $dstfile } $self->{GRAPHS}[0]->get_nodes) {
      print STDERR " Found from $dir ($dstfile)\n" if $graphincludes::params::debug;
      last;
    } else {
      print STDERR " Not from $dir ($dstfile)\n" if $graphincludes::params::debug;
      $dstfile = undef;
    }
  }

  return $dstfile;		# can be undef !
}

sub _fileexists {
  my ($file, @path) = @_;
  foreach my $dir (@path) {
    my $f = catpath('', $dir, $file);
    return $f if -r $f;
  }
  return undef;
}

sub record_missed_dep {
  my $self = shift;
  my ($src, $dst) = @_;

  if (defined _fileexists ($dst, @graphincludes::params::sysinclpath)) {
    # list as system include
    $self->{REPORT}->{SYS}->{$dst} = 1;
  } else {
    # list as unknown header
    push @{$self->{REPORT}->{HDR}->{$dst}}, $src;
  }
}

sub special_edge {
  my $self = shift;
  my ($src, $dst) = @_;

  my $attrs = $self->{SPECIALEDGES}->{$src}->{$dst};

  if (defined $attrs) {
    return $attrs;
  } else {
    return undef;
  }
}

1;
