package org.reasm.m68k.assembly.internal;

import java.io.FileNotFoundException;
import java.io.IOException;

import org.reasm.AssemblyMessage;
import org.reasm.Value;
import org.reasm.m68k.messages.IncbinLengthMustNotBeNegativeErrorMessage;
import org.reasm.m68k.messages.IncbinStartMustNotBeNegativeErrorMessage;
import org.reasm.m68k.messages.ValueOutOfRangeErrorMessage;

/**
 * The <code>INCBIN</code> (a.k.a. <code>BINCLUDE</code>) directive.
 *
 * @author Francis Gagné
 */
class IncbinDirective extends Mnemonic {

    static final IncbinDirective INCBIN = new IncbinDirective();

    private static final CardinalValueVisitor.ErrorFactory START_NEGATIVE_VALUE_ERROR_FACTORY = new CardinalValueVisitor.ErrorFactory() {
        @Override
        public AssemblyMessage createMessage() {
            return new IncbinStartMustNotBeNegativeErrorMessage();
        }
    };

    private static final CardinalValueVisitor.ErrorFactory LENGTH_NEGATIVE_VALUE_ERROR_FACTORY = new CardinalValueVisitor.ErrorFactory() {
        @Override
        public AssemblyMessage createMessage() {
            return new IncbinLengthMustNotBeNegativeErrorMessage();
        }
    };

    private IncbinDirective() {
    }

    @Override
    void assemble(M68KAssemblyContext context) throws IOException {
        if (context.numberOfOperands < 1) {
            context.addWrongNumberOfOperandsErrorMessage();
            return;
        }

        if (context.numberOfOperands > 3) {
            context.addWrongNumberOfOperandsErrorMessage();
        }

        final String filePath = IncludeDirective.getFilePath(context, 0);
        if (filePath != null) {
            final byte[] data = context.builder.getAssembly().fetchBinaryFile(filePath);
            if (data == null) {
                throw new FileNotFoundException(filePath);
            }

            int start = 0;
            int length = data.length;
            if (context.numberOfOperands >= 2) {
                final Value startOperand = evaluateExpressionOperand(context, 1);
                final CardinalValueVisitor startVisitor = context.cardinalValueVisitor;
                startVisitor.reset(start, START_NEGATIVE_VALUE_ERROR_FACTORY);
                Value.accept(startOperand, startVisitor);
                final long startLong = startVisitor.getValue();
                if (startLong < 0 || startLong > Integer.MAX_VALUE) {
                    new ValueOutOfRangeErrorMessage(startLong);
                }

                start = (int) startLong;

                if (context.numberOfOperands >= 3) {
                    final Value lengthOperand = evaluateExpressionOperand(context, 2);
                    final CardinalValueVisitor lengthVisitor = context.cardinalValueVisitor;
                    lengthVisitor.reset(start, LENGTH_NEGATIVE_VALUE_ERROR_FACTORY);
                    Value.accept(lengthOperand, lengthVisitor);
                    final long lengthLong = lengthVisitor.getValue();
                    if (lengthLong < 0 || lengthLong > Integer.MAX_VALUE) {
                        new ValueOutOfRangeErrorMessage(lengthLong);
                    }

                    length = (int) lengthLong;
                }
            }

            if (length != 0) {
                context.builder.appendAssembledData(data, start, length);
            }
        }
    }

}

@@ -2249,33 +2249,33 @@
  startVisitor);%0A
- 
+%0A
                f
@@ -2269,29 +2269,24 @@
             
-final
  long startL
@@ -2350,110 +2350,161 @@
 ong 
-%3C 0 %7C%7C startLong %3E Integer.MAX_VALUE) %7B%0A                    new ValueOutOfRangeErrorMessage(startLong)
+%3E data.length) %7B%0A                    context.addTentativeMessage(new ValueOutOfRangeErrorMessage(startLong));%0A                    startLong = data.length
 ;%0A  
@@ -2560,16 +2560,62 @@
 artLong;
+%0A                length = data.length - start;
 %0A%0A      
@@ -2881,21 +2881,22 @@
 r.reset(
-start
+length
 , LENGTH
@@ -2991,17 +2991,17 @@
 sitor);%0A
- 
+%0A
         
@@ -3011,21 +3011,16 @@
         
-final
  long le
@@ -3095,116 +3095,187 @@
 ong 
-%3C 0 %7C%7C lengthLong %3E Integer.MAX_VALUE) %7B%0A                        new ValueOutOfRangeErrorMessage(lengthLong)
+%3E data.length - start) %7B%0A                        context.addTentativeMessage(new ValueOutOfRangeErrorMessage(lengthLong));%0A                        lengthLong = data.length - start
 ;%0A  

Fix bugs in IncbinDirective