package OpenEmbeded::Builder;

use strict;
use warnings;

use Logging::LoggerManager;
use Logging::Level;
use Settings::Simple;
use Getopt::Long;
use Systems::AbstractSystem;
use OpenEmbeded::Image;
use File::Basename;
use Cwd;

sub new {
    my $self = bless {}, shift;
    if ( shift->inject($self) != 0 ) {
        exit(1);
    }
    return $self;
}

my %actions = (
                buil   => "[package] : Build OpenEmbeded Image",
                update => "[package] : Update OpenEmbeded Image",
                clean  => ": Clean up environement",
                update => "[openenbeded] : update either zaurus fr customizations or openembeded branch"
);

my $URLOE      = "http://feed.zaurusfr.org/zaurusfr/OE/OE.mtn.bz2";
my $SVN_ROOT   = "http://artisan.karma-lab.net/software/subversion/zaurusfr/trunk/zaurusfr";
my $SVN_MYOE   = "$SVN_ROOT/myoe";
my $SVN_PATCHS = "$SVN_ROOT/patchs";

sub start() {
    my $self   = shift;
    my $action = shift;
    $self->validate;
    $self->checkStuffPath;
    #$self->checkIfConnected;
    $self->checkTools;
    $self->checkOpenEmbeded;

    $self->logger->section("Running '$action' action");
    $self->$action(@_);
}

sub build {
    my ($self) = shift;

    my @packages;
    if (@_) {
        @packages = @_;
    }
    else {
        @packages = $self->image->packages;
    }

    # Sanity check
    $self->checkOpenEmbededEnvironementFile;
    $self->checkBitBakeLocalConfiguration;
    $self->checkBranch;
    $self->checkCustomizations;

    foreach my $package (@packages) {
        $self->createPackage($package);
    }
}

sub update {
    my ($self) = shift;
    my $target = shift;

    if ( defined $target && $target eq 'openembeded' ) {
        print "Do you really want to update ? It's quite possible your image/package will no compile any more !\n";
        my $answer = <STDIN>;
        exit 0 if ( $answer !~ /o|O|y|Y/i );
        $self->updateBranch();
    }
    else {
        $self->updateCustomizations();
    }

}

sub clean {
    my ($self) = shift;

    my @packages;
    if (@_) {
        @packages = @_;
    }
    else {
        @packages = $self->image->packages;
    }
    foreach my $package (@packages) {
        $self->cleanPackage($package);
    }
}

sub logger {
    my $self = shift;
    if ( !defined $self->{logger} ) {
        $self->{logger} = Logging::LoggerManager::Instance->logger(__PACKAGE__);
    }
    return $self->{logger};
}

sub stuff {
    my $self = shift;
    if (@_) {
        $self->{stuff} = shift;
    }
    return $self->{stuff};
}

sub image {
    my $self = shift;
    if (@_) {
        my $image = new OpenEmbeded::Image(shift);
        $self->{image} = $image;
    }
    return $self->{image};
}

sub host {
    my $self = shift;
    if (@_) {
        my $system = shift;
        $system = "Systems::" . $system;
        eval "require $system";
        $self->{host} = $system->new;
        $self->{host}->logFile(getcwd()."/openembeded");
    }
    return $self->{host};
}

sub actions {
    return %actions;
}

sub machine {
    my $self = shift;
    if (@_) { $self->{machine} = shift }
    return $self->{machine};
}

sub createPackage {
    my ( $self, $package ) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;
    $self->logger->section("Creating package '$package'");

    chdir($self->stuff);
    if ( $package =~ /\.bb$/ ) {
        $self->host->runFree("cd $image;source oe.env;bitbake -b $branch/packages/$package");
    }
    else {
        $self->host->runFree("cd $image;source oe.env;bitbake $package");
    }
}

sub cleanPackage {
    my ( $self, $package ) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;
    chdir($self->stuff);
    $self->logger->section("Cleaning package '$package'");
    $self->host->runFree("cd $image/;source oe.env;bitbake -c clean $package");
}

sub checkBranch {
    my ($self) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;

    # si la branche n'est pas présente : creation de celle-ci
    if ( !-d $self->image->name . "/$branch" ) {
        $self->logger->startProcess("Creating branch '$branch'");
        $self->host->runVerbose("cd $image;mtn co --db=../OE.mtn --branch $branch");
        $self->logger->validateProcess();
    }
    else {
        $self->logger->status("Found existing branch '$branch'");

    }
}

sub updateBranch {
    my ($self) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;
    $self->logger->info("Updating branch '$branch'");
    if ( !-d "$image" ) {
        $self->host->run("mkdir -p $image");
    }

    # Mise a jour de la base
    $self->logger->info("Mise a jour de la base OE");
    $self->host->run("mtn --db=OE.mtn pull monotone.openembedded.org $branch");

    # maj de la branche
    $self->logger->info("Mise a jour de '$branch'");
    $self->host->run("cd $image/$branch;mtn update");
}

