package com.jogamp.common.os;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.List;

import jogamp.common.os.PlatformPropsImpl;
import jogamp.common.os.elf.ElfHeaderPart1;
import jogamp.common.os.elf.ElfHeaderPart2;
import jogamp.common.os.elf.Section;
import jogamp.common.os.elf.SectionArmAttributes;
import jogamp.common.os.elf.SectionHeader;

import org.junit.Test;

import com.jogamp.common.os.Platform.OSType;
import com.jogamp.junit.util.SingletonJunitCase;

import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestElfReader01 extends SingletonJunitCase {
    public static String GNU_LINUX_SELF_EXE = "/proc/self/exe";
    public static String ARM_HF_EXE = "tst-exe-armhf";
    public static String ARM_SF_EXE = "tst-exe-arm";
    static File userFile = null;

    private static boolean checkFileReadAccess(final File file) {
        try {
            return file.isFile() && file.canRead();
        } catch (final Throwable t) { }
        return false;
    }
    static File findJVMLib(final String libName) {
        final ClassLoader cl = TestElfReader01.class.getClassLoader();
        final List<NativeLibrary.LibPath> possibleLibPaths = NativeLibrary.enumerateLibraryPaths(libName, libName, libName, true, cl);
        for(int i=0; i<possibleLibPaths.size(); i++) {
            final NativeLibrary.LibPath libPath = possibleLibPaths.get(i);
            final File lib = new File(libPath.path);
            System.err.println("XXX2 #"+i+": test "+lib);
            if( checkFileReadAccess(lib) ) {
                return lib;
            }
            System.err.println("XXX2 #"+i+": "+lib+" not readable");
        }
        return null;
    }

    @Test
    public void test01GNULinuxSelfExe () throws IOException {
        if( null == userFile ) {
            if( OSType.LINUX == Platform.getOSType() ) {
                final File f = new File(GNU_LINUX_SELF_EXE);
                if( checkFileReadAccess(f) ) {
                    testElfHeaderImpl(f, false);
                }
            }
        }
    }

    @Test
    public void test02JavaLib () throws IOException {
        if( null == userFile ) {
            File jvmLib = findJVMLib("java");
            if( null == jvmLib ) {
                jvmLib = findJVMLib("jvm");
            }
            if( null != jvmLib ) {
                testElfHeaderImpl(jvmLib, false);
            }
        }
    }

    @Test
    public void test99UserFile() throws IOException {
        if( null != userFile ) {
            testElfHeaderImpl(userFile, false);
        }
    }

    void testElfHeaderImpl(final File file, final boolean fileOutSections) throws IOException {
        Platform.initSingleton();
        System.err.println("Test file "+file.getAbsolutePath());
        final RandomAccessFile in = new RandomAccessFile(file, "r");
        try {
            final ElfHeaderPart1 eh1;
            final ElfHeaderPart2 eh2;
            try {
                eh1 = ElfHeaderPart1.read(PlatformPropsImpl.OS_TYPE, in);
                eh2 = ElfHeaderPart2.read(eh1, in);
            } catch (final Exception e) {
                System.err.println("Probably not an ELF file - or not in current format: (caught) "+e.getMessage());
                e.printStackTrace();
                return;
            }
            int i=0;
            System.err.println(eh1);
            System.err.println(eh2);
            System.err.println("SH entsz     "+eh2.raw.getE_shentsize());
            System.err.println("SH off       "+toHexString(eh2.raw.getE_shoff()));
            System.err.println("SH strndx    "+eh2.raw.getE_shstrndx());
            System.err.println("SH num "+eh2.sht.length);
            if( 0 < eh2.sht.length ) {
                System.err.println("SH size "+eh2.sht[0].raw.getBuffer().limit());
            }
            {
                final SectionHeader sh = eh2.getSectionHeader(SectionHeader.SHT_ARM_ATTRIBUTES);
                boolean abiVFPArgsAcceptsVFPVariant = false;
                if( null != sh ) {
                    final SectionArmAttributes sArmAttrs = (SectionArmAttributes) sh.readSection(in);
                    final SectionArmAttributes.Attribute abiVFPArgsAttr = sArmAttrs.get(SectionArmAttributes.Tag.ABI_VFP_args);
                    if( null != abiVFPArgsAttr ) {
                        abiVFPArgsAcceptsVFPVariant = SectionArmAttributes.abiVFPArgsAcceptsVFPVariant(abiVFPArgsAttr.getULEB128());
                    }
                }
                System.err.println("abiVFPArgsAcceptsVFPVariant "+abiVFPArgsAcceptsVFPVariant);
            }
            for(i=0; i<eh2.sht.length; i++) {
                final SectionHeader sh = eh2.sht[i];
                System.err.println(sh);
                final int type = sh.getType();
                if( SectionHeader.SHT_STRTAB == type ) {
                    dumpSection(in, sh, "SHT_STRTAB", fileOutSections);
                } else if( SectionHeader.SHT_ARM_ATTRIBUTES == type ) {
                    dumpSection(in, sh, "SHT_ARM_ATTRIBUTES", fileOutSections);
                }
            }
        } finally {
            in.close();
        }
    }

    static void dumpSection(final RandomAccessFile in, final SectionHeader sh, final String name, final boolean fileOut) throws IllegalArgumentException, IOException {
        final Section s = sh.readSection(in);
        if(fileOut) {
            final File outFile = new File("ElfSection-"+sh.getIndex()+"-"+name);
            final OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
            try {
                out.write(s.data, s.offset, s.length);
            } finally {
                out.close();
            }
        }
        System.err.println(name+": read "+s.length+", "+s);
    }

    public static void main(final String args[]) throws IOException {
        for(int i=0; i<args.length; i++) {
            if(args[i].equals("-file")) {
                i++;
                userFile = new File(args[i]);
            }
        }
        final String tstname = TestElfReader01.class.getName();
        org.junit.runner.JUnitCore.main(tstname);
    }

    static String toHexString(final int i) { return "0x"+Integer.toHexString(i); }
    static String toHexString(final long i) { return "0x"+Long.toHexString(i); }

}
