# -*- coding: utf-8 -*-
# Copyright (C) 2011-2013 Rocky Bernstein <rocky@cpan.org>
# FIXME: Could combine manager code from breakpoints and display
use strict; use warnings; no warnings 'redefine';
use English qw( -no_match_vars );

use Class::Struct;
use strict;

struct DBDisplay => {
    number      => '$', # display number
    enabled     => '$', # True if display is enabled
    arg         => '$', # What to display
    fmt         => '$', # Format to use in display
    return_type => '$'  # Kind of value expected in return
};

package DBDisplay;
sub inspect($)
{
    my $self = shift;
    sprintf("number %s, enabled: %d, fmt: %s, arg: %s",
            $self->number, $self->enabled, $self->fmt, $self->arg);
};


package Devel::Trepan::DB::DisplayMgr;

sub new($$)
{
    my ($class,$dbgr) = @_;
    my $self = {max => 0};
    $self->{dbgr} = $dbgr;
    bless $self, $class;
    $self->clear();
    $self;
}

sub clear($)
{
    my $self = shift;
    $self->{list} = [];
    $self->{next_id} = 1;
}

sub inspect($)
{
    my $self = shift;
    my $str = '';
    for my $display (@{$self->{list}}) {
        next unless defined $display;
        $str .= $display->inspect . "\n";
    }
    $str;
}

# Remove all breakpoints that we have recorded
sub DESTROY() {
    my $self = shift;
    for my $disp (@{$self->{list}}) {
        $self->delete_by_display($disp) if defined($disp);
    }
    $self->{clear};
}

sub find($$)
{
    my ($self, $index) = @_;
    for my $display (@{$self->{list}}) {
        next unless $display;
        return $display if $display->number eq $index;
    }
    return undef;
}

sub delete($$)
{
    my ($self, $index) = @_;
    my $display = $self->find($index);
    if (defined ($display)) {
        $self->delete_by_display($display);
        return $display;
    } else {
        return undef;
    }
}

sub delete_by_display($$)
{
    my ($self, $delete_display) = @_;
    for my $candidate (@{$self->{list}}) {
        next unless defined $candidate;
        if ($candidate eq $delete_display) {
            $candidate = undef;
            return $delete_display;
        }
    }
    return undef;
}

sub add($$;$)
{
    my ($self, $display,$return_type) = @_;
    $self->{max}++;
    $return_type = '$' unless defined $return_type;
    my $number = $self->{max};
    my $disp = DBDisplay->new(
        number      => $number,
        enabled     => 1,
        arg         => $display,
        fmt         => '$',
        return_type => $return_type);
    push @{$self->{list}}, $disp;
    return $disp;
}

sub compact($)
{
    my $self = shift;
    my @new_list = ();
    for my $display (@{$self->{list}}) {
        next unless defined $display;
        push @new_list, $display;
    }
    $self->{list} = \@new_list;
    return $self->{list};
}

sub is_empty($)
{
    my $self = shift;
    $self->compact();
    return scalar(0 == @{$self->{list}});
}

sub find($$)
{
    my ($self, $num) = @_;
    for my $display (@{$self->{list}}) {
        return $display if $display->number == $num;
    }
    return undef;
}

sub max($)
{
    my $self = shift;
    my $max = 0;
    for my $display (@{$self->{list}}) {
        $max = $display->number if $display->number > $max;
    }
    return $max;
}

sub size($)
{
    my $self = shift;
    $self->compact();
    return scalar @{$self->{list}};
}

sub reset($)
{
    my $self = shift;
    $self->{list} = [];
}


unless (caller) {

    sub display_status($$)
    {
        my ($displays, $i) = @_;
        printf "list size: %s\n", $displays->size();
        my $max = $displays->max();
        $max = -1 unless defined $max;
        printf "max: %d\n", $max;
        print $displays->inspect();
        print "--- ${i} ---\n";
    }

    eval "use rlib '..';";
    require Devel::Trepan::Core;
    my $dbgr = Devel::Trepan::Core->new;
    my $displays = Devel::Trepan::DB::DisplayMgr->new($dbgr);
    display_status($displays, 0);
    my $display1 = DBDisplay->new(
        number=>1, enabled => 1, arg => '$dbgr', fmt => '$'
    );

    $displays->add($display1);
    display_status($displays, 1);

    my $display2 = DBDisplay->new(
        number=>2, enabled => 0, arg => '$displays', fmt => '$'
    );
    $displays->add($display2);
    display_status($displays, 2);

    $displays->delete_by_display($display1);
    display_status($displays, 3);

    my $display3 = DBDisplay->new(
       number=>3, enabled => 1, arg => 'foo', fmt => '$'
    );
    $displays->add($display3);
    display_status($displays, 4);

}

1;