# Creation du fichier oe.env
sub checkOpenEmbededEnvironementFile {
    my ( $self, $package ) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;

    $self->logger->startProcess("Creating oe.env");

    $self->host->createDir("angstrom");
    if ( !-e "zaurusfr" ) {
        $self->host->run("ln -s angstrom zaurusfr");
    }
    if ( !open( OEENV, ">angstrom/oe.env" ) ) {
        $self->logger->severe("Unable to create 'oe.env' : $!");
        exit 1;
    }
    my $stuff = getcwd();
    print OEENV "
    export BBPATH=$stuff/$image/build:$stuff/$image/$branch
    export PATH=$stuff/tools/bitbake/bin:\$PATH
    unset TERMINFO
    unset LD_LIBRARY_PATH && export LD_LIBRARY_PATH
    export LANG=C
    ";
    close OEENV;
    $self->logger->validateProcess();
}

# Creation du fichier build/local.conf
sub checkBitBakeLocalConfiguration {
    my ( $self, $package ) = @_;
    my $image  = $self->image->name;
    my $branch = $self->image->branch;
    my $stuff  = getcwd();

    if ( !-e "$image/build/conf/local.conf" ) {
        $self->logger->startProcess("Creating Bitbake local configuration");

        if ( !-d $self->image->name . "/build/conf" ) {
            $self->host->createDir( $self->image->name . "/build/conf" );
        }
        open( LOCAL, ">$image/build/conf/local.conf" );
        print LOCAL '
MACHINE = "' . $self->machine . '"
TARGET_ARCH = "arm"
TARGET_OS = "linux"
DISTRO = "' . $self->image->distribution . '"
KERNEL_VERSION = "2.6"

INHERIT = "package_ipk"
ASSUME_PROVIDED +="qemu-native"

DL_DIR   = "' . $stuff . '/sources/"
BBFILES = "' . $stuff . '/' . $image
          . '/' . $self->image->branch . '/packages/*/*.bb '
          . $stuff . '/'
          . $image
          . '/myoe/packages/*/*.bb"

#what kind of images do we want?
IMAGE_FSTYPE="jffs2 tar"

#Make use of my SMP box
# PARALLEL_MAKE="-j2"

GLIBC_GENERATE_LOCALES = "en_US.UTF-8 fr_FR.UTF-8"

KERNEL_STAGING = "${STAGING_DIR}/${PACKAGE_ARCH}-${HOST_OS}/kernel"
STAGING_KERNEL_DIR = "\${STAGING_DIR}/\${PACKAGE_ARCH}-${HOST_OS}/kernel"
STAMP = "${TMPDIR}/stamps/${PACKAGE_ARCH}-${HOST_OS}/${PF}"
WORKDIR = "${TMPDIR}/work/${PACKAGE_ARCH}-${HOST_OS}/${PF}"

#Add verbosity to make fixing easier
BBINCLUDELOGS = "yes"
CVS_TARBALL_STASH = "http://www.oesources.org/source/current/"
';
        $self->logger->validateProcess();
    }
    else {
        $self->logger->status("Checking Bitbake local configuration");
    }
}

sub checkStuffPath {
    my ($self) = @_;
    $self->host->createDir("stuff");
    chdir("stuff");
    $self->stuff(getcwd());
}

# La machine est-elle connectée a l'internet ?
sub checkIfConnected {
    my ($self) = @_;
    $self->logger->startProcess("Checking if we are connected");
    my $error = $self->host->checkConnection();
    $self->logger->validateProcess($error);
    if ( $error != 0 ) { exit(1); }
}
sub checkBitbake {
    my ( $self, $version ) = @_;
    my $name    = "bitbake";

    if ( ! -d "tools/$name") {
        $self->logger->section("Installing $name");
        $self->host->createDir("tools");
        chdir("tools");
        $self->logger->info("Downloading...");
        $self->host->run("svn co http://svn.berlios.de/svnroot/repos/bitbake/branches/bitbake-$version bitbake");
        chdir($name);
        $self->logger->info("Building...");
        $self->host->runProgress("./setup.py build");
    }    
}
sub checkQemu {
    my ( $self, $version ) = @_;
    my $name    = "qemu";
    my $base    = "$name-$version";
    my $archive = "$base.tar.gz";

    $self->checkPackage('libsdl1.2-dev');
    if ( $self->host->commandExists("qemu-arm")) {
        $self->logger->section("Installing $base");
        $self->host->createDir("tools");
        chdir("tools");
        if ( !-e "$archive" ) {
            $self->logger->info("Downloading...");
           $self->host->run("wget http://fabrice.bellard.free.fr/qemu/$archive");
        }
        if ( !-d "$base" ) {
            $self->logger->info("Uncompressing...");
            $self->host->run("tar -zxvf $archive"); 
        }
        chdir($base);
        $self->logger->info("Building...");
        my $gcc3=`find /usr/bin -name "gcc-3*"`;
        chop($gcc3);
        $self->host->runProgress("./configure --prefix=/usr --disable-kqemu --cc=$gcc3 --disable-darwin-user --enable-linux-user");
        $self->host->runProgress("make");
        $self->host->runProgress("sudo make install");
    }
}

