apply plugin: 'c'
apply plugin: 'java'

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

def workdir='build/unzip_boot'

model {
    buildTypes {
        release
    }

    components {
        abootimg(NativeExecutableSpec) {
            binaries.all {
                cppCompiler.define 'HAS_BLKID'
                linker.args "-lblkid"
            }
        }
    }

    components {
        mkbootfs(NativeExecutableSpec) {
            binaries.all {
            }
        }
    }

    components {
        libmincrypt(NativeLibrarySpec) {
            binaries.all {
            }
        }
        mkbootimg(NativeExecutableSpec) {
            binaries.all {
                cCompiler.args '-std=c11'
                lib library: "libmincrypt", linkage: 'static'
            }
        }
    }
}

task unpack_bootimg(type: Exec, dependsOn: 'abootimgExecutable') {
    doFirst {
        def rootdir = new File(workdir)
        if (rootdir.exists()) {
            rootdir.deleteDir()
        }
        rootdir.mkdirs()
    }
    workingDir '.'
    executable 'build/exe/abootimg/abootimg'
    args = ['-x', 'boot.img', workdir+'/bootimg.cfg', workdir+'/kernel', workdir+'/ramdisk.img.gz']
}

task unpack_ramdisk_gz << {
    unGnuzipFile(workdir+"/ramdisk.img.gz", workdir + "/ramdisk.img")
}
unpack_ramdisk_gz.dependsOn(unpack_bootimg)

task unpack_cpio(type: Exec, dependsOn: unpack_ramdisk_gz) {
    doFirst {
        def rootdir = new File(workdir+ "/root")
        if (rootdir.exists()) {
            rootdir.deleteDir()
        }
        rootdir.mkdirs()
    }
    workingDir workdir + "/root"
    executable 'cpio'
    args = ['-i', '-F', '../ramdisk.img']
}

task unpack(type: Delete, dependsOn: unpack_cpio) {
  delete workdir + "/ramdisk.img.gz"
  delete workdir + "/ramdisk.img"
}

task pack_ramdisk_and_gz { Task task ->
    doLast {
        ByteArrayOutputStream mkbootfs_out = new ByteArrayOutputStream()
        task.project.exec {
            commandLine = [ 'build/exe/mkbootfs/mkbootfs', workdir + "/root" ]
            standardOutput = mkbootfs_out
        }
        def ByteArrayInputStream gzip_in = new ByteArrayInputStream(mkbootfs_out.buf)
        gnuZipFile(workdir + "/ramdisk.img.gz", gzip_in)
    }
}
pack_ramdisk_and_gz.dependsOn('mkbootfsExecutable')

task pack_clear(type: Exec, dependsOn: [pack_ramdisk_and_gz, 'mkbootimgExecutable']) {
    def theCmdLine = getRamdiskConfig(workdir, 'cmdline')
    def theBaseAddr = getBaseAddress(workdir)
    workingDir '.'
    executable 'build/exe/mkbootimg/mkbootimg'
    args = [
        '--kernel', workdir + "/kernel",
        '--ramdisk', workdir + "/ramdisk.img.gz",
        '--cmdline', theCmdLine,
        '--base', theBaseAddr,
        '--output', 'boot.img.clear']
}
if (!new File(workdir + "/bootimg.cfg").exists()) {
    pack_clear.enabled = false;
}

void unGnuzipFile(String compressedFile, String decompressedFile) throws IOException {
    byte[] buffer = new byte[1024];
    try {
        FileInputStream fileIn = new FileInputStream(compressedFile);
        GZIPInputStream gZIPInputStream = new GZIPInputStream(fileIn);
        FileOutputStream fileOutputStream = new FileOutputStream(decompressedFile);
        int bytes_read;
        while ((bytes_read = gZIPInputStream.read(buffer)) > 0) {
            fileOutputStream.write(buffer, 0, bytes_read);
        }
        gZIPInputStream.close();
        fileOutputStream.close();
        System.out.println("The file was decompressed successfully!");
    } catch (IOException ex) {
        throw ex;
    }
}

void gnuZipFile(String compressedFile, InputStream fis) throws IOException {
    byte[] buffer = new byte[1024];
    try {
        FileOutputStream fos = new FileOutputStream(compressedFile);
        GZIPOutputStream gos = new GZIPOutputStream(fos);

        int bytes_read;
        while ((bytes_read = fis.read(buffer)) > 0) {
            gos .write(buffer, 0, bytes_read);
        }
        gos.finish();
        gos.close();
        System.out.println("The file compressed successfully!");
    } catch (IOException ex) {
        throw ex;
    }
}

void gnuZipFile(String compressedFile, String decompressedFile) throws IOException {
    byte[] buffer = new byte[1024];
    try {
        FileOutputStream fos = new FileOutputStream(compressedFile);
        GZIPOutputStream gos = new GZIPOutputStream(fos);

        FileInputStream fis = new FileInputStream(decompressedFile);
        int bytes_read;
        while ((bytes_read = fis.read(buffer)) > 0) {
            gos .write(buffer, 0, bytes_read);
        }
        fis.close();
        gos.finish();
        gos.close();
        System.out.println("The file compressed successfully!");
    } catch (IOException ex) {
        throw ex;
    }
}

String getBaseAddress(String inWorkdir) {
    Long ret;
    try {
        ret = Long.parseLong(getRamdiskConfig(inWorkdir, "kerneladdr").substring(2), 16) - 0x8000L;
    } catch (NumberFormatException e) {
        throw new RuntimeException("NumberFormatException: invalid kerneladdr value")
    }
    return Long.toHexString(ret);
}

String getRamdiskConfig(String inWorkdir, String inKey) {
    String ret;
    if (!new File(inWorkdir+ "/bootimg.cfg").exists()) {
        return "0x0";
    }
    try {
        Properties prop = new Properties();
        InputStream fis = new FileInputStream(inWorkdir+ "/bootimg.cfg")
        prop.load(fis);
        ret = prop.getProperty(inKey);
        if (fis != null) {
            fis.close();
        }
    } catch (IOException e) {
        throw new RuntimeException("IOException");
    }
    return ret;
}

task pack(type: JavaExec, dependsOn: [pack_clear, 'boot_signer:jar']) {
    main = 'com.android.verity.BootSignature'
    classpath = files("boot_signer/build/libs/boot_signer.jar")
    maxHeapSize '512m'
    args '/boot','boot.img.clear', 'security/verity.pk8', 'security/verity.x509.pem', 'boot.img.signed'
}

task _setup(type: Copy) {
    from 'src/test/resources/boot.img'
    into '.'
}

