/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.jruby.lexer;

import org.jcodings.Encoding;
import org.jruby.RubyArray;
import org.jruby.RubyIO;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.io.ChannelHelper;

import java.io.ByteArrayInputStream;
import java.nio.channels.Channel;

import static org.jruby.api.Create.newString;

/**
 *  Lexer source for ripper when we have all bytes available to us.
 */
public class ByteListLexerSource extends LexerSource {
    private final ByteList completeSource; // The entire source of the file
    private int offset = 0; // Offset into source overall (mri: lex_gets_ptr)

    /**
     * Create our food-source for the lexer.
     * 
     * @param sourceName is the file we are reading
     * @param line starting line number for source (used by eval)
     * @param in the ByteList backing the source we want to lex
     */
    public ByteListLexerSource(String sourceName, int line, ByteList in, RubyArray list) {
        super(sourceName, line, list);
        this.completeSource = in;
    }
    
    @Override
    public Encoding getEncoding() {
        return completeSource.getEncoding();
    }
    
    @Override
    public void setEncoding(Encoding encoding) {
        completeSource.setEncoding(encoding);
        encodeExistingScriptLines(encoding);
    }
    
    
    @Override
    public ByteList gets() {
        int length = completeSource.length();
        if (offset >= length) return null; // At end of source/eof

        int end = completeSource.indexOf('\n', offset) + 1;
        if (end == 0) end = length;

        ByteList line = completeSource.makeShared(offset, end - offset);
        offset = end;


        if (scriptLines != null) {
            var context = scriptLines.getRuntime().getCurrentContext();
            scriptLines.append(context, newString(context, line));
        }

        return line;
    }

    @Override
    public int getOffset() {
        return offset;
    }

    @Override
    public Channel getRemainingAsChannel() {
        ByteArrayInputStream bais = new ByteArrayInputStream(completeSource.unsafeBytes(), completeSource.begin(), completeSource.realSize());
        bais.skip(offset);
        return ChannelHelper.readableChannel(bais);
    }

    @Override
    public IRubyObject getRemainingAsIO() {
        if (scriptLines == null) return null;
        return new RubyIO(scriptLines.getRuntime(), getRemainingAsChannel());
    }
}
