/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package malik.emulator.fileformats.text.mapped;

import java.io.*;
import malik.emulator.fileformats.*;

public final class ManifestDecoder extends CustomKeyValueList implements AttributedTextDecoder
{
    private static int skipLineEndings(byte[] stream, int offset) {
        int i = offset;
        for(int c; (c = stream[i]) == '\n' || c == '\r'; i += c == '\r' && stream[i + 1] == '\n' ? 2 : 1);
        return i;
    }

    private String parsed;
    private final Object monitor;

    public ManifestDecoder() {
        this.monitor = new Object();
    }

    public void loadFromInputStream(InputStream stream) throws IOException {
        int size;
        byte[] s;
        stream.read(s = new byte[(size = stream.available()) + 1], 0, size);
        synchronized(monitor)
        {
            clear();
            try
            {
                String key;
                String value;
                for(int i = size >= 3 && s[0] == (byte) 0xef && s[1] == (byte) 0xbb && s[2] == (byte) 0xbf ? 3 : 0; s[i = skipLineEndings(s, i)] != 0 && i < size; set(key, value))
                {
                    if(s[i = parseKey(s, i)] != ':')
                    {
                        throw new InvalidManifestFileFormatException("ManifestDecoder.loadFromInputStream: неправильный формат файла MANIFEST.MF.");
                    }
                    key = parsed.intern();
                    i = parseValue(s, i + 1);
                    value = parsed.intern();
                }
            }
            finally
            {
                parsed = null;
            }
        }
    }

    public void loadFromInputStream(InputStream stream, String encoding) throws IOException {
        loadFromInputStream(stream);
    }

    public void loadFromDataStream(ExtendedDataInputStream stream) throws IOException {
        loadFromInputStream(stream);
    }

    public void loadFromDataStream(ExtendedDataInputStream stream, String encoding) throws IOException {
        loadFromInputStream(stream);
    }

    public String[] getAttributes(String key) {
        int i;
        int j;
        int len;
        int index;
        String[] result;
        String value;
        if((value = get(key)) == null) return null;
        if((value = value.trim()).length() <= 0) return new String[0];
        for(len = 1, j = 0; (j = value.indexOf(',', j)) >= 0; j++) len++;
        for(result = new String[len], i = j = index = 0; (j = value.indexOf(',', j)) >= 0; i = ++j) result[index++] = value.substring(i, j).trim().intern();
        result[index] = value.substring(i).trim().intern();
        return result;
    }

    private int parseKey(byte[] stream, int offset) throws InvalidManifestFileFormatException {
        int c;
        int i = offset;
        if(((c = stream[i++]) < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
        {
            throw new InvalidManifestFileFormatException("ManifestFile.loadFromInputStream: неправильный формат файла MANIFEST.MF.");
        }
        for(; ((c = stream[i]) >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '_'; i++);
        parsed = new String(stream, offset, i - offset);
        return i;
    }

    private int parseValue(byte[] stream, int offset) {
        int c;
        int i = offset;
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        if(stream[i] == ' ') offset = ++i;
        for(; ; )
        {
            for(; (c = stream[i]) != 0 && c != '\r' && c != '\n'; i++);
            result.write(stream, offset, i - offset);
            if(c == 0) break;
            i += c == '\r' && stream[i + 1] == '\n' ? 2 : 1;
            if((c = stream[i]) != ' ') break;
            offset = ++i;
        }
        parsed = result.toString();
        return i;
    }
}
