#!/usr/bin/env perl

###################
################### This tool is horribly written.
################### It is a bloody hack.
################### It should be refactored. But it seems to kind of work
###################
###################

my $executed_as = join( ' ', @ARGV );

use warnings;
use strict;
use File::Temp qw/tempdir/;
use Cwd qw/abs_path cwd/;
use JSON::PP;
use Getopt::Long;

my @ARCHES = 	(qw(avr gd32));

my $platforms_template = {
    avr => {
        'toolsDependencies' => [
            {
                "packager" => "arduino",
                "name"     => "avr-gcc",
                "version"  => "7.3.0-atmel3.6.1-arduino7"
            },
            {
                "packager" => "arduino",
                "name"     => "avrdude",
                "version"  => "6.3.0-arduino17"
            }
        ],
        'name'   => "Kaleidoscope keyboards",
        'boards' => [
            { 'name' => 'Keyboardio Model 01' },
            { 'name' => 'Keyboardio Atreus' }
        ],
        'architecture' => 'avr',
        'category'     => 'Contributed',
        'help'         => {
            'online' => 'https://community.keyboard.io'
        }
    },
    gd32 => {
        'name'              => "Kaleidoscope keyboards (GD32)",
        'architecture'      => 'gd32',
        'toolsDependencies' => [
            {
                'name'     => 'xpack-arm-none-eabi-gcc',
                'packager' => 'keyboardio',
                'version'  => '9.3.1-1.3'
            },
            {
                'packager' => 'keyboardio',
                'version'  => '0.11.0-1',
                'name'     => 'xpack-openocd'
            },
            {
                'name'     => 'dfu-util',
                'version'  => '0.10.0-arduino1',
                'packager' => 'keyboardio'
            }
        ],
        'version' => '0.0.1',
        'name'    => 'Kaleidoscope Keyboards (GD32)',
        'boards'  => [
            {
                'name' => 'Keyboardio Model 100'
            }
        ],
        'category' => 'Contributed',
        'help'     => {
            'online' => 'https://community.keyboard.io'
        }
    }
};
my $version             = '';
my $tag                 = 'master';
my $gd32_tag	= 'main';
my $kaleidoscope_tag    = 'master';
my $working_dir         = tempdir( CLEANUP => 0 );
my $index_filename_slug = 'keyboardio';
my $output_dir          = $working_dir . '/boards_manager_repo';
my $builds_dir          = $output_dir . '/builds';

my $only_latest_platform = 0;

my $bundle_repo = 'https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio';
my $gd32_core_repo = 'https://github.com/keyboardio/ArduinoCore-GD32-Keyboardio';
my $package_repo       = 'https://github.com/keyboardio/boardsmanager';

my $push_package_repo = 0;

GetOptions(
    "bundle-tag=s"               => \$tag,
    "arch-gd32-tag=s"               => \$gd32_tag,
    "kaleidoscope-tag=s"         => \$kaleidoscope_tag,
    "bundle-repo=s"              => \$bundle_repo,
    "output-repo=s"              => \$package_repo,
    "index-filename-slug=s"      => \$index_filename_slug,
    "version=s"                  => \$version,
    "only-one-platform-revision" => \$only_latest_platform,
    "push"                       => \$push_package_repo
) or die("Error in command line arguments\n");

my $index_filename = 'package_' . $index_filename_slug . '_index.json';

my $main_branch_name = 'main';

if ( $version eq '' && $tag =~ /^v(\d.*)$/ ) {
    $version = $1;
}

my $checkout_dir = 'kaleidoscope-checkout';

my $build_base_url = $package_repo . "/raw/$main_branch_name/builds";
$build_base_url =~ s|ssh://git@|https://|;

print "Working directory: $working_dir\n";
chdir($working_dir);

clone_repos( $tag, $gd32_tag, $bundle_repo, $gd32_core_repo, $checkout_dir, $package_repo );

chdir( $working_dir . "/" . $checkout_dir );
if ($only_latest_platform) {
    `rm -rf $builds_dir/*`;
}

foreach my $arch (@ARCHES) {

    remove_unneeded_files($arch);

    # Get the specified Kaleidoscope build and all its plugins as libraries
    # into the checkout
    chdir("$working_dir/$checkout_dir/$arch/libraries/Kaleidoscope");
    checkout_kaleidoscope($kaleidoscope_tag);

    lift_plugins_to_arduino_libs();
    set_plugin_versions( $working_dir, $checkout_dir, $arch, $version );

    chdir($working_dir);

    my $release_dir = "Kaleidoscope-$arch-$version";
    my $filename    = $release_dir . ".tar.bz2";

    build_archive( $arch, $checkout_dir, $release_dir, $filename );
    deploy_build( $builds_dir, $filename );

}

