package Coro::LocalScalar;
use strict;
no strict 'refs';
use Guard;
use Carp;
use Scalar::Util qw/weaken/;
our $VERSION = '0.1';

=head1 NAME

Coro::LocalScalar - local() for Coro

=head1 ABOUT

Perl local() function unuseful for Coro threads. This module uses tie magick to make scalar local for each Coro thread.

=head1 SYNOPSIS

	use Coro;
	use Coro::LocalScalar;
	use Coro::EV;
	
	my $scalar;
	
	Coro::LocalScalar->new->localize($scalar);
	
	async {
		$scalar = "thread 1";
		print "1 - $scalar\n";
		cede;
		print "3 - $scalar\n";
		cede;
		print "5 - $scalar\n";
		
	};
	
	async {
		$scalar = "thread 2";
		print "2 - $scalar\n";
		cede;
		print "4 - $scalar\n";
		cede;
		print "6 - $scalar\n";
	};
	EV::loop;

prints
	1 - thread 1
	2 - thread 2
	3 - thread 1
	4 - thread 2
	5 - thread 1
	6 - thread 2



	my $obj = Coro::LocalScalar->new;
	
		# no tie magick used
	$obj->value("data");
	$obj->value = "data"; 
	my $value = $obj->value;
	
	#or
	
	my $local_lvalue_closure = $obj->closure; # lvalue coderef
	
	$local_lvalue_closure->() = "local data"; # no tie magick used
	

	
	my $testobj = Someclass->new;
	
	# attach setter/getter and tied hash element to your object
	$obj->attach($testobj, 'element_local_in_coros');
	
	$testobj->element_local_in_coros("data");
	$testobj->element_local_in_coros = "data";
	
	$testobj->{element_local_in_coros}; # tie magick used


=cut





sub new { bless {} } 
sub TIESCALAR {$_[1]};

sub closure { 
	my $self = shift;
	
	return sub :lvalue {
		$self->get;
	}
}

sub localize { # tie scalar to container
	my $self = shift;
	
	$self->{scalar} = \$_[0];
	weaken $self->{scalar}; # no circular
	
	tie($_[0], __PACKAGE__, $self );
	$self;
}

sub attach { # attach setter/getter and tied hash element to class
	# $container->attach($comeobj, 'db_conn'); $comeobj->{db_conn} and $comeobj->db_conn now local in Coros
	*{ref($_[1]).'::'.$_[2]} = $_[0]->closure;
	
	$_[1]->{$_[2]} = undef unless exists $_[1]->{$_[2]};
	$_[0]->localize($_[1]->{$_[2]});
	
	$_[0];
}




sub _proto : lvalue {
	my $self = shift;
	
	unless(exists($self->{$Coro::current})){
		my $coro = $Coro::current;
		
		$self->{$coro} = undef;
		
		# init coro data destructor
			$coro->on_destroy(sub {
				
				${ $self->{scalar} } = undef; # Delete scalar value to prevent unexpected behavior
				#perl stores values in scalar even if it tied. When Coro::LocalScalar destroys internal value, value stored in scalar still persists although you expected that it would be deleted( and DESTROY called if it`s object)
				
				delete $self->{$coro};
				undef $coro; 
			});
		
	};
	if(@_){ $self->{$Coro::current} = shift }
	
	$self->{$Coro::current};
}

*STORE = *_proto;

sub FETCH : lvalue { shift->{$Coro::current} };


our $AUTOLOAD;
sub AUTOLOAD :lvalue { *{$AUTOLOAD} = *_proto; shift->_proto }

sub DESTROY {}
sub UNTIE {};

1;