using System;

namespace bigloo
{
  public class input_procedure_port: input_port
  {
    public procedure _in;

    public input_procedure_port( procedure  p )
      : base( "[procedure]", foreign.default_io_bufsiz  )
    {
      _in= p;
    }

    public override bool rgc_charready()
    {
      return true;
    }

    public override void close()
    {
      eof= true;
      //other_eof= true; // ??? FIXME
    }

    public override bool rgc_fill_buffer()
    {
      int bufsize = this.bufsiz;
      int abufsize = this.abufsiz;
      int matchstart = this.matchstart;
      byte[] buffer = this.buffer;
      Object value = foreign.eval_funcall_0( _in );
      int value_len = 0;

      if (value is byte[]) 
      {
        value_len = ((byte[])value).Length;
      } 
      else 
      {
        if (value is bchar ) 
        {
          value_len = 1;
        } 
        else 
        {
          if (foreign.EOF_OBJECTP( value ) ||
            (value == bigloo.foreign.BFALSE)) 
          {
            eof = true;
            return false;
          } 
          else 
          {
            foreign.fail( "input-procedure-port",
              "Procedure result must be a string, or a char, or #f, or the eof-object",
              this );
          }
        }
      }

      int already_used = (abufsize - matchstart);

      // first let's see if the buffer is big enough
      rgc_enlarge_buffer_size(value_len + already_used);

      bufsize = this.bufsiz;

      // do we need to shift?
      if ((abufsize + value_len) > bufsiz) 
      {
        // we shift the buffer left
        for ( int i = 0 ; i < already_used ; ++i )
          buffer[i] = buffer[matchstart+i];

        abufsize -= matchstart;
        this.matchstart = 0;
        this.matchstop -= matchstart;
        this.forward -= matchstart;
        this.lastchar = buffer[matchstart-1];
      }

      // finally we insert our object
      if (value is byte[]) 
      {
        for (int i = 0; i < value_len; i++) 
        {
          buffer[i + abufsize - 1] = ((byte[])value)[i];
        }
      } 
      else 
      {
        if (value is bchar) 
        {
          buffer[abufsize - 1] = ((bchar)value).value;
        }
      }
      
      this.abufsiz = abufsize + value_len;

      return true;
    }
  }
}