update_index_file( $output_dir, $index_filename );
chdir("$output_dir");
commit_results($index_filename);
finalize_action($push_package_repo);

exit 0;

sub set_plugin_versions {
    my $working_dir  = shift;
    my $checkout_dir = shift;
    my $arch         = shift;
    my $version      = shift;
    chdir("$working_dir/$checkout_dir/$arch/libraries");
    `perl -pi -e's/version=0.0.0/version=$version/' */library.properties`;
}

sub clone_repos {
    my $tag          = shift;
    my $gd32_tag = shift;
    my $bundle_repo  = shift;
    my $gd32_repo = shift;
    my $checkout_dir = shift;
    my $package_repo  = shift;
`git clone --depth=1 --quiet --recurse-submodules --branch $tag $bundle_repo $checkout_dir`;

`git clone --depth=1 --quiet --recurse-submodules --branch $gd32_tag $gd32_repo $checkout_dir/gd32`;
    `git clone $package_repo $output_dir`;
}

sub checkout_kaleidoscope {
    my $tag = shift;
    `git checkout --quiet $tag`;
}

sub lift_plugins_to_arduino_libs {
    if ( -d 'plugins' ) {
        chdir("plugins");

        # move the plugins to be fullfledged arduino libraries
        `mv * ../../`;
    }
}

sub update_index_file {
    my $output_dir     = shift;
    my $index_filename = shift;

    my $json = JSON::PP->new->allow_nonref;
    local $/;

    my $index_path = $output_dir . '/' . $index_filename;

    open( my $fh, '<', $index_path ) || die "Could not open $index_path $!";
    my $json_text = <$fh>;
    my $data      = decode_json($json_text);

    #my $this_packager = $data->{'packages'}->[0]->{'name'};
    #foreach my $tool (@{$platforms_template->{'gd32'}->{'toolsDependencies'}}) {
    #		# we need to set the packager of these tools to ourself, since that's what arduino expects
    #	    $tool->{"packager"} = $this_packager;
    #	}

    if ($only_latest_platform) {
        @{ $data->{'packages'}->[0]->{'platforms'} } =
          ( $platforms_template->{'avr'}, $platforms_template->{'gd32'} );
    }
    else {
        push @{ $data->{'packages'}->[0]->{'platforms'} },
          $platforms_template->{'avr'},
          $platforms_template->{'gd32'};
    }
    my $json_out = $json->canonical->pretty->encode($data);
    open( my $out_fh, '>', $index_path );
    print $out_fh $json_out;
    close($out_fh);
}

sub commit_results {
    my $index_filename = shift;
    `git add $index_filename`;
    `git add builds`;
    `git commit -a -m 'Built by $executed_as'`;
}

sub remove_unneeded_files {
    my $arch = shift;

    my @bundle_dirs_to_remove = (
        "etc",
        "doc",
        $arch . '/bootloaders/*/lufa',
        "$arch/libraries/Kaleidoscope/testing",
        "$arch/libraries/Kaleidoscope/tests",
        "$arch/libraries/Kaleidoscope/docs"
    );

    foreach my $dir_to_remove (@bundle_dirs_to_remove) {
        print "rm -rf $dir_to_remove\n";
        `rm -rf $dir_to_remove`;
    }
}

sub deploy_build {
    my $builds_dir = shift;
    my $filename   = shift;
    `mkdir -p $builds_dir`;
    `cp $filename $builds_dir/`;
}

sub build_archive {
    my $arch         = shift;
    my $checkout_dir = shift;
    my $release_dir  = shift;
    my $filename     = shift;

    `mv $checkout_dir/$arch $checkout_dir/$release_dir`;
    ;    # A hack to get consistent naming with the old setup
    `find "$checkout_dir/$release_dir" -name .git |xargs rm -rf`;
    `tar cjvf $filename -C $checkout_dir/ $release_dir `;

    my $sha256 = `sha256sum $filename | cut -d' ' -f 1 `;
    chomp($sha256);
    die "There was a problem generating the sha256" unless ($sha256);
    my $size = -s $filename;

    $platforms_template->{$arch}->{'archiveFileName'} = $filename;
    $platforms_template->{$arch}->{'url'} = $build_base_url . '/' . $filename;
    $platforms_template->{$arch}->{'version'}  = $version;
    $platforms_template->{$arch}->{'checksum'} = 'SHA-256:' . $sha256;
    $platforms_template->{$arch}->{'size'}     = $size;

}

sub finalize_action {
    my $do_push = shift;

    if ($do_push) {
        `git push`;
    }
    else {
        print "Now, you need to cd to $output_dir check the content and push it\n";
    }

}