The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package  Graph::ChartSVG::Layer;
use Moose;

has 'data'    => ( isa => 'Graph::ChartSVG::Data',    is => 'rw', required => 0 );
has 'glyph'   => ( isa => 'Graph::ChartSVG::Glyph',   is => 'rw', required => 0 );
has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Data;
use Moose;

has 'data_set'  => ( isa => 'ArrayRef',       is => 'rw', required => 0 );
has 'type'      => ( isa => 'Str',            is => 'rw', required => 0, default => 'line' );
has 'thickness' => ( isa => 'Num',            is => 'rw', required => 0, default => 1 );
has 'color'     => ( isa => 'Str | ArrayRef', is => 'rw', required => 0, default => '00000000' );
has 'opacity'   => ( isa => 'Num',            is => 'rw', required => 0, default => 1 );
has 'max'       => ( isa => 'Int',            is => 'rw', required => 0 );
has 'last'      => ( isa => 'Int',            is => 'rw', required => 0 );
has 'label'     => ( isa => 'Str',            is => 'rw', required => 0 );
has 'offset'    => ( isa => 'Num',            is => 'rw', required => 0, default => 0 );
has 'scale'     => ( isa => 'Num | ArrayRef', is => 'rw', required => 0, default => 1 );

1;

package Graph::ChartSVG::Frame;
use Moose;

has 'type'      => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'color'     => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );

1;

package  Graph::ChartSVG::Glyph;
use Moose;

has 'x'    => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
has 'y'    => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
has 'filled'    => ( isa => 'Bool',     is => 'rw', required => 0 );
has 'color'     => ( isa => 'Str',      is => 'rw', required => 0, default => '00000000' );
has 'anchor'    => ( isa => 'Str',      is => 'rw', required => 0, default => 'start' );
has 'data_set'  => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'thickness' => ( isa => 'Num',      is => 'rw', required => 0, default => 1 );
has 'font'           => ( isa => 'Str', is => 'rw' );
has 'size'           => ( isa => 'Num', is => 'rw' );
has 'font_weight'    => ( isa => 'Str', is => 'rw' );
has 'stretch'        => ( isa => 'Str', is => 'rw' );
has 'letter_spacing' => ( isa => 'Num', is => 'rw' );
has 'word_spacing'   => ( isa => 'Num', is => 'rw' );
has 'label'          => ( isa => 'Str', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Border;
use Moose;

has 'left'   => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'right'  => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'top'    => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'bottom' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );

1;

package  Graph::ChartSVG::Label;
use Moose;

has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
has 'text' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'font' => ( isa => 'Str', is => 'rw' );
has 'font_scaling'       => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
has 'style'              => ( isa => 'Str', is => 'rw', required => 0 );
has 'space'              => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'size'               => ( isa => 'Num', is => 'rw', required => 0, default => 10 );
has 'align'              => ( isa => 'Str', is => 'rw', required => 0, default => 'left' );
has 'rotation'           => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'kerning_correction' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );

1;

package  Graph::ChartSVG::Grid_def;
use Moose;

has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
has 'number'    => ( isa => 'Num',   is => 'rw', required => 1 );
has 'thickness' => ( isa => 'Num',   is => 'rw', required => 0, default => 1 );
has 'label'     => ( isa => 'Graph::ChartSVG::Label', is => 'rw', required => 0 );
has 'label2'    => ( isa => 'Graph::ChartSVG::Label', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Grid;
use Moose;

has 'x'      => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y'      => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y_up'   => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'x_up'   => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'x_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'debord' => ( isa => 'Graph::ChartSVG::Border',   is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );

1;

package  Graph::ChartSVG::Overlay;
use Moose;

has 'type'     => ( isa => 'Str', is => 'rw', required => 0, default => 'v' );
has 'debord_1' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'debord_2' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'data_set' => ( isa => 'HashRef', is => 'rw', required => 0 );
has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );

1;

package Graph::ChartSVG;
use Moose;

use constant PI => 4 * atan2( 1, 1 );

use SVG;
use SVG::Parser qw(SAX=XML::LibXML::SAX::Parser);

use List::Util qw( max  min sum );
# use Math::Complex;
# use Compress::Zlib;
use Hash::Merge qw( merge );
use Data::Serializer;

# use Carp::Clan;
use Carp;
use Data::Dumper;
# use Devel::Size qw(size total_size);
# use Clone qw(clone);

use MIME::Base64;

use vars qw( $VERSION );

$VERSION = '2.06';

has 'active_size' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'total_size'  => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'bg_color'    => ( isa => 'Str',      is => 'rw', required => 0, default => 'ffffffff' );
has 'frame'       => ( isa => 'Graph::ChartSVG::Frame',    is => 'rw', required => 0, default => sub { Graph::ChartSVG::Frame->new } );
has 'grid'        => ( isa => 'Graph::ChartSVG::Grid',     is => 'rw', required => 0 );
#has 'reticle'     => ( isa => 'HashRef',  is => 'rw', required => 0 );
has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay',  is => 'rw', required => 0 );
has 'glyph'   => ( isa => 'ArrayRef', is => 'rw', required => 0 );
#has 'layer'    => ( isa => 'ArrayRef', is => 'rw' ,default => sub { [ Layer->new]} );
has 'layer'   => ( isa => 'ArrayRef[Layer]', is => 'rw' );
has 'image'   => ( isa => 'Str',             is => 'rw' );
has 'svg_raw' => ( isa => 'Str',             is => 'rw' );
has 'border'  => ( isa => 'Graph::ChartSVG::Border',          is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );
has 'tag' => ( isa => 'Bool', is => 'rw', required => 0 );
#has 'tag' => ( isa => 'Tag', is => 'rw', required => 0 );
#has 'tag' =>  ( isa => 'HashRef', is => 'rw', required => 0 );

sub Tag
{
    my $self = shift;
    my $args = shift;
    if ( exists $self->{ tag } )
    {
        $self->{ tag } = merge( $self->{ tag }, $args );
    }
    else
    {
        $self->{ tag } = $args;
    }
    bless $self;
}

sub label
{
    my $self  = shift;
    my $label = shift;
    foreach my $l ( 1 .. ( scalar @{ $self->{ Layer } } ) )
    {

        my $m = $l - 1;

        if (   defined $self->{ Layer }->[$m]
            && exists $self->{ Layer }->[$m]->{ label }
            && $self->{ Layer }->[$m]->{ label } eq $label )
        {
# carp "$l =>  ".Dumper($self->{ Layer }->[$m]->{ data_set });
            return wantarray
              ? ( $self->{ Layer }->[$m]->{ data_set }, $m )
              : $self->{ Layer }->[$m]->{ data_set };
        }
    }
    return ();
}

sub move
{
    my $self = shift;
    my $from = shift;
    my $to   = shift;
    my $type = 'Layer';
    my @tmp;
    if ( exists $self->{ $type } )
    {
        my $elem = splice @{ $self->{ $type } }, $from, 1;
        @tmp = ( @{ $self->{ $type } }[ 0 .. ( $to - 1 ) ], $elem, @{ $self->{ $type } }[ ( $to ) .. $#{ $self->{ $type } } ] );
    }

    $self->{ $type } = \@tmp;
}

sub add
{
    my $self   = shift;
    my $what   = shift;
    my $where  = shift;
    my $insert = shift;

    my $type = 'Layer';
    my @tmp;
    if ( exists $self->{ $type } )
    {
        @tmp = @{ $self->{ $type } };
    }
    if ( defined $where )
    {
        if ( $where >= 0 )
        {
            if ( !exists $what->{ label } )
            {
                $what->{ label } = $where;
            }
            if ( defined $insert )
            {
                my @t = ( @tmp[ 0 .. ( $where - 1 ) ], $what, @tmp[ $where .. $#tmp ] );
                @tmp = @t;
            }
            else
            {
                $tmp[$where] = $what;
            }
        }
        else
        {
            if ( !exists $what->{ label } )
            {
                $what->{ label } = 0;
            }
            unshift @tmp, $what;
        }
    }
    else
    {
        push @tmp, $what;
    }

    $self->{ $type } = \@tmp;
}

sub render
{
    my $self = shift;
    my @tmp = ( $self->active_size->[0] + $self->border->right + $self->border->left, $self->active_size->[1] + $self->border->top + $self->border->bottom );
    $self->total_size( \@tmp );

    my $svg = SVG->new(
        width      => $self->total_size->[0],
        height     => $self->total_size->[1],
        standalone => 'yes',
# border => 1,
    );

    if ( $self->bg_color )
    {
        my $tag = $svg->rectangle(
            x      => 0,
            y      => 0,
            width  => $self->total_size->[0],
            height => $self->total_size->[1],
            fill   => '#' . ( unpack "a6", $self->bg_color ) || 0,
            id     => 'background'
        );
    }

    my $layer_ind = 0;
    my $data_goup = $svg->group(
        id        => "data",
        transform => "matrix(1,0,0,-1," . ( $self->border->left ) . "," . ( $self->border->top + $self->active_size->[1] ) . ")"
    );
#     my $layer_goup = $svg->group(
#         id        => "layer",
#         transform => "matrix(1,0,0,-1," . ( $self->border->left ) . "," . ( $self->border->top + $self->active_size->[1] ) . ")"
#     );

    my @list_data;
    foreach my $layer ( @{ $self->{ Layer } } )
    {
        my $layer_goup;
        push @list_data, "data_$layer_ind";
        if ( ( ref $layer ) eq 'Graph::ChartSVG::Data' )
        {
            if ( defined $layer->{ data_set } )
            {
                my $scale = $layer->{ scale };

                if ( $layer->{ type } =~ /_up$/ )
                {
                    $scale *= 0.5;
                    $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( ( $self->active_size->[1] / 2 ) + $layer->{ offset } ) . " )" );
                }
                elsif ( $layer->{ type } =~ /_down$/ )
                {
                    $scale *= -0.5;
# carp "scale=$scale   act=".$self->active_size->[1]."  off=" .$layer->{ offset };
                    $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( ( ( $self->active_size->[1] / 2 ) - $layer->{ offset } ) ) . " )" );
                }
                else
                {
                    if ( $layer->{ offset } )
                    {
                        $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( $layer->{ offset } ) . " )" );
                    }
                    else
                    {
                        $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0,0)" );
                    }
                }

                if ( ref( $layer->{ data_set }->[0] ) eq 'ARRAY' )
                {

                    my @all_xv;
                    my @all_yv;
                    my @all_style;
                    my $stack_size = scalar @{ $layer->{ data_set } };
                    if ( $layer->{ type } =~ /_stack/ )
                    {
                        if ( $layer->{ type } =~ /line|bar/ )
                        {
                            my $max = 0;
                            for my $stack_idx ( 0 .. $#{ $layer->{ data_set } } )
                            {
                                $max = max( $max, $#{ $layer->{ data_set }->[$stack_idx] } );
                            }

                            for my $stack_idx ( 0 .. $#{ $layer->{ data_set } } )
                            {
                                my @xv;
                                my @yv;
                                my $dot = -1;
                                for my $raw_val_idx ( 0 .. $max )
                                {
                                    my $raw_val = $layer->{ data_set }->[$stack_idx][$raw_val_idx];
                                    $dot++;
                                    last if $dot >= $self->active_size->[0];
                                    my $plot_val = ( $raw_val || 0 );
                                    $plot_val =
                                        $plot_val * $scale > $self->active_size->[1]
                                      ? $self->active_size->[1]
                                      : $plot_val;

                                    $xv[$raw_val_idx] = $dot;
                                    $yv[$raw_val_idx] = sum_array( $stack_idx, $raw_val_idx, $layer->{ data_set } );

                                }
                                my %style;
                                if ( $layer->{ type } =~ /bar/ )
                                {

                                    push @xv, $dot;
                                    push @yv, 0;
                                    push @xv, 0;
                                    push @yv, 0;

                                    %style = (
                                        'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] ) ) / 255 ) || 1,
                                        'stroke' => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
                                        'stroke-width' => $layer->{ thickness },
                                        'fill'         => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
                                        'fill-opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] )[1] ) / 255 ) || 1,
                                        'fill-rule'    => 'nonzero'
                                    );
                                }
                                else
                                {
                                    %style = (
                                        'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] )[1] ) / 255 ) || 1,
                                        'stroke' => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
                                        'stroke-width' => $layer->{ thickness },
                                        'fill'         => '#ff0000',
                                        'fill-opacity' => 0,
                                        'fill-rule'    => 'nonzero'
                                    );
                                }
                                push @all_xv,    \@xv;
                                push @all_yv,    \@yv;
                                push @all_style, \%style;

                            }

                            foreach ( my $idx = $#all_xv ; $idx >= 0 ; $idx-- )
                            {
                                my $points = $layer_goup->get_path(
                                    x       => $all_xv[$idx],
                                    y       => $all_yv[$idx],
                                    -type   => 'polyline',
                                    -closed => 'true'           #specify that the polyline is NOT closed.
                                );

                                my $id_data;
                                if ( exists $layer->{ label } )
                                {
                                    $id_data = $layer->{ label } . '_' . $idx;
                                }
                                else
                                {
                                    $id_data = 'layerdata_' . $layer_ind . '_' . $idx;
                                }
                                my $tag = $layer_goup->polyline(
                                    %$points,
                                    id    => $id_data,
                                    style => $all_style[$idx]
                                );
                            }
                        }
                    }
                    else
                    {
                        carp( "if data_set not arayref_of_arrayref it should be stack type " );
                    }
                }
                else
                {
                    if ( $layer->{ type } =~ /line|bar/ )
                    {
                        my @xv;
                        my @yv;
                        my $dot = -1;

                        foreach my $raw_val ( @{ $layer->{ data_set } } )
                        {
                            $dot++;

                            last if $dot >= $self->active_size->[0];
                            my $plot_val = ( $raw_val || 0 );
                            $plot_val =
                                $plot_val * $scale > $self->active_size->[1]
                              ? $self->active_size->[1]
                              : $plot_val;

                            push @xv, $dot;
                            push @yv, $plot_val;
                        }
                        my %style;
                        if ( $layer->{ type } =~ /bar/ )
                        {

                            push @xv, $dot;
                            push @yv, 0;
                            push @xv, 0;
                            push @yv, 0;

                            %style = (
                                'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
                                'stroke' => '#' . ( unpack "a6", $layer->{ color } ) || 0,
                                'stroke-width' => $layer->{ thickness },
                                'fill'         => '#' . ( unpack "a6", $layer->{ color } ) || 0,
                                'fill-opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
                                'fill-rule'    => 'nonzero'
                            );
                        }
                        else
                        {
                            %style = (
                                'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
                                'stroke' => '#' . ( unpack "a6", $layer->{ color } ) || 0,
                                'stroke-width' => $layer->{ thickness },
                                'fill'         => '#ff0000',
                                'fill-opacity' => 0,
                                'fill-rule'    => 'nonzero'
                            );
                        }
                        my $points = $layer_goup->get_path(
                            x       => \@xv,
                            y       => \@yv,
                            -type   => 'polyline',
                            -closed => 'true'        #specify that the polyline is NOT closed.
                        );

                        my $id_data;
                        if ( exists $layer->{ label } )
                        {
                            $id_data = $layer->{ label };
                        }
                        else
                        {
                            $id_data = 'layerdata_' . $layer_ind;
                        }
                        my $tag = $layer_goup->polyline(
                            %$points,
                            id    => $id_data,
                            style => \%style
                        );
                    }
                }
            }
        }

#######################################
################ Glyph ################
#######################################
        if ( ( ref $layer ) eq 'Graph::ChartSVG::Glyph' )
        {
            $layer_goup = $data_goup->group( id => "data_$layer_ind" );
            my $X = 0;
            my $Y = 0;
            # if ( $layer->{ x } eq 'active_min' )
            # {
# #                  $X += $self->border->left;
            # }
            # elsif ( $layer->{ x } eq 'active_max' )
            # {
                # $X += $self->active_size->[0];
            # }
            # else
            # {
                # if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
                # {
                    # $X = $layer->{ x };
                # }
                # else
                # {
                    # $X += $layer->{ x } - $self->border->left;
                # }
            # }
            # if ( $layer->{ y } eq 'active_max' )
            # {
                # $Y += $self->active_size->[1];
            # }
            # elsif ( $layer->{ y } eq 'active_min' )
            # {
# #                  $Y += $self->border->bottom;
            # }
            # else
            # {
                # if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
                # {
                    # $Y = $layer->y;
                # }
                # else
                # {
                    # $Y += $layer->y - $self->border->bottom;
                # }
            # }

                if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
                { 
                    $X = $layer->{ x };
                    $Y = $layer->y;
                }
                else
                {
                     $X += $layer->{ x } - $self->border->left;
                    $Y += $layer->y - $self->border->bottom;
                }

            if ( exists $layer->{ type } && $layer->{ type } eq 'text' )
            {
                foreach my $set ( @{ $layer->data_set } )
                {
                    my $text_angle  = exists( $set->{ rotation } ) && $set->{ rotation } || 0;
                    my $font_style  = 'normal';
                    my $font_weight = $set->{ font_weight } || $layer->font_weight || 'normal';

                    my $f_style = $set->{ style } || $layer->style;
                    if ( $f_style =~ /(italic)|(oblique)/ )
                    {
                        $font_style = 'italic';
                    }
                    if ( $f_style =~ /bold/ )
                    {
                        $font_weight = 'bold';
                    }

                    my $letter_spacing = $set->{ letter_spacing } || $layer->letter_spacing || 'normal';
                    my $word_spacing   = $set->{ word_spacing }   || $layer->word_spacing   || 'normal';
                    my $font_stretch   = $set->{ stretch }        || $layer->stretch        || 'normal';

                    my $txt = $layer_goup->text(
                        x => $set->{ x }  || 0,
                        y => -$set->{ y } || 0,
                        style => {
                            'font-family' => $set->{ font } || $layer->font,
                            'font-size'   => $set->{ size } || $layer->size,
                            'font-style'  => $font_style,
                            'font-weight' => $font_weight,
                            'font-stretch'   => $font_stretch,
                            'letter-spacing' => $letter_spacing,
                            'word-spacing'   => $word_spacing,
                            'fill'           => '#' . ( $set->{ color } || $layer->color || 'ffffff' ),
                            'stroke'         => '#' . ( $set->{ stroke } || $set->{ color } || $layer->color || '000000' ),
                            'writing-mode'   => 'lr',
                            'text-anchor' => $set->{ anchor } || $layer->anchor
                        },
                        transform => "matrix(1,0,0,-1," . ( $X ) . "," . ( $Y ) . ") rotate($text_angle)"
                    );
                    $txt->tspan( dy => "0" )->cdata( $set->{ text } );

                }
            }
            elsif ( exists $layer->{ type } && $layer->{ type } eq 'line' )
            {
                my $ind = 0;
                foreach my $set ( @{ $layer->data_set } )
                {
                    my @xv;
                    my @yv;
                    my $dot = -1;

                    foreach my $point ( @{ $set->{ data } } )
                    {
                        next unless ( ref $point eq 'ARRAY' );
                        push @xv, $point->[0] + $X;
                        push @yv, $point->[1] + $Y;
                        $dot++;
                    }
                    my %style;
                    my $color_hex = $set->{ color } || $layer->{ color };
                    if ( exists $layer->{ filled } && $layer->{ filled } == 1 )
                    {
                        push @xv, $xv[0];
                        push @yv, $yv[0];
                        %style = (
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        );
                    }
                    else
                    {
                        %style = (
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 )
                              || 1,
                            'stroke' => '#' . ( unpack "a6", $color_hex )
                              || 0,
                            'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
#                         'fill'         => '#ff0000',
                            'fill-opacity' => 0,
                            'fill-rule'    => 'nonzero'
                        );
                    }
                    my $points = $layer_goup->get_path(
                        x       => \@xv,
                        y       => \@yv,
                        -type   => 'polyline',
                        -closed => 'true',
                    );
                    my $id_data;
                    if ( exists $layer->{ label } )
                    {
                        $id_data = $layer->{ label };
                    }
                    else
                    {
                        $id_data = 'glyph_' . $layer_ind . '_' . $ind;
                    }
                    my $tag = $layer_goup->polyline(
                        %$points,
                        id    => $id_data,
                        style => \%style,
                    );
                    $ind++;
                }
            }
            elsif ( exists $layer->{ type } && $layer->{ type } eq 'ellipse' )
            {
                my $ind = 0;
                foreach my $set ( @{ $layer->data_set } )
                {
                    my $cx = ( $set->{ cx } + $X ) || 0;
                    my $cy = ( $set->{ cy } )      || 0;
                    my $rx = $set->{ rx }          || 0;
                    my $ry = $set->{ ry }          || 0;
                    my %style;

                    my $color_hex = $set->{ color } || $layer->{ color };
                    if ( exists $layer->{ filled } && $layer->{ filled } == 1 )
                    {
                        %style = (
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        );
                    }
                    else
                    {
                        %style = (
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 )
                              || 1,
                            'stroke' => '#' . ( unpack "a6", $color_hex )
                              || 0,
                            'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
#                        'fill'         => '#ff0000',
                            'fill-opacity' => 0,
                            'fill-rule'    => 'nonzero'
                        );
                    }

                    my $tag = $layer_goup->ellipse(
                        cx    => $cx,
                        cy    => $cy,
                        rx    => $rx,
                        ry    => $ry,
                        id    => 'ellipse_' . $layer_ind . '_' . $ind,
                        style => \%style,
#                         transform => "matrix(1,0,0,-1," . $self->border->left . "," . ( $self->total_size->[1] - $Y - $self->border->bottom ) . ")",
                    );
                    $ind++;
                }
            }
            elsif ( exists $layer->{ type } && $layer->{ type } eq 'image' )
            {
                my $image_nbr = 1;
                foreach my $set ( @{ $layer->data_set } )
                {
                    my $raw_img = $set->{ image };
                    my $img64   = encode_base64( $raw_img, "\n" );
                    my $tag     = $svg->image(
                        x => $set->{ x }  || 0,
                        y => -$set->{ y } || 0,
                        width     => $set->{ width },
                        height    => $set->{ height },
                        id        => 'image_' . $layer_ind . '_' . $image_nbr,
                        '-href'   => "data:image/png;base64," . $img64,
                        transform => "matrix(1,0,0,1," . $X . "," . $Y . ")",
                    );
                    $image_nbr++;
                }
# $tag = $svg->image(
# x=>100, y=>100,
# width=>300, height=>200,
# '-href'=>"image.png", #may also embed SVG, e.g. "image.svg"
# id=>'image_1'
# );

            }
        }

#######################################
############## Overlay ################
#######################################
        if ( ( ref $layer ) eq 'Graph::ChartSVG::Overlay' )
        {
            $layer_goup = $data_goup->group( id => "data_$layer_ind" );
            my $ind       = 0;
            my $color_hex = $layer->{ color };
            if ( $layer->{ type } eq 'v' )
            {
                foreach my $start ( keys %{ $layer->{ data_set } } )
                {
                    my $stop = $layer->{ data_set }->{ $start };
                    my $k    = $layer_goup->rectangle(
                        x      => $start,
                        y      => -$layer->{ debord_1 },
                        width  => $stop - $start,
                        height => $self->{ active_size }->[1] + $layer->{ debord_1 } + $layer->{ debord_2 },
                        style  => {

                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        },
                        id => 'v_overlay_' . $layer_ind . '_' . $ind
                    );
                    $ind++;
                }
            }
            if ( $layer->{ type } eq 'h' )
            {
                foreach my $start ( keys %{ $layer->{ data_set } } )
                {
                    my $stop = $layer->{ data_set }->{ $start };
                    my $k    = $layer_goup->rectangle(
                        x      => -$layer->{ debord_1 },
                        y      => $start,
                        width  => $self->{ active_size }->[0] + $layer->{ debord_1 } + $layer->{ debord_2 },
                        height => $stop - $start,
                        style  => {
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        },
                        id => 'h_overlay_' . $layer_ind . '_' . $ind
                    );
                    $ind++;
                }
            }
        }
        $layer_ind++;
    }

    my $info_data_group = $svg->group( id => "info_data" );
    my $obj = Data::Serializer->new( 'compress' => 1 );
    my $tag = $obj->serialize( \@list_data );
    $info_data_group->comment( $tag );

#######################################
## grid
#######################################
    if ( defined $self->grid )
    {
        if ( defined $self->grid->x )
        {
            my $x_grid_group = $svg->group( id => "x_grid" );
            my $x_grid_text = $x_grid_group->group(
                id        => "x_grid_text",
                transform => "matrix(1,0,0,1, 0," . ( $self->border->bottom ) . " )"
            );
            my $thickness = $self->grid->x->thickness || 1;
            my $color_hex = $self->grid->x->color;
            my $max_length_x;
            $max_length_x = max( map( length, @{ $self->grid->x->label->text } ) )
              if ( defined( $self->grid->x->label )
                && ( ref( $self->grid->x->label->text ) eq 'ARRAY' ) );
            my $max_length_x2;
            $max_length_x2 = max( map( length, @{ $self->grid->x->label2->text } ) )
              if ( defined( $self->grid->x->label2 )
                && ( ref( $self->grid->x->label2->text ) eq 'ARRAY' ) );

            for ( my $nbr = $self->grid->x->number - 1 ; $nbr >= 0 ; $nbr-- )
            {
                my $val = $nbr * ( ( ( $self->active_size->[1] ) / ( $self->grid->x->number - 1 ) ) );
                my $text_indx = $self->grid->x->number - $nbr - 1;

                my $tag = $x_grid_group->line(
                    id    => 'x_grid_' . $nbr,
                    x1    => $self->border->left - $self->grid->debord->left,
                    y1    => $self->border->bottom + $val,
                    x2    => $self->border->left + $self->active_size->[0] + $self->grid->debord->right,
                    y2    => $self->border->bottom + $val,
                    style => {
                        'fill'            => 'none',
                        'opacity'         => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                        'stroke'          => '#' . ( unpack "a6", $color_hex ) || 0,
                        'stroke-width'    => $thickness,
                        'stroke-linecap'  => 'butt',
                        'stroke-linejoin' => 'miter',
                        'stroke-opacity'  => 1
                    },
                    transform => "matrix(1,0,0,-1," . ( 0 ) . "," . ( $self->total_size->[1] ) . ")",
                );

                if ( defined $self->grid->x->label && defined $self->grid->x->label->text->[$text_indx] )
                {
                    my $text_color = ( $self->grid->x->label->color || $color_hex );
                    my $radian = ( $self->grid->x->label->rotation / 180 ) * PI || 0;
                    my $cos    = cos( $radian );
                    my $sin    = sin( $radian );
                    my $len    = length( $self->grid->x->label->text->[$text_indx] );

                    my $font_style  = 'normal';
                    my $font_weight = 'normal';
                    my $f_style     = $self->grid->x->label->style || '';
                    if ( $f_style =~ /(italic)|(oblique)/ )
                    {
                        $font_style = 'italic';
                    }
                    if ( $f_style =~ /bold/ )
                    {
                        $font_weight = 'bold';
                    }

                    my $x_offset = 0;
                    my $y_offset = 0;

                    my %style;
                    if ( $self->grid->x->label->align =~ /left/i )
                    {
                        %style = (
                            'font-family'  => $self->grid->x->label->font,
                            'font-size'    => $self->grid->x->label->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'start',
#                             'baseline-shift'=> '-10%',
                        );
                        $x_offset = $self->grid->x->label->size * ( $max_length_x - 1 );
                        if ( $self->grid->x->label->rotation )
                        {
                            $y_offset = ( $sin * $len * $self->grid->x->label->size ) - ( $sin * $self->grid->x->label->size );
                        }
                    }
                    else
                    {
                        %style = (
                            'font-family'  => $self->grid->x->label->font,
                            'font-size'    => $self->grid->x->label->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'end',
#                             'baseline-shift'=> '-10%',
                        );
                    }

                    my $txt = $x_grid_text->text(
                        x     => $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset,
                        y     => $self->border->top - $self->border->bottom + $val + ( $self->grid->x->label->size * 0.3 ) - $y_offset,
                        style => \%style,
# transform => " rotate( " . $self->grid->x->label->rotation . "," . ( $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset ) . "," . ( $self->border->bottom + $val - $y_offset ) . " ) ",
                        transform => " rotate( " . $self->grid->x->label->rotation . "," . ( $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset ) . "," . ( $self->border->bottom + $val - $y_offset ) . " ) ",

                    );
                    $txt->tspan( dy => "0" )->cdata( $self->grid->x->label->text->[$text_indx] );
                }

##########################################
# second x label ( right side )
##########################################
                if ( defined $self->grid->x->label2 && defined $self->grid->x->label2->text->[$text_indx] )
                {
                    my $text_color = ( $self->grid->x->label2->color || $color_hex );
                    my $radian      = ( $self->grid->x->label2->rotation / 180 ) * PI || 0;
                    my $cos         = cos( $radian );
                    my $sin         = sin( $radian );
                    my $len         = length( $self->grid->x->label2->text->[$text_indx] );
                    my $font_style  = 'normal';
                    my $font_weight = 'normal';
                    my $f_style     = $self->grid->x->label2->style || '';

                    if ( $f_style =~ /(italic)|(oblique)/ )
                    {
                        $font_style = 'italic';
                    }
                    if ( $f_style =~ /bold/ )
                    {
                        $font_weight = 'bold';
                    }
                    my $x_offset = 0;
                    my $y_offset = 0;

                    my %style;
                    if ( $self->grid->x->label2->align =~ /right/i )
                    {
                        %style = (
                            'font-family'  => $self->grid->x->label2->font,
                            'font-size'    => $self->grid->x->label2->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'end',
#                             'baseline-shift'=> '-10%',
                        );
                        $x_offset = $self->grid->x->label2->size * ( $max_length_x2 - 1 );
                        if ( $self->grid->x->label2->rotation )
                        {
                            $y_offset = ( $sin * ( $max_length_x2 - 1 ) * $self->grid->x->label2->size ) - ( $sin * $self->grid->x->label2->size );
                        }
                    }
                    else
                    {
                        %style = (
                            'font-family'  => $self->grid->x->label2->font,
                            'font-size'    => $self->grid->x->label2->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'start',
#                             'baseline-shift'=> '-10%',
                        );
                    }
                    my $txt = $x_grid_text->text(
                        x => $self->border->left + $self->grid->debord->right + $self->grid->x->label2->space + $x_offset + $self->active_size->[0],
# y         => $self->border->bottom + $val + ( $self->grid->x->label2->size * 0.3 ) + $y_offset,
                        y => $self->border->top - $self->border->bottom + $val + ( $self->grid->x->label2->size * 0.3 ) - $y_offset,

                        style     => \%style,
                        transform => " rotate( " . $self->grid->x->label2->rotation . "," . ( $self->border->left + $self->grid->debord->right + $self->grid->x->label2->space + $x_offset + $self->active_size->[0] ) . "," . ( $self->border->bottom + $val + $y_offset ) . " ) ",
                    );
                    $txt->tspan( dy => "0" )->cdata( $self->grid->x->label2->text->[$text_indx] );
                }
            }
        }

####################################
## grid Y ( vertical )
####################################
        if ( defined $self->grid->y )
        {
            my $y_grid_group = $svg->group( id => "y_grid", transform => "matrix(1,0,0,-1, 0," . ( $self->total_size->[1] ) . " )" );
            my $y_grid_text = $y_grid_group->group(
                id        => "y_grid_text",
                transform => "matrix(1,0,0,-1, 0," . ( $self->border->bottom ) . " )"
            );
            my $thickness = $self->grid->y->thickness || 1;
            my $color_hex = $self->grid->y->color;
            my $max_length_y;
            $max_length_y = max( map( length, @{ $self->grid->y->label->text } ) )
              if ( defined( $self->grid->y->label )
                && ( ref( $self->grid->y->label->text ) eq 'ARRAY' ) );
            my $max_length_y2;
            $max_length_y2 = max( map( length, @{ $self->grid->y->label2->text } ) )
              if ( defined( $self->grid->y->label2 )
                && ( ref( $self->grid->y->label2->text ) eq 'ARRAY' ) );

            for my $nbr ( 0 .. ( $self->grid->y->number - 1 ) )
            {
                my $val = ( ( $nbr ) * ( $self->active_size->[0] / ( $self->grid->y->number - 1 ) ) );

                my $tag = $y_grid_group->line(
                    id    => 'y_grid_' . $nbr,
                    x1    => $self->border->left + $val,
                    y1    => $self->border->bottom - $self->grid->debord->bottom,
                    x2    => $self->border->left + $val,
                    y2    => $self->border->bottom + $self->active_size->[1] + $self->grid->debord->top,
                    style => {
                        'fill'            => 'none',
                        'opacity'         => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                        'stroke'          => '#' . ( unpack "a6", $color_hex ) || 0,
                        'stroke-width'    => $thickness,
                        'stroke-linecap'  => 'butt',
                        'stroke-linejoin' => 'miter',
                        'stroke-opacity'  => 1
                    },
                );

                if ( defined $self->grid->y->label && defined $self->grid->y->label->text->[$nbr] )
                {
                    my $text_color  = $self->grid->y->label->color || $color_hex;
                    my $font_style  = 'normal';
                    my $font_weight = 'normal';
                    my $f_style     = $self->grid->y->label->style || '';

                    if ( $f_style =~ /(italic)|(oblique)/ )
                    {
                        $font_style = 'italic';
                    }
                    if ( $f_style =~ /bold/ )
                    {
                        $font_weight = 'bold';
                    }

                    my $radian   = ( $self->grid->y->label->rotation / 180 ) * PI || 0;
                    my $cos      = cos( $radian );
                    my $sin      = sin( $radian );
                    my $len      = length( $self->grid->y->label->text->[$nbr] );
                    my $x_offset = 0;
                    my $y_offset = 0;

                    my $l = ( 0.628 * $self->grid->y->label->size * $max_length_y ) - 5.052;
                    my %style;
                    if ( $self->grid->y->label->align =~ /left/i )
                    {
                        %style = (
                            'font-family'  => $self->grid->y->label->font,
                            'font-size'    => $self->grid->y->label->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'end',
#                             'baseline-shift'=> '-10%',
                        );
                        if ( $self->grid->y->label->rotation )
                        {
                            $y_offset = ( $self->grid->debord->bottom + $self->grid->y->label->space ) * 2;
                        }
                    }
                    else
                    {
                        %style = (
                            'font-family'  => $self->grid->y->label->font,
                            'font-size'    => $self->grid->y->label->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'start',
#                             'baseline-shift'=> '-10%',
                        );
                        $x_offset = $self->grid->y->label->size * ( ( 0.7025 * $max_length_y ) - 1.601 ) * $cos;
                        $y_offset = ( $l * $sin ) + ( $self->grid->debord->bottom + $self->grid->y->label->space ) + ( $l / $cos );

                    }

                    my $txt = $y_grid_text->text(
                        x         => $self->border->left + $val - $x_offset,
                        y         => $y_offset,
                        style     => \%style,
                        transform => " rotate( " . $self->grid->y->label->rotation . "," . ( $self->border->left + $val - $x_offset ) . ", " . $y_offset . " ) ",
                    );
                    $txt->tspan( dy => "0" )->cdata( $self->grid->y->label->text->[$nbr] );

                }

                if ( defined $self->grid->y->label2 && defined $self->grid->y->label2->text->[$nbr] )
                {
                    my $text_color  = $self->grid->y->label2->color || $color_hex;
                    my $font_style  = 'normal';
                    my $font_weight = 'normal';
                    my $f_style     = $self->grid->y->label2->style || '';

                    if ( $f_style =~ /(italic)|(oblique)/ )
                    {
                        $font_style = 'italic';
                    }
                    if ( $f_style =~ /bold/ )
                    {
                        $font_weight = 'bold';
                    }

                    my $radian   = ( $self->grid->y->label2->rotation / 180 ) * PI || 0;
                    my $cos      = cos( $radian );
                    my $sin      = sin( $radian );
                    my $len      = length( $self->grid->y->label2->text->[$nbr] );
                    my $x_offset = 0;
                    my $y_offset = 0;
#                     my $l        = ( 0.628 * $self->grid->y->label2->size * $max_length_y2 ) - 5.052;
                    my $l = $self->grid->y->label2->size * $max_length_y2 * $self->grid->y->label2->font_scaling;
                    my %style;

                    if ( $self->grid->y->label2->align =~ /left/i )
                    {
                        %style = (
                            'font-family'  => $self->grid->y->label2->font,
                            'font-size'    => $self->grid->y->label2->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'start',
#                             'baseline-shift'=> '-10%',
                        );
                        if ( $self->grid->y->label2->rotation )
                        {
                            $y_offset = -( $self->active_size->[1] + $self->grid->debord->top + $self->grid->y->label2->space );
                        }
                    }
                    else
                    {
                        %style = (
                            'font-family'  => $self->grid->y->label2->font,
                            'font-size'    => $self->grid->y->label2->size,
                            'font-style'   => $font_style,
                            'font-weight'  => $font_weight,
                            'fill'         => '#' . ( $text_color || 'ffffff' ),
                            'stroke'       => '#' . ( $text_color || '000000' ),
                            'writing-mode' => 'lr',
                            'text-anchor'  => 'end',
#                             'baseline-shift'=> '-10%',
                        );
                        $x_offset = ( $l * $cos ) + $val + $self->border->left;
                        $y_offset = ( $l * $sin ) - $self->grid->debord->bottom - $self->active_size->[1] - $self->grid->debord->top - $self->grid->y->label2->space;
                    }
                    my $txt = $y_grid_text->text(
                        x         => $x_offset,
                        y         => $y_offset,
                        style     => \%style,
                        transform => " rotate( " . $self->grid->y->label2->rotation . "," . ( $x_offset ) . ", " . $y_offset . " ) ",
                    );
                    $txt->tspan( dy => "0" )->cdata( $self->grid->y->label2->text->[$nbr] );
                }
            }
        }
    }

#######################################
####### Frame #######
#######################################
    if ( defined $self->frame )
    {
        my $k = $svg->rectangle(
            x      => $self->{ border }->left,
            y      => $self->{ border }->top,
            width  => $self->{ active_size }->[0],
            height => $self->{ active_size }->[1],
#               rx    => 10, ry     => 5,
            style => {
                stroke => '#' . ( ( $self->frame )->{ color } ),
                fill => 'none',
                'stroke-width' => ( $self->frame )->{ thickness } || 0,
            },
            id => 'frame'
        );
    }

#######################################
####### TAG #######
#######################################
    if ( exists $self->{ tag } && $self->{ tag } )
    {
        my $tag_group = $svg->group( id => "serial_tag" );
        my $obj = Data::Serializer->new( 'compress' => 1 );
        my $tag = $obj->serialize( $self->{ tag } );
        $tag_group->comment( $tag );
    }

    $self->image(
        $svg->xmlify(
            -namespace  => "svg",
            -pubid      => "-//W3C//DTD SVG 1.0//EN",
            -standalone => "no",
            -inline     => 1
        )
    );
    $self->{ svg_raw } = $svg;
}

sub sum_array
{
    my $col  = shift;
    my $line = shift;
    my $data = shift;
    my $res  = 0;
    for my $c_idx ( 0 .. $col )
    {
        $res += $data->[$c_idx][$line] || 0;
    }
    return $res;
}

sub reduce
{
    my $self = shift;
    my %object_hash = ( @_ );
    
    my $object      = \%object_hash;

    my $width_out   = $self->{ active_size }->[0];
    my $start            = $object->{ start }      || 0;
    my $percentile_value = $object->{ percentile } || 0.95;
    my $end              = $object->{ end }        || $width_out;
    my @data_in          = @{ $object->{ data } };
    my $data_in_size     = scalar @data_in;
 
    no warnings "all";
    my @perc = sort { $a <=> $b } @data_in[ $start .. $end ];
    my $prec_ind = int( scalar( @perc ) * $percentile_value );
    my @data_out;
    my %STATS;
    $STATS{ perc } = $perc[$prec_ind] || 0;
    $STATS{ min } = ( min @data_in ) || 0;
    $STATS{ max } = max @data_in;
    $STATS{ sum } = sum @data_in;
    $STATS{ avg } = $STATS{ sum } / scalar( @data_in );
    use warnings "all";
    
    my $width_in = $end - $start + 1;

    my $data_dot     = ( scalar @data_in ) / $width_in;
    my $data_dot_int = int( $data_dot + 0.5 );
    my @chars;

    if ( exists $object->{ init } )
    {
        # @data_out = map( $object->{ init }, @data_out );
        @data_out = (  $object->{ init } || 0  ) x $width_out;
    }

    if ( $#data_out <= $#data_in )
    {
        my $old_val = 0;
        for ( my $dot = $start ; $dot <= $end ; $dot++ )
        {
            my $s     = ( $dot - $start ) * $data_dot;
            my $e     = $s + $data_dot - 1;
            my @slice = @data_in[ $s .. $e ];

            if ( scalar( @slice ) )
            {
                if ( exists $object->{ type } && $object->{ type } =~ /^nrz$/i )
                {
                    foreach my $idx ( 0 .. $#slice )
                    {
                        if ( $slice[$idx] == 0 )
                        {
                            $slice[$idx] = $old_val;
                        }
                        else
                        {
                            $old_val = $slice[$idx];
                        }
                    }
                }
                if ( scalar( @slice ) > 1 )
                {
                    $data_out[$dot] = sum( @slice ) / scalar( @slice );
                }
                else
                {
                    $data_out[$dot] = 0;
                }
            }
            else
            {
                $data_out[$dot] = 0;
            }
            $STATS{ last }     = $dot;
            $STATS{ last_val } = $data_in[ $end - 1 ];
        }
    }
    else
    {
        if ( exists $object->{ type } && $object->{ type } =~ /^line|nrz$/i )
        {
            my $dot     = 0;
            my $old_val = 0;
          W: while ( $dot <= $width_in )
            {
                my $ind = ( int( ( $dot / ( $width_in / $data_in_size ) ) ) );
                my $val1 = ( $ind > $#data_in ? $data_in[-1] : $data_in[$ind] ) || 0;
                my $val2 = ( ( $ind + 1 ) > $#data_in ? $data_in[-1] : $data_in[ ( $ind + 1 ) ] ) || 0;

                my $inc = ( $val2 - $val1 ) / ( ( $width_in / $data_in_size ) );
                my $val = $val1 || 0;
                for ( 0 .. ( $width_in / $data_in_size ) )
                {
                    $STATS{ last } = $dot;
                    last W if ( $dot >= $width_in );
                    if ( $object->{ type } =~ /^nrz$/i && ( !$val2 || !$val ) )
                    {
                        carp "in nrz  [ $dot + $start ] = $old_val";
                        $data_out[ $dot + 1 ] = $old_val;
# $data_out[ $dot ] = $val;
# $start = $dot;
                    }
# else
# {
                    $data_out[ $dot + $start ] = $val;
                    $old_val = $val;
                    $val += $inc;
# }

                    if ( $inc > 0 )
                    {
                        $val = $val > $val2 ? $val2 : $val;
                    }
                    else
                    {
                        $val = $val < $val2 ? $val2 : $val;
                    }

                    $dot++;
                }
            }
        }
        else
        {
            $STATS{ last } = $width_in;
            for ( my $dot = 1 ; $dot <= $width_in ; $dot++ )
            {
                my $ind = ( int( ( $dot / ( $width_in / $data_in_size ) ) ) );
                $data_out[ $dot + $start - 1 ] = $ind > $#data_in ? $data_in[-1] || 0 : $data_in[$ind] || 0;
            }
        }
    }
    return wantarray ? ( \@data_out, \%STATS ) : \@data_out;
}

sub img_from
{
    my $self = shift;
    my $file = shift;
    my $obj  = Data::Serializer->new( 'compress' => 1 );

    my $svg = SVG::Parser->new()->parsefile( $file );

    my $info_data = $svg->getElementByID( 'info_data' );
# my  $element = $info_data->cloneNode( 1 );
# my  $c = ($element->getElements('comment'))[0];
    my $kid = $info_data->getFirstChild();

    my $com = $kid->xmlify();

    $com =~ s/^\s*<!--\s*//m;
    $com =~ s/\s*-->$//;

# my  @comments = $kid->getElements('comment');
# carp "**" x 50;
#   carp Dumper(\@c);
# my $comments = $c->{ '-comment' };
# foreach my $comment ( @comments )
# {
# # $comment =~ s/^\s*//;
# carp "<$comment>";
#
    my $tag = $obj->deserialize( $com );
# carp Dumper($comment->{ '-comment' });
    foreach my $data_tag ( @$tag )
    {
# carp Dumper( $data_tag );
        my $data     = $svg->getElementByID( $data_tag );
        my $kid_data = $data->getFirstChild();
# carp Dumper($kid_data);
# carp $kid_data->xmlify();
        my $data_element = $kid_data->cloneNode( 1 );
# carp Dumper( $data_element );
# my $parser=new SVG::Parser();
#
# my $svg1=$parser->parse_string($kid_data->xmlify());
# carp Dumper($svg1);

# # my  $data_element = $data->cloneNode( 2 );
# carp Dumper($data);
    }
#
# }

#   my @rectangles=$data->getElements("");
# carp Dumper(\@rectangles);
# foreach my $sib1 ( @$ref)
# {
# carp "**" x 50;
# foreach my $sib2 ( @{$sib1->getSiblings()} )
# {
# carp Dumper($sib2);
#
# }
#
# }
    $self;
}

# __PACKAGE__->meta->make_immutable;
#
1;

=head1 METHODS
	
	OO interface

=head2 Graph::ChartSVG->new

=over

Create a new Chart 

possible parameters are :

=back

=head3  active_size 

=over 

=back

    an array ref with x,y size of the active graph ( without the reserved border for label )
    
 
=head3  bg_color
 
=over 

=back

  an hex color for the global background color
 
=head3      frame
 
=over 

=back

  a Frame object to surround the active part of the graph

=head3      grid
 
=over 

=back

  a Grid oject to add to the graph
  
  
=head3     overlay
 
=over 

=back

 a Overlay to add on top of the graph ( useful to enhance a period in alarm )
  
=head3     layer
 
=over

=back

 a Layer object
  
=head3     border
 
=over 

=back

 a Border object ( = some extra space to fit aroubd the active graph to allow label.
                This increase the actual_size and create the total_size)
 
=head3      tag
 
=over 

=back

 a Tag objet ( if missing create a automatically incremented one )
 
=head3      glyph
 
=over 

=back

 a Glyph object to add on the graph ( like a arrow to point at the end of the current data )
    


my $graph = Graph::ChartSVG->new( active_size => \@size, bg_color => 'FCF4C6', frame => $f, png_tag => 1 );


=over 

=back

=head2 Frame->new

=over

=back
 
=head3      color
 
=over 

=back

 a hex color of the frame
 
=head3     thickness
 
=over 

=back

 thickness of the frame

my $f = Frame->new( color => 'ff0000', thickness => 3 );

=over 

=back

=head2 Border->new

=over

=back
 
=head3      top

 space between the active part of the graph and the top of the image
 
=over

=back
 
=head3      bottom

 space between the active part of the graph and the bottom of the image
 
=over

=back
 
=head3      left

 space between the active part of the graph and the left of the image

=over

=back
 
=head3      right

space between the active part of the graph and the right of the image
 
my $b = Border->new( top => 200, bottom => 100, left => 80, right => 200 );

=over 

=back

=head2 $graph->border

method to add or change a border on the graph
 
=over 

=back

=head2  Data->new

 create a new set of data 
 
=over

=back
 
=head3 type

 the type of graph used for that data set.
 could be :
 
=over 

=item

line  

a normal line

=item  

line_up     

a line with zero starting at the middle of the active graph with increasing value in the top direction 

=item 

line_down 

a line with zero starting at the middle of the active graph with increasing value in the bottom direction 

=item     

bar

a filled graph 

=item     

bar_up 

a filled graph with zero starting at the middle of the active graph with increasing value in the top direction 

=item      

bar_down 

a filled graph with zero starting at the middle of the active graph with increasing value in the bottom direction 
   
=item     

line_stack  

a set of line stacked under each other 

=item     

line_stack_up 

a set of line stacked under each other with zero starting at the middle of the active graph with increasing value in the top direction 

=item     

line_stack_down 

a set of line stacked under each otherwith zero starting at the middle of the active graph with increasing value in the bottom direction 
    
=item     

bar_stack

a set of filled graph stacked under each other 

=item     

bar_stack_up 

a set of filled graph stacked under each other with zero starting at the middle of the active graph with increasing value in the top direction 

=item     

bar_stack_down

a set of filled graph stacked under each other  with zero starting at the middle of the active graph with increasing value in the bottom direction 
  

=back
 
=head3 color

the hex color of the graph

if Data is stack type, it should be a array ref with all the hex color

=over

=back
 
=head3 thickness

the thickness of the line used 

in bar the thickness of the border


=over

=back
 
=head3 label

a label to set to the SVG object

=over

=back
 
=head3 offset

a vertical offset ( where the zero start )

=over 

=back

=head4 example:
 
    my $l = Data->new( type => 'line', color => 'ff9800A0', thickness => 3, label => 'oblique' ,  offset    => 50 );

=over 

=back

=head2  $l->data_set( ... )

include a set of data to a Data object

it is an array ref with all data

or an array ref of array ref  for the stack type of Graph

=over 

=back

=head4 example:

  $l->data_set( \@data1 );
  
  
=over 

=back

=head2  $graph->add( ... )

add a element in the graph 

the element could be:

=over 

=item

data_set

=item

Glyph

=item

Overlay


=back

=head4 example:

 $graph->add( $l );
 
method to add an object to a graph

=over 

=back

=head2 graph->grid( 
    debord => ...,
    x => Grid_object,
    y = > Grid_object,
    )

    $graph->grid(

        Grid->new(
            debord => Border->new( # the debord size of the grid ( = the grid is greater than the active size )
                top => 20, 
                bottom => 10, 
                left => 10,
                right => 10 ), 

            x => Grid_def->new(         # label on the left border of the graph
                color     => '1292FF',
                number    => 10,
                thickness => 2,
                label     => Label->new(
                    font  => 'verdana',
                    color => '0000ff',
                    size  => 15,
                    text  => \@text,    # a array ref with all the text to set on the left border of the graph
                    space => 10,        # space between the end of the label and the start of the grid
                    align => 'right',
                    rotation => -30,
                ),
                    label2 => Label->new(   # label on the right border of the graph
                    font  => 'times',
                    color => '0000ff',
                    s ize  => 20,
                    text  => \@text2,
                    space => 10,
                    # align => 'right',
                    # rotation => -45,
                ),
            ),

            y => Grid_def->new(
                color     => '00fff0',
                number    => $VERT_GRID,
                thickness => 1,
                label     => Label->new(  # label on the left bottom of the graph
                    font     => 'verdana',
                    color    => 'ff00ff',
                    size     => 14,
                    text     => \@DATE,
                    space    => 10,
                    rotation => -30,
                    align    => 'right',
                ),
                label2 => Label->new( # label on the left top of the graph
                    font         => 'verdana',
                    font_scaling => 0.558,
                    color        => 'B283FF',
                    size         => 16,
                    text         => \@DATE2,
                    align        => 'right',
                    space        => 0,
                    rotation     => -30,
                ),
            )
        )
    );

 
=over 

=back

=head2 $graph->label( label_name);

search for the layer with the label = label_name

in array context return ( ref_data, layer_level)
in scalar context return ref_data

(ref_data = an array ref with the data set )


 my ( $Mal, $Mlan ) = $graph->label( 'src_all' );
 
=over 

=back

=head2  $graph->move( from, to );

Move a speciied layer to another level.

the other layer are shifted to allow the insert

=over 

=back

=head2 Glyph->new

3 type of glyph are available:

'line'   draw a polyline or polygon
'text'   draw a text
'image' include  a PNG image Embeded in the SVG

To draw an arrow:

    my $g1 = Glyph->new(
        x        => $graph->border->left ,  
        y        =>$graph->active_size->[1] +$graph->border->bottom ,       
        type     => 'line',
        filled   => 1,                  # if 1 = fill the polygon ( be sure to correctly close the path )
        color    => '0faFff',
        data_set => [
            {
                data => [ [ 0, 0 ], [ 8, 10 ], [ 0, 10 ], [ 0, 10 + 20 ], [ 0, 10 ], [ -8, 10 ], [ 0, 0 ] ], # the list of point to create the polyline
                thickness => 3
            }
        ]
    );


To write 2 text label ( in one Glyph )

        $g = Glyph->new(
            label =>'label_max',
            x => 100 ,
            y        =>200,
            type     => 'text',
            color    =>0xff0000,
            size     => 9,                              # if the glyph's type is 'text', this is the font size
            font     => 'Verdana',                      # the TrueType font to use
            data_set => [                               # the data set contain an array with all the text to plot followed by the relative position + the optional rotation
                {
                    text   => "hello text 1",
                    x      => 0,                                # the relative position in x for that specific text
                    y      => 15,                               # the relative position in yfor that specific text
                    anchor => 'end',                                    # the text anchor ( could be  start, middle or end )
                    rotation => -45,                            # a rotation in ° in trigonometric direction ( anti-clock )
                    style => 'oblique'                          # could be 	normal (default) ,| italic = oblique
                },
                {
                    text =>"Bye text 2",
                    x      => 60,                                # the relative position in x for that specific text
                    y      => 15,                              # the relative position in yfor that specific text
                    anchor => 'end',
                    # rotation => -45,
                    style => 'oblique'
                },
            ],
        );
        
       
 To inlude a PNG image ( the image is encoded with MIME::Base64 )
      
         $g = Glyph->new(
            label => 'port_label',
            x     => $graph->active_size->[0] + $graph->border->left + 250,
            y     => $graph->active_size->[1] + $graph->border->top+$graph->border->bottom  -4,
            type  => 'image',
            data_set => [    
                {
                    image  => $img_bin,   
                    x      => 0,
                    y      => -5,
                    width  => $buf_x,           # the width of the image
                    height => $buf_y,           # the height of the image
                },
            ],
        ); 
        
=over 

=back

=head2 $graph>image

return the SVG image ( to be writed in a file )

    open( my $IMG, '>', $file_svg ) or die $!;
    binmode $IMG;
    print $IMG $graph>image;
    close $IMG;
    
!!!!    This object is only available after the render method !!!!

=over 

=back

=head2 $graph->reduce(        data  => \@dot1,
        start => 50,
        end   => 360,
        init  => 300 );
        
This method allow to create a set of data directly usable a a data_set.
If there are more plotting value in the input data then the size of the graph, use some average to create the plotting dot
If there are lower plotting value in the input data then the size of the graph, fill the gap to smooth the graph
data = an array ref with the input data        
start =  where the data start in the reduced data ( if missing =0 )
end = where the data end in the reduced data ( f missing = end of the active size graph )
init = a default value to set the element when there is no data to add, like before start or after end ( if missing use undef )


return a array of 2  element 

The first one contains an array refwith the reduced set of data
The second a hash ref with the statistic info

$VAR1 = {
          'perc' => 345,
          'last_val' => 359,
          'avg' => '400',
          'min' => 0,
          'last' => 360,
          'max' => 800,
          'sum' => 320400
        };



=over 

=back

=head2 $graph->render

create the SVG from all the objects

=over 

=back