package net.educoder.ecsonar.model;

import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.Data;
import net.educoder.ecsonar.protobuf.DbFileSources;
import net.jpountz.lz4.LZ4BlockInputStream;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import static java.lang.String.format;

/**
 * @Author: youys
 * @Date: 2022/9/27
 * @Description:
 */
@Data
public class FileSource {

    private static final String SIZE_LIMIT_EXCEEDED_EXCEPTION_MESSAGE = "Protocol message was too large.  May be malicious.  " +
            "Use CodedInputStream.setSizeLimit() to increase the size limit.";

    private Long id;
    private String projectUuid;
    private String fileUuid;
    private String lineHashes;

    private String srcHash;
    private byte[] binaryData = new byte[0];
    private String dataHash;

    public DbFileSources.Data decodeSourceData(byte[] binaryData) {
        try {
            return decodeRegularSourceData(binaryData);
        } catch (IOException e) {
            throw new IllegalStateException(
                    format("Fail to decompress and deserialize source data [id=%s,fileUuid=%s,projectUuid=%s]", id, fileUuid, projectUuid),
                    e);
        }
    }

    private static DbFileSources.Data decodeRegularSourceData(byte[] binaryData) throws IOException {
        try (LZ4BlockInputStream lz4Input = new LZ4BlockInputStream(new ByteArrayInputStream(binaryData))) {
            return DbFileSources.Data.parseFrom(lz4Input);
        } catch (InvalidProtocolBufferException e) {
            if (SIZE_LIMIT_EXCEEDED_EXCEPTION_MESSAGE.equals(e.getMessage())) {
                return decodeHugeSourceData(binaryData);
            }
            throw e;
        }
    }

    private static DbFileSources.Data decodeHugeSourceData(byte[] binaryData) throws IOException {
        try (LZ4BlockInputStream lz4Input = new LZ4BlockInputStream(new ByteArrayInputStream(binaryData))) {
            CodedInputStream input = CodedInputStream.newInstance(lz4Input);
            input.setSizeLimit(Integer.MAX_VALUE);
            return DbFileSources.Data.parseFrom(input);
        }
    }
}
