#! /usr/bin/env perl

use strict;
use warnings;
use Getopt::Long;

my %pfs;
my %archs;
my %files;

my $tag = qr {^\s*\#\s*};

sub usage
{
  my $exitvalue = shift;
  my $errortext = shift;
  print "Error: $errortext\n\n" if defined $errortext;
  print STDERR <<EOH;
Usage: $0 options... command
TODO: write this usage
EOH
  exit $exitvalue;
}

my $kconfig_src_file;
my $kconfig_obj_file;
my @kconfig_arch_files;
my @kconfig_pkg_files;

GetOptions("kconfig-src-file=s" => \$kconfig_src_file,
           "kconfig-obj-file=s" => \$kconfig_obj_file,
           "kconfig-arch-file=s" => sub { push @kconfig_arch_files, $_[1]; },
           "kconfig-pkg-file=s" => sub { push @kconfig_pkg_files, $_[1]; }
         ) or usage(1);

usage(1, "--kconfig-src-file not specified.") unless defined $kconfig_src_file;
usage(1, "'$kconfig_src_file' does not exist.") unless -e $kconfig_src_file;
usage(1, "--kconfig-obj-file not specified.") unless defined $kconfig_obj_file;

foreach my $f (@kconfig_arch_files) {
  my $bsp_name;
  $bsp_name = $1 if $f =~ /\/bsp\/([^\/]+)\//;
  open(X, $f) || die "Cannot open $f: $!";
  my $pf;
  my $arch;
  my $arch_cpu;
  while ($_=<X>) {
    if (/$tag PF:\s*(\S+)/x) {
      $pf = { name => $bsp_name, file => $f };
      push (@{$files{$f}}, "PF_$1");
      $pfs{$1} = $pf;
    }
    if  (defined $pf) {
      $pf->{desc}  = $1                        if /$tag PFDESCR:\s*(.+)/x;
      push(@{$pf->{select}}, split(/\s+/, $1)) if /$tag PFSELECT:\s*(.+)/x;
      push(@{$pf->{dep}},    split(/\s+/, $1)) if /$tag PFDEPENDS:\s*(.+)/x;
    }

    if (/^$tag ARCH:\s*(\S+)\s+(\S+)/x) {
      $arch_cpu = undef;
      $arch = { name => $2, file => $f };
      $archs{$1} = $arch;
      push (@{$files{$f}}, $1);
    }
    if (defined $arch) {
      $arch->{desc} = $1            if /$tag ARCHDESCR:\s*(.+)/x;
      $arch->{default_cpu} = $1     if /$tag ARCHDEFAULTCPU:\s*(.+)/x;
      $arch->{default_pf} = $1      if /$tag ARCHDEFAULTPF:\s*(.+)/x;
      push (@{$arch->{select}}, $1) if /$tag ARCHSELECT:\s*(.+)/x;
      push (@{$arch->{dep}}, $1)    if /$tag ARCHDEPENDS:\s*(.+)/x;
      push (@{$arch->{help}}, $1)   if /$tag ARCHHELP:\s*(.+)/x;
      if (/$tag ARCHCPU:\s*(\S+)\s+(.+)/x) {
        $arch_cpu = { name => $1, desc => $2 };
        push (@{$arch->{cpus}}, $arch_cpu);
      }
    }
    if (defined $arch_cpu) {
      push (@{$arch_cpu->{dep}}, $1)    if /$tag ARCHCPUDEPENDS:\s*(.+)/x;
      push (@{$arch_cpu->{select}}, $1) if /$tag ARCHCPUSELECT:\s*(.+)/x;
      push (@{$arch_cpu->{help}}, $1)   if /$tag ARCHCPUHELP:\s*(.+)/x;
    }
  }

  close X;
}

open(IN, $kconfig_src_file) || die "Cannot open $kconfig_src_file: $!";
open(OUT, ">$kconfig_obj_file") || die "Cannot open $kconfig_obj_file: $!";
print OUT "# vi:set ft=kconfig:\n# This Kconfig is auto-generated.\n";
while ($_=<IN>) {
  print OUT;
  if (/$tag ARCH_CHOICE_DEFAULT\W/x) {
    my $a = 'x86';
    $a = $ENV{ARCH} if $ENV{ARCH};
    die "Unknown architecture '$a'" unless defined $archs{"BUILD_ARCH_$a"};
    print OUT "\tdefault BUILD_ARCH_$a\n";
  }
  if (/$tag ARCH_DEFAULT_CPU\W/x) {
    foreach my $i (sort keys %archs) {
      print OUT "\tdefault $archs{$i}{default_cpu}  if $i\n"
        if defined $archs{$i}{default_cpu};
    }
  }
  if (/$tag ARCH_DEFAULT_PF\W/x) {
    foreach my $i (sort keys %archs) {
      print OUT "\tdefault $archs{$i}{default_pf}  if $i\n"
        if defined $archs{$i}{default_pf};
    }
  }
  if (/$tag ARCH_PLATFORMS\W/x) {
    my $f = "$kconfig_obj_file.platforms";
    open(my $pf, $f) || die "Cannot open '$f': $!";
    while (my $l = <$pf>)
      {
        print OUT $l;
      }
    close $pf;
  }
  if (/$tag ARCH_NAME\W/x) {
    foreach my $i (sort keys %archs) {
      print OUT "\tdefault \"$archs{$i}{name}\"  if $i\n"
        if defined $archs{$i}{name};
    }
  }
  if (/$tag ARCH_CHOICE\W/x) {
    foreach my $i (sort keys %archs) {
      next unless defined $archs{$i}{desc};
      print OUT "config $i\n";
      print OUT "\tbool \"$archs{$i}{desc}\"\n";
      print OUT "\tdepends on $_\n" foreach (@{$archs{$i}{dep}});
      print OUT "\tselect $_\n" foreach (@{$archs{$i}{select}});
      if (defined $archs{$i}{help}) {
        print OUT "\thelp\n";
        print OUT "\t\t$_\n" foreach (@{$archs{$i}{help}});
      }
      print OUT "\n";
    }
  }
  if (/$tag ARCH_CPU\W/x) {
    foreach my $i (sort keys %archs) {
      foreach my $c (@{$archs{$i}{cpus}}) {
        print OUT "config $c->{name}\n";
        print OUT "\tbool \"$c->{desc}\"\n";
        print OUT "\tdepends on $i\n";
        print OUT "\tdepends on $_\n" foreach (@{$c->{dep}});
        print OUT "\tselect $_\n" foreach (@{$c->{select}});
        next unless $c->{help};
        print OUT "\thelp\n";
        print OUT "\t\t$_\n" foreach (@{$c->{help}});
        print OUT "\n";
      }
    }
  }

  if (/$tag PF_INCLUDE\W/x) {
    foreach my $i (sort keys %files) {
      print OUT "if " . join(" || ", @{$files{$i}})."\n";
      print OUT "\tsource \"$i\"\n";
      print OUT "endif\n";
    }
    print OUT "config BSP_NAME\n";
    print OUT "\tstring\n";
    foreach my $i (sort keys %pfs) {
      if (defined $pfs{$i}{name}) {
	print OUT "	default \"$pfs{$i}{name}\" if PF_$i\n";
      }
    }
  }
  if (/$tag PF_CHOICE\W/x) {
    foreach my $i (sort keys %pfs) {
      $pfs{$i}{desc} = "$i Platform" unless defined $pfs{$i}{desc};
    }

    foreach my $i (sort { $pfs{$a}{desc} cmp $pfs{$b}{desc} } keys %pfs) {
      print OUT "config PF_$i\n";
      print OUT "	bool \"$pfs{$i}{desc}\"\n";
      print OUT "	depends on $_\n" foreach (@{$pfs{$i}{dep}});
      print OUT "	select $_\n" foreach (@{$pfs{$i}{select}});
      print OUT "\n";
    }
  }

  if (/$tag PKG_KCONFIG\W/x) {
    foreach my $kconfig_file (@kconfig_pkg_files) {
      (my $rel_path = $kconfig_file) =~ s/Kconfig.L4$//;

      open(my $in, $kconfig_file) || die "Cannot open '$kconfig_file': $!";
      while (my $l = <$in>) {
        if ($l =~ /^(\s*source\s+")(.+".*)/) {
          $l = $1.$rel_path.$2;
        }
        print OUT $l;
      }
      close $in;
    }
  }
}
close IN;
close OUT;
