package MooseX::Cache::Meta::Instance;
use Moose;
use Storable qw(dclone);
use Data::UUID;

extends 'Moose::Meta::Instance';

{

    sub generate_oid {
        Data::UUID->new()->create_str();
    }

    has cache => (
        isa     => 'Cache::BaseCache',
        is      => 'ro',
        lazy    => 1,
        weaken  => 1,
        default => sub { $_[0]->associated_metaclass->get_cache_handler },
        handles => {
            cache_set => 'set',
            cache_get => 'get',
        }
    );

    sub create_instance {
        my $self = shift;
        my $oid  = $self->generate_oid;

        $self->cache_set( $oid, {} );

        $self->bless_instance_structure(
            {
                oid      => $oid,
                instance => $self->cache_get($oid)
            }
        );
    }

    sub find_instance {
        my ( $self, $oid ) = @_;
        my $instance = $self->cache_get($oid);
        $self->bless_instance_structure(
            {
                oid      => $oid,
                instance => $instance
            }
        );
    }

    sub clone_instance {
        my ( $self, $instance ) = @_;

        my $class = $self->{meta}->name;
        my $oid   = $self->generate_oid;

        my $clone = dclone( $instance->{instance} );

        $self->bless_instance_structure(
            {
                oid      => $oid,
                instance => $clone
            }
        );
    }
}

sub get_instance_oid {
    my ( $self, $instance ) = @_;
    $instance->{oid};
}

sub get_slot_value {
    my ( $self, $instance, $slot_name ) = @_;
    $instance->{instance} = $self->cache_get( $instance->{oid} );
    return $instance->{instance}->{$slot_name};
}

sub set_slot_value {
    my ( $self, $instance, $slot_name, $value ) = @_;
    $instance->{instance}->{$slot_name} = $value;
    $self->cache_set( $instance->{oid}, $instance->{instance} );
}

sub is_slot_initialized {
    my ( $self, $instance, $slot_name, $value ) = @_;
    $instance->{instance} = $self->cache_get( $instance->{oid} );
    exists $instance->{instance}->{$slot_name} ? 1 : 0;
}

sub weaken_slot_value {
    confess "No clue how to do this with Cache::Cache";
}

sub inline_slot_access {
    my ( $self, $instance, $slot_name ) = @_;
    sprintf "%s->{instance}->{%s}", $instance, $slot_name;
}

sub inline_cache_get {
    my ( $self, $instance ) = @_;
    sprintf
      "%s->{instance} = %s->meta->get_meta_instance->cache_get(%s->{oid}) ",
      $instance, $instance, $instance;
}

sub inline_cache_set {
    my ( $self, $instance ) = @_;
    sprintf
      "%s->meta->get_meta_instance->cache_set(%s->{oid}, %s->{instance}) ",
      $instance, $instance, $instance;
}

sub inline_get_slot_value {
    my ( $self, $instance, $slot_name ) = @_;

    sprintf "do{ %s; %s }" => (
        $self->inline_cache_get($instance),
        $self->inline_slot_access( $instance, $slot_name )
    );
}

sub inline_set_slot_value {
    my ( $self, $instance, $slot_name, $value ) = @_;
    return sprintf "%s; %s = $value; %s" => (
        $self->inline_cache_get($instance),
        $self->inline_slot_access( $instance, $slot_name ),
        $self->inline_cache_set($instance)
    );
}

no Moose;
1;
__END__