sub checkMonotone {
    my ( $self, $version ) = @_;
    my $name    = "monotone";
    my $base    = "$name-$version";
    my $archive = "$base.tar.gz";

    $self->checkPackage('libboost-dev');
    if ( $self->host->commandExists("mtn")) {
        $self->logger->section("Installing $base");
        $self->host->createDir("tools");
        chdir("tools");
        if ( !-e "$archive" ) {
            $self->logger->info("Downloading...");
            $self->host->run("wget http://monotone.ca/downloads/$version/$archive");
        }
        if ( !-d "$base" ) {
            $self->logger->info("Uncompressing...");
            $self->host->run("tar -zxvf $archive");
        }
        chdir($base);
        $self->logger->info("Building...");
        $self->host->runProgress("./configure --prefix=/usr");
        $self->host->runProgress("make");
        $self->host->runProgress("sudo make install");
    }
}

sub checkTools {
    my ($self) = @_;
    my @packages = (
                     "bzip2",      "tar",       "wget",         "python",
                     "subversion", "git",       "cvs",          "help2man",
                     "diffstat",   "texi2html", "python-psyco", "python-pysqlite2","g++", "zlib1g-dev", "pax-utils",
                     "patch",      "gawk",      "texinfo"
    );
    $self->checkPackage(@packages);
   $self->checkMonotone("0.37");
   $self->checkQemu("0.9.0");
    $self->checkBitbake("1.8");
}

sub checkPackage {
    my ($self) = shift;
    while (@_) {
        my $package = shift;
#        print $package."\n";
        if ( $self->host->packageExists($package) ) {
            $self->logger->startProcess("Installing package '$package'");
            my $res = $self->host->installPackage($package);
            $self->logger->validateProcess($res);
            if ($res) { exit(1); }
        }
    }    
}

sub validate {
    my ($self) = @_;
    if ( !defined $self->host ) {
        $self->logger->severe("You should have a valid host definition in your configuration.");
        exit(1);
    }
    if ( !defined $self->image ) {
        $self->logger->severe("You should have a valid image definition in your configuration.");
        exit(1);
    }
    $self->image->validate;
    $self->logger->info( "   target machine                 : %s", $self->machine );
}


# L'environnement Openembedded est-il present ?
sub checkOpenEmbeded {
    my ($self) = @_;
    chdir($self->stuff);
    if ( !-e "OE.mtn" ) {
        $self->logger->section("Installing OpenEmbedded");
        $self->logger->info("Downloading repository...");
        $self->host->run("wget -c $URLOE -O OE.mtn.bz2");
        $self->logger->info("Uncompression...");
        $self->host->run("bunzip2 OE.mtn.bz2");
        $self->logger->info("Migrating...");
        $self->host->run("mtn --db OE.mtn db migrate");
    }

}

sub checkCustomizations {
    my ($self) = @_;
    my $image = $self->image->name;
    if ( !-d "$image/patchs" ) {
        $self->logger->info("Retreiving patchs");
        $self->host->run( "
                cd $image;
                svn co $SVN_PATCHS"
        );
        $self->buildPatchs;
    }
    if ( !-d "$image/myoe" ) {
        $self->logger->info("Retreiving myoe");
        $self->host->runProgress("cd $image; svn co $SVN_MYOE");
    }
}

sub updateCustomizations {
    my ($self) = @_;
    my $image = $self->image->name;
    $self->logger->info("Updating patchs");
    $self->host->run( "
        cd $image/patchs; 
        svn up;
        "
    );
    $self->buildPatchs;
    $self->logger->info("Updating myoe ");
    $self->host->runVerbose(
        "cd $image/myoe;
                      svn up" );
}

sub buildPatchs {
    my ($self) = @_;
    my $image = $self->image->name;
    $self->logger->info("Build patchs");
    $self->host->run( "
        cd $image/patchs;
        make install"
    );
}

sub showSettings {
    my ( $self, $builder ) = @_;
    $self->logger->section("Current settings");
    $self->logger->info( "   machine                 : %s", $self->machine );
    $self->logger->section("Current Image");
    $self->logger->info( "   name                    : %s", $self->image->name );
    my @packages;
    $self->logger->info( "   packages                : %s", join( "\n", $self->image->packages ) );
}
1;

