# This module extends the base class to allow display of Chinese characters using the built-in CJK fonts.
package TableObjSupportCJKFonts;

use parent qw/TableObj/;

use constant RE_IS_CJK => qr/
    (?: \p{Block=CJK_Compatibility}
    |   \p{Block=CJK_Compatibility_Forms}
    |   \p{Block=CJK_Compatibility_Ideographs}
    |   \p{Block=CJK_Compatibility_Ideographs_Supplement}
    |   \p{Block=CJK_Radicals_Supplement}
    |   \p{Block=CJK_Strokes}
    |   \p{Block=CJK_Symbols_And_Punctuation}
    |   \p{Block=CJK_Unified_Ideographs}
    |   \p{Block=CJK_Unified_Ideographs_Extension_A}
    |   \p{Block=CJK_Unified_Ideographs_Extension_B}
    |   \p{Block=CJK_Unified_Ideographs_Extension_C}
    )
/x;

sub _init {
    my ( $self, $pdf, $startPage, $dataArray, %args ) = @_;

    $self->{pdf}  = $pdf;
    $self->{page} = $startPage;
    $self->{data} = $dataArray;
    $self->{text} = $self->{page}->text();
    $self->{gfx}  = $self->{page}->gfx();

    $self->{font}     = $args{font};
    $self->{fontSize} = $args{font_size};

    $self->{numRows}    = scalar(@$dataArray);
    $self->{numColumns} = scalar( @{ $dataArray->[0] } );

    $self->{newPageFunc} = $args{new_page_func};

    my $linkArray = $args{row_links};

    # The bottom margin.
    #
    $self->{bottomMargin} = $args{bottom_margin};

    # Where to start the table when we start a new page.
    #
    $self->{newPageY} = $args{new_page_y};

    # RSD-6476, add CJK fonts
    $self->{cjkfont} = $self->{pdf}->cjkfont('Song');

    my @collWidth;
    my @rowHeight;
    my @rowLines;

    my @cells;
    for ( my $j = 0 ; $j < $self->{numRows} ; $j++ ) {
        my @row;
        my %lineData;

        if ( 0 == $j ) {
            $self->{rowPadding}[$j] = 0;
        } else {
            $self->{rowPadding}[$j] = defined $args{row_padding} ? $args{row_padding} : kDefaultRowPadding;

            # Any specific row properties we should take into account?
            #
            if ( defined $args{row_props} ) {
                if ( defined $args{row_props}[$j]{pad} ) {
                    $self->{rowPadding}[$j] = $args{row_props}[$j]{pad};
                }
            }
        }

        # See if the 'repeat' row is being declared.
        #
        if ( defined $args{row_props} && $args{row_props}[$j]{repeat} ) {
            $self->{repeatRow} = $j;
        }

        # Now see if the 'repeatRows' flag is set.  This allows multiple rows to be repeated.
        #
        elsif ( defined $args{row_props} && $args{row_props}[$j]{repeatRows} ) {
            $self->{repeatRows} .= "-" . $j;
        }

        for ( my $i = 0 ; $i < $self->{numColumns} ; $i++ ) {
            my $width    = 0;
            my $maxWidth = 0;
            my %cell;
            $cell{text} = $dataArray->[$j][$i];
            $cell{link} = $linkArray->[$j][$i];

            $cell{font}          = $self->{font};
            $cell{fontSize}      = $self->{fontSize};
            $cell{justify}       = 'left';
            $cell{image}         = '';
            $cell{doNotTruncate} = '';
            $cell{pad}           = kDefaultColPadding;

            my $columnPadding = defined $args{column_padding} ? $args{column_padding} : kDefaultColPadding;

            # If there are column_props defined, use those to override default values.
            #
            if ( defined $args{column_props} ) {
                my $default = $args{column_props}->[$i];

                $cell{font}          = $default->{font}            if ( defined $default->{font} );
                $cell{fontSize}      = $default->{font_size}       if ( defined $default->{font_size} );
                $cell{justify}       = $default->{justify}         if ( defined $default->{justify} );
                $cell{image}         = $default->{image}           if ( defined $default->{image} );
                $cell{doNotTruncate} = $default->{do_not_truncate} if ( defined $default->{do_not_truncate} );
                $columnPadding       = $default->{pad}             if ( defined $default->{pad} );

                $width    = $default->{min_w} if ( defined $default->{min_w} );
                $maxWidth = $default->{max_w} if ( defined $default->{max_w} );
            }

            # We don't add padding to the left-most column.  That would be goofy.
            #
            if ( 0 == $i ) {
                $self->{columnPadding}[$i] = 0;
            } else {
                if ( !defined $self->{columnPadding}[$i] || $columnPadding > $self->{columnPadding}[$i] ) {
                    $self->{columnPadding}[$i] = $columnPadding;
                }
            }

            # If this is the header line, look for header overrides.
            # !!! I may change this a bit...
            #
            if ( 0 == $j && defined $args{header_props} ) {
                $cell{font}     = $args{header_props}{font}      if ( defined $args{header_props}{font} );
                $cell{fontSize} = $args{header_props}{font_size} if ( defined $args{header_props}{font_size} );
            }

            # Look for row-level property overrides
            #
            if ( defined $args{row_props} ) {
                $cell{font}     = $args{row_props}->[$j]->{font}      if ( defined $args{row_props}->[$j]->{font} );
                $cell{fontSize} = $args{row_props}->[$j]->{font_size} if ( defined $args{row_props}->[$j]->{font_size} );
            }

            # Don't do this for images!
            if ( !$cell{image} ) {
                my $cellTextWidth = $self->{text}->advancewidth( $cell{text}, font => $cell{font}, fontsize => $cell{fontSize} );

                if ( $cellTextWidth > $width ) {
                    if ( !$maxWidth ) {
                        $width = $cellTextWidth;
                    } elsif ( $cellTextWidth > $maxWidth && !$cell{doNotTruncate} ) {

                        # !!! We're going to _truncate_ the string to fit.
                        # !!! For now, we'll attempt a somewhat peculiar 'center-truncation' technique.
                        #
                        my $elipsis = '...';
                        my $elipsisWidth = $self->{text}->advancewidth( $elipsis, font => $cell{font}, fontsize => $cell{fontSize} );

                        my @chars = split( //, $cell{text} );

                        my $preString;
                        my $postString;
                        my $lastPreString;
                        my $lastPostString;
                        while ( scalar @chars > 0 ) {
                            $preString .= shift(@chars);
                            if ( scalar @chars ) {
                                $postString = pop(@chars) . $postString;
                            }

                            my $preWidth  = $self->{text}->advancewidth( $preString,  font => $cell{font}, fontsize => $cell{fontSize} );
                            my $postWidth = $self->{text}->advancewidth( $postString, font => $cell{font}, fontsize => $cell{fontSize} );
                            if ( $preWidth + $postWidth + $elipsisWidth > $maxWidth ) {
                                last;
                            }
                            $lastPreString  = $preString;
                            $lastPostString = $postString;
                        }

                        # Replace text with truncated version.
                        #
                        $cell{text} = $lastPreString . $elipsis . $lastPostString;
                    }
                }
            }

            if ( $width > $collWidth[$i] ) {
                $collWidth[$i] = $width;
            }

            my $cellHeight = $cell{fontSize};

            # Do we need to increase the row height, too?
            #
            if ( $cellHeight > $rowHeight[$j] ) {
                $rowHeight[$j] = $cell{fontSize};
            }

            push @row, \%cell;
        }

        # If we have been asked to draw lines, take those into account too when calculating rowHeight
        #
        if ( defined $args{row_props} && defined $args{row_props}[$j]{lines} ) {
            my @lines;
            my $maxLineHeight = 0;
            foreach my $lineDef ( @{ $args{row_props}[$j]{lines} } ) {

                # !!! These defaults should be able to be globally overridden...
                #
                my $lineHeight = ( defined $lineDef->{pen_size} ? $lineDef->{pen_size} : kDefaultPenSize );
                $lineHeight += ( defined $lineDef->{top_pad}    ? $lineDef->{top_pad}    : kDefaultTopLinePad );
                $lineHeight += ( defined $lineDef->{bottom_pad} ? $lineDef->{bottom_pad} : kDefaultBottomLinePad );

                if ( $lineHeight > $maxLineHeight ) {
                    $maxLineHeight = $lineHeight;
                }

                push @lines, $lineDef;
            }

            $lineData{maxHeight} = $maxLineHeight;
            $lineData{lines}     = \@lines;
        }

        push @cells,    \@row;
        push @rowLines, \%lineData;
    }

    $self->{cells}       = \@cells;
    $self->{columnWidth} = \@collWidth;
    $self->{rowHeight}   = \@rowHeight;
    $self->{rowLines}    = \@rowLines;

    return $self;
}

sub printRow {
    my ( $self, $x, $y, $j ) = @_;

    my $localX = $x;

    my $clientNameClean = Common::Client::Current()->WebAlias();
    $clientNameClean = Common::Client::Current()->ClientNameClean() unless $clientNameClean;

    # Adjust 'y' down by the height of this row.
    #
    $y = $self->adjustY( $y, $self->{rowHeight}->[$j] + $self->{rowPadding}->[$j] );

    # In case we need to make a link later...
    #
    my $top = $y + $self->{rowHeight}->[$j] + $self->{rowPadding}->[$j];

    for ( my $i = 0 ; $i < $self->{numColumns} ; $i++ ) {
        my $cell     = $self->{cells}->[$j][$i];
        my $colWidth = $self->{columnWidth}->[$i];

        # Apply the cell padding to the left
        #
        $localX += $self->{columnPadding}->[$i];

        # For creating the link dimensions below.
        #
        my $left = $localX;

        # Set the font data appropriately
        # RSD-6476 - this is a quick fix for displaying Chinese characters
        # Note! Checks text in all cells and affects velocity
        if ( $cell->{text} =~ RE_IS_CJK ) {
            $self->{text}->font( $self->{cjkfont}, '6/pt' );
        } else {
            $self->{text}->font( $cell->{font}, $cell->{fontSize} );
        }

        if ( $cell->{link} ) {
            $self->{text}->fillcolor("blue");
        } else {
            $self->{text}->fillcolor("black");
        }

        # Output the text
        #
        # Hold on, it could be an image!
        if ( $cell->{image} eq 'gif' ) {
            EmbedImage::addIconGIF(
                page   => $self->{page},
                pdf    => $self->{pdf},
                source => $cell->{text},
                link   => $cell->{link},
                x      => $x,
                y      => $y
            );

            # We create the link for images in the embed function, so let's clear it out now.
            #
            $cell->{link} = '';
        } elsif ( 'left' eq $cell->{justify} ) {
            $self->{text}->translate( $localX, $y );
            $self->{text}->text( $cell->{text} );
        } elsif ( 'right' eq $cell->{justify} ) {
            $self->{text}->translate( $localX + $colWidth, $y );
            $self->{text}->text_right( $cell->{text} );
        } elsif ( 'center' eq $cell->{justify} ) {
            $self->{text}->translate( $localX + ( $colWidth / 2 ), $y );
            $self->{text}->text_center( $cell->{text} );
        }

        $localX += $colWidth;

        if ( $cell->{link} ) {

            # And now we'll create the actual link
            my $annot  = $self->{page}->annotation();
            my $bottom = $y;
            my $right  = $localX;

            my $url = $cell->{link};
            my $anchorTest = substr( $url, 0, 1 );

            # !!!
            # If the first character of the link is '#',
            # we're going to assume this is meant to be an
            # internal link for this document.
            #

            if ( $anchorTest ne '#' ) {

                # external link
                $url = "https://" . $clientNameClean . ".royaltyshare.com" . $url;
                $annot->url( $url, -rect => [ $left, $bottom, $right, $top ] );
            } else {
                # internal link - strip '#' prefix for named_destination() call
                my $destName = substr( $url, 1 );
                my $dest = $self->{pdf}->named_destination( 'Dests', $destName );
                if ($dest && $dest->can('link')) {
                    $dest->link($self->{page});  # placeholder until album page is created
                }
                
                # Try destination object first (newer PDF::API2), fallback to string (older versions)
                eval {
                    $annot->link( $dest, -rect => [ $left, $bottom, $right, $top ] );
                    registerPendingLink( $self->{pdf}, $destName, $annot );

                    # Fix Action D: PDF::API2 doesn't set it correctly for named destinations
                    if ($annot->{A} && $annot->{A}->{S} && $annot->{A}->{S}->val eq 'GoTo' && $dest && $dest->{D}) {
                        $annot->{A}->{D} = $dest->{D};
                    }
                };
                if ($@) {
                    # Fallback: use destination name without '#' for compatibility with older PDF::API2
                    $annot->link( $destName, -rect => [ $left, $bottom, $right, $top ] );
                }
            }
        }

    }

    # Any lines to draw?
    #
    if ( defined $self->{rowLines}[$j] && defined $self->{rowLines}[$j]{maxHeight} ) {
        my $lineData  = $self->{rowLines}[$j];
        my $lineArray = $lineData->{lines};

        # Seems kinda dumb to draw a 'part' of a line on a page.
        # So, if the line height is going to cause the page to wrap, let's wrap the page first,
        # and _then_ start drawing the line.
        #
        $y = $self->wrapIfNecessary( $y, $lineData->{maxHeight} );

        foreach my $line (@$lineArray) {
            next unless ( defined $line->{start_col} && defined $line->{end_col} );
            my $lineXStart = $x;
            my $lineY      = $y;

            $lineY -= ( defined $line->{top_pad} ? $line->{top_pad} : kDefaultTopLinePad );

            # Find the starting X offset
            #
            for ( my $xx = 0 ; $xx < $line->{start_col} ; $xx++ ) {
                $lineXStart += $self->columnWidth($xx);
            }
            my $lineXEnd = $lineXStart + $self->columnWidth( $line->{start_col}, $line->{end_col} );

            my $lineHeight = ( defined $line->{pen_size} ? $line->{pen_size} : kDefaultPenSize );
            my $color      = ( defined $line->{color}    ? $line->{color}    : kDefaultLineColor );
            $self->{gfx}->strokecolor($color);

            # Draw the line
            #
            for ( my $yy = 0 ; $yy < $lineHeight ; $yy++ ) {
                $self->{gfx}->move( $lineXStart, $lineY );
                $self->{gfx}->line( $lineXEnd, $lineY );
                $self->{gfx}->stroke();

                $lineY--;
            }
        }

        $y = $self->adjustY( $y, $lineData->{maxHeight} );
    }

    return $y;
}

sub registerPendingLink {
    my ( $pdf, $destName, $annot ) = @_;

    return unless $pdf && $destName && $annot;
    $pdf->{_rs_pending_links} = {} unless $pdf->{_rs_pending_links};
    $pdf->{_rs_pending_links}{$destName} = [] unless $pdf->{_rs_pending_links}{$destName};
    push @{ $pdf->{_rs_pending_links}{$destName} }, $annot;
}

sub updatePendingLinks {
    my ( $pdf, $destName, $dest ) = @_;

    return unless $pdf && $destName && $dest && $dest->{D};
    my $pending = $pdf->{_rs_pending_links}{$destName};
    return unless $pending && ref($pending) eq 'ARRAY';

    foreach my $annot (@$pending) {
        next unless $annot->{A} && $annot->{A}->{S} && $annot->{A}->{S}->val eq 'GoTo';
        $annot->{A}->{D} = $dest->{D};
    }

    delete $pdf->{_rs_pending_links}{$destName};
}


1;