# -*- coding: utf-8 -*-
# Copyright (C) 2011 Rocky Bernstein <rocky@cpan.org>

# Simulate I/O using lists of strings.
use rlib '../../..';

use rlib '../../..';
package Devel::Trepan::IO::StringArrayInput;
use warnings; use strict;

use Devel::Trepan::IO;

use vars qw(@ISA);
@ISA = qw(Devel::Trepan::IO::InputBase);

# Simulate I/O using an array of strings. Sort of like StringIO, but
# even simplier.

sub new($$;$)
{
    my ($class, $inp, $opts) = @_;
    $opts = {} unless defined $opts;
    my $self = Devel::Trepan::IO::InputBase->new($inp, $opts);
    $self->{closed} = 0;
    bless $self, $class;
    $self
}

# this close() interface is defined for class compatibility
sub close($) 
{
    my $self = shift;
    $self->{closed} = 1;
}

sub is_closed($) 
{
    my $self = shift;
    $self->{closed};
}

sub is_eof($) 
{
    my $self = shift;
    $self->{closed} || !@{$self->{input}};
}

# Nothing to do here. Interface is for compatibility
sub flush($) { ; }

# Read a line of input. undef is returned on EOF.  
# Note that we don't support prompting.
sub readline($)
{
    my $self = shift;
    return undef if $self->is_eof;
    unless (@{$self->{input}}) {
        return undef;
    }
    my $line = shift @{$self->{input}};
    return $line ;
  }

sub have_term_readline($) 
{
    return 0;
}

#   class << self
#     # Use this to set where to read from.
#     sub open(inp, opts={})
#       if inp.is_a?(Array)
#         return self.new(inp)
#       else
#         raise IOError, "Invalid input type (%s) for %s" % [inp.class, inp]
#       }
#     }
#   }
# }

# Simulate I/O using an array of strings. Sort of like StringIO, but
# even simplier.
package Devel::Trepan::IO::StringArrayOutput;
use vars qw(@ISA);
@ISA = qw(Devel::Trepan::IO::OutputBase);

sub new
{
    my ($class, $out, $opts) = @_;
    $out = []  unless defined $out; 
    $opts = {} unless defined $opts;
    my $self = Devel::Trepan::IO::OutputBase->new($out, $opts);
    $self->{closed} = 0;
    bless $self, $class;
    return $self;
}

# Nothing to do here. Interface is for compatibility
sub close($)
{
    my $self = shift;
    $self->{closed} = 1;
}

sub is_closed($)
{
    my $self = shift;
    $self->{closed};
  }

sub is_eof()
{
    my $self = shift;
    $self->{close} || !$self->{output};
}

# Nothing to do here. Interface is for compatibility
sub flush() { ; }

# This method the debugger uses to write. In contrast to
# writeline, no newline is added to the } to `str'.
#
sub write($$)
{
    my ($self, $msg) = @_;
    return undef if $self->{closed};
    push @{$self->{output}}, $msg;
}
  
# used to write to a debugger that is connected to this
# server; Here, we use the null string '' as an indicator of a
# newline.
sub writeline($$)
{
    my ($self, $msg) = @_;
    $self->write($msg);
    $self->write('');
}

#   class << self
#     # Use this to set where to write to. output can be a 
#     # file object or a string. This code raises IOError on error.
#     # 
#     # If another file was previously open upon calling this open,
#     # that will be stacked and will come back into use after
#     # a close_write().
#     sub open(output=[])
#       if output.is_a?(Array)
#         return self.new(output)
#       else
#         raise IOError, ("Invalid output type (%s) for %s" % 
#                         [output.class, output])
#       }
#     }
#   }
# }

# Demo
unless (caller) {
  my $inp = Devel::Trepan::IO::StringArrayInput->new(
      ['Now is the time', 'for all good men']);
  my $line = $inp->readline;
  print $line, "\n";
  $line = $inp->readline;
  print $line, "\n";
  $line = $inp->readline;
  print "That's the end the line\n" unless defined $line;

  my $out = Devel::Trepan::IO::StringArrayOutput->new;
  $out->writeline("Some output");
  $out->writeline('Hello, world!');
  print $out->{output}[0], "\n";
  print $out->{output}[1], "\n";
  print $out->{output}[2], "\n";
#   out.write('Hello');
#   p out.output
#   out.writeline(', again.');
#   p out.output
# #     io.open_write(sys.stdout)
#   out.flush_after_write = true
#   out.write('Last hello')
#   print "Output is closed? #{out.closed?}"
#   out.close
#   p out.output
#   begin
#     out.writeline("You won't see me")
#   rescue
#   }

#   # Closing after already closed is okay
#   out.close
#   print "Output is closed? #{out.closed?}"
#   print "Input is closed? #{inp.closed?}"
#   inp.close
#   print "Input is closed? #{inp.closed?}"
}