/*
 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.tools.jaotc.binformat.elf;

import java.nio.ByteBuffer;
import java.util.ArrayList;

import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Sym;
import jdk.tools.jaotc.binformat.elf.ElfSymbol;
import jdk.tools.jaotc.binformat.elf.ElfByteBuffer;

final class ElfSymtab {

    private final ArrayList<ElfSymbol> localSymbols = new ArrayList<>();
    private final ArrayList<ElfSymbol> globalSymbols = new ArrayList<>();

    /**
     * number of symbols added
     */
    private int symbolCount;

    /**
     * String holding symbol table strings
     */
    private final StringBuilder strTabContent = new StringBuilder();

    /**
     * Keeps track of bytes in string table since strTabContent.length() is number of chars, not
     * bytes.
     */
    private int strTabNrOfBytes = 0;

    ElfSymtab() {
        symbolCount = 0;
    }

    ElfSymbol addSymbolEntry(String name, byte type, byte bind, byte secHdrIndex, long offset, long size) {
        // Get the current symbol index and append symbol name to string table.
        int index;
        ElfSymbol sym;

        if (name.isEmpty()) {
            index = 0;
            strTabContent.append('\0');
            strTabNrOfBytes += 1;
            sym = new ElfSymbol(symbolCount, index, type, bind, secHdrIndex, offset, size);
            localSymbols.add(sym);
        } else {
            // We can't trust strTabContent.length() since that is
            // chars (UTF16), keep track of bytes on our own.
            index = strTabNrOfBytes;
            // strTabContent.append("_").append(name).append('\0');
            strTabContent.append(name).append('\0');
            // + 1 for null, + 1 for "_"
            // strTabNrOfBytes += (name.getBytes().length + 1 + 1);
            strTabNrOfBytes += (name.getBytes().length + 1);

            sym = new ElfSymbol(symbolCount, index, type, bind, secHdrIndex, offset, size);
            if ((bind & Elf64_Sym.STB_GLOBAL) != 0)
                globalSymbols.add(sym);
            else
                localSymbols.add(sym);
        }
        symbolCount++;
        return (sym);
    }

    // Update the symbol indexes once all symbols have been added.
    // This is required since we'll be reordering the symbols in the
    // file to be in the order of Local then global.
    void updateIndexes() {
        int index = 0;

        // Update the local symbol indexes
        for (int i = 0; i < localSymbols.size(); i++) {
            ElfSymbol sym = localSymbols.get(i);
            sym.setIndex(index++);
        }

        // Update the global symbol indexes
        for (int i = 0; i < globalSymbols.size(); i++) {
            ElfSymbol sym = globalSymbols.get(i);
            sym.setIndex(index++);
        }
    }

    int getNumLocalSyms() {
        return localSymbols.size();
    }

    int getNumGlobalSyms() {
        return globalSymbols.size();
    }

    // Create a single byte array that contains the symbol table entries
    byte[] getSymtabArray() {
        ByteBuffer symtabData = ElfByteBuffer.allocate(symbolCount * Elf64_Sym.totalsize);
        byte[] retarray;

        updateIndexes();

        // Add the local symbols
        for (int i = 0; i < localSymbols.size(); i++) {
            ElfSymbol sym = localSymbols.get(i);
            byte[] arr = sym.getArray();
            symtabData.put(arr);
        }
        // Add the global symbols
        for (int i = 0; i < globalSymbols.size(); i++) {
            ElfSymbol sym = globalSymbols.get(i);
            byte[] arr = sym.getArray();
            symtabData.put(arr);
        }
        retarray = symtabData.array();

        return (retarray);
    }

    // Return the string table array
    byte[] getStrtabArray() {
        byte[] strs = strTabContent.toString().getBytes();
        return (strs);
    }
}
