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.elf.ElfHeader;
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.JunitTracer;

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

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestElfReader01 extends JunitTracer {
    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";
            
    private static boolean checkFileReadAccess(File file) {
        try {
            return file.isFile() && file.canRead();
        } catch (Throwable t) { }
        return false;
    }    
    static File findJVMLib(String libName) {
        ClassLoader cl = TestElfReader01.class.getClassLoader();
        final List<String> possibleLibPaths = NativeLibrary.enumerateLibraryPaths(libName, libName, libName, true, cl);
        for(int i=0; i<possibleLibPaths.size(); i++) {
            final String libPath = possibleLibPaths.get(i);
            final File lib = new File(libPath);
            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 testGNULinuxSelfExe () throws IOException {
        if( OSType.LINUX == Platform.getOSType() ) {
            File f = new File(GNU_LINUX_SELF_EXE);
            if( checkFileReadAccess(f) ) {
                testElfHeaderImpl(f, false);
            }
        }
    }
    
    @Test
    public void testJavaLib () throws IOException {
        File jvmLib = findJVMLib("java");
        if( null == jvmLib ) {
            jvmLib = findJVMLib("jvm");
        }
        if( null != jvmLib ) {
            testElfHeaderImpl(jvmLib, false);
        }
    }
    
    void testElfHeaderImpl(File file, boolean fileOutSections) throws IOException {
        System.err.println("Test file "+file.getAbsolutePath());
        RandomAccessFile in = new RandomAccessFile(file, "r");
        try {
            final ElfHeader eh;
            try {
                eh = ElfHeader.read(in);
            } catch (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(eh);
            System.err.println("SH entsz     "+eh.d.getE_shentsize());
            System.err.println("SH off       "+toHexString(eh.d.getE_shoff()));
            System.err.println("SH strndx    "+eh.d.getE_shstrndx());
            System.err.println("SH num "+eh.sht.length);
            if( 0 < eh.sht.length ) {
                System.err.println("SH size "+eh.sht[0].d.getBuffer().limit());
            }
            {
                SectionHeader sh = eh.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<eh.sht.length; i++) {
                final SectionHeader sh = eh.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(RandomAccessFile in, SectionHeader sh, String name, boolean fileOut) throws IllegalArgumentException, IOException {
        final Section s = sh.readSection(in);
        if(fileOut) {
            File outFile = new File("ElfSection-"+sh.getIndex()+"-"+name);
            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(String args[]) throws IOException {
        String tstname = TestElfReader01.class.getName();
        org.junit.runner.JUnitCore.main(tstname);
    }
    
    static String toHexString(int i) { return "0x"+Integer.toHexString(i); }
    static String toHexString(long i) { return "0x"+Long.toHexString(i); }
    
}