#!/usr/bin/perl

=head1 NAME

CSSJ::MainOutputTie - 本体キャプチャのためのハンドルトラップ

=head2 概要

本体をキャプチャしてサーバーへ送るため、ハンドルへの操作をトラップするタイです。
詳細は、Perlのtie関数およびTIEHANDLEについての解説を参照してください。

通常、プログラマがこのパッケージを直接使う必要はありません。

=head2 作者

$Date: 2006/03/30 08:35:00 $ MIYABE Tatsuhiko

=cut
package CSSJ::MainOutputTie;

use strict;
use CSSJ::Helpers;
use CSSJ::CTIP;
use CSSJ::Builder;

sub TIEHANDLE ($**$$$$) {
  my $class = shift;
  my $fp = shift;
  my $out = shift;
  my $errorFunc = shift;
  my $progressFunc = shift;
  my $contentLengthFunc = shift;
  my $ignoreHeader = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  $out = Symbol::qualify_to_ref($out, caller());
  my $self = {
    'FP' => $fp,
    'builder' => new CSSJ::Builder($fp, $out, $errorFunc, $progressFunc, $contentLengthFunc),
    'buffer' => '',
    'header' => $ignoreHeader
  };

  bless $self, $class;
}

sub send_buffer {
  my $self = shift;
  my $flush = shift;
  
  my $builder = $self->{builder};

  my $fp = $self->{FP};
  for (;;) {
  	my $bufferLength = length($self->{buffer});
    if (!$flush && $bufferLength < CSSJ::Helpers::BUFFER_SIZE) {
      last;
    }
    my $buff = substr($self->{buffer}, 0, CSSJ::Helpers::BUFFER_SIZE);
    my $len = length($buff);
    if (length($buff) <= 0) {
      last;
    }
    $self->{buffer} = substr($self->{buffer}, $len, $bufferLength - $len);
    my $packet = pack('NC', $len + 1, CSSJ::CTIP::REQ_DATA).$buff;
    $len = length($packet);
    my ($rin,$win,$ein) = ('','','');
    my $fn = fileno($fp);
    vec($rin, $fn, 1) = 1;
    vec($win, $fn, 1) = 1;
    $ein = $rin | $win;
    for (;;) {
      my ($rout,$wout,$eout);
      my $status = select($rout = $rin, $wout = $win, $eout = $ein, undef);
      defined($status) or return undef;
      if ($len > 0 && vec($wout, $fn, 1)) {
        my $rlen = CSSJ::Helpers::write($fp, $packet);
        defined($rlen) or return undef;
      	$packet = substr($packet, 0, $rlen);
        $len -= $rlen;
      }
      if (vec($rout, $fn, 1)) {
        defined($builder->next()) or return undef;
      }
	  if ($len <= 0) {
        last;
      }
    }
  }
  return 1;
}

sub PRINT {
    my $self = shift;
    my $data = shift;
    
    if ($self->{header}) {
    	my $len = length($data);
		for (my $i = 0; $i < $len; $i++) {
		    my $c = substr($data, $i, 1);
		    if ($c eq "\n") {
		    	if ($self->{header} == 1) {
		    		$self->{header} = 2;
		    	}
		    	elsif ($self->{header} == 2) {
		    		$self->{header} = 0;
		    		$data = substr($data, $i + 1);
		    		last;
		    	}
		    }
		    elsif ($c ne "\n") {
		    	$self->{header} = 1;
		    }
		}
		if ($self->{header}) {
			return;
		}
    }
	$self->{buffer} .= $data;
	$self->send_buffer(0);
}

sub PRINTF {
    my $self = shift;
    my $format = shift;
    
    my $data = sprintf($format, @_);
    return $self->PRINT($data);
}

sub WRITE {
    my $self = shift;
    my($buf,$len,$offset) = @_;
    return $self->PRINT($buf, $offset, $len);
}

sub BINMODE {
	# ignore
}

sub FILENO {
	return undef;
}

sub CLOSE {
	my $self = shift;
	
	$self->send_buffer(1) or return undef;
	CSSJ::CTIP::req_end($self->{FP}) or return undef;
	return $self->{builder};
}

return 1;