package LatexIndent::TrailingComments;

#	This program is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	See http://www.gnu.org/licenses/.
#
#	Chris Hughes, 2017-2025
#
#	For all communication, please visit: https://github.com/cmhughes/latexindent.pl
use strict;
use warnings;
use LatexIndent::Tokens          qw/%tokens/;
use LatexIndent::Switches        qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/;
use LatexIndent::GetYamlSettings qw/%mainSetting/;
use LatexIndent::LogFile         qw/$logger/;
use Data::Dumper;
use Exporter qw/import/;
our @EXPORT_OK
    = qw/remove_trailing_comments put_trailing_comments_back_in $trailingCommentRegExp add_comment_symbol construct_trailing_comment_regexp @trailingComments $alignMarkUpBlockPresent/;
our @trailingComments;
our $commentCounter = 0;
our $notPrecededByRegExp;
our $trailingCommentRegExp;
our $alignMarkUpBlockPresent = 0;

sub construct_trailing_comment_regexp {
    $notPrecededByRegExp = qr/${${$mainSetting{fineTuning}}{trailingComments}}{notPrecededBy}/;
    my $notPreceededBy = ${ ${ mainSetting {fineTuning} }{trailingComments} }{notPreceededBy};

    if ($notPreceededBy) {
        $logger->warn(
            "*fineTuning:trailingComments:notPreceededBy is ok for now, but in future versions, fineTuning:trailingComments:notPrecededBy will be used"
        );
        $notPrecededByRegExp = qr/$notPreceededBy/;
    }

    $trailingCommentRegExp = qr/$notPrecededByRegExp%$tokens{trailingComment}\d+$tokens{endOfToken}/;
}

sub add_comment_symbol {

    # add a trailing comment token after, for example, a square brace [
    # or a curly brace { when, for example, BeginStartsOnOwnLine == 2
    my $self  = shift;
    my %input = @_;

    my $commentValue = ( defined $input{value} ? $input{value} : q() );

    # increment the comment counter
    $commentCounter++;

    # store the comment -- without this, it won't get processed correctly at the end
    push( @trailingComments,
        { id => $tokens{trailingComment} . $commentCounter . $tokens{endOfToken}, value => $commentValue } );

    # log file info
    $logger->trace("*Updating trailing comment array")       if $is_t_switch_active;
    $logger->trace( Dumper( \@trailingComments ), 'ttrace' ) if ($is_tt_switch_active);

    # the returned value
    return $tokens{trailingComment} . $commentCounter . $tokens{endOfToken};
}

sub remove_trailing_comments {
    my $self = shift;

    $commentCounter = 0;

    $logger->trace("*Storing trailing comments") if $is_t_switch_active;

    my $afterComment = qr/${${$mainSetting{fineTuning}}{trailingComments}}{afterComment}/;

    # perform the substitution
    ${$self}{body} =~ s/
                            $notPrecededByRegExp  # not preceded by a \
                            %                     # % 
                            (
                                $afterComment     # anything else
                            )
                            $                     # up to the end of a line
                        /   
                            my $commentValue = $1;

                            my $alignMarkUpBlock = ($commentValue =~m @^\*\h*\\(?:begin|end)\{([^}]+)\}*@ and ${ $mainSetting{lookForAlignDelims} }{$1} ? 1 : 0);

                            $alignMarkUpBlockPresent = $alignMarkUpBlock unless $alignMarkUpBlockPresent;

                            # increment comment counter and store comment
                            $commentCounter++;
                            push(@trailingComments,{id=>$tokens{trailingComment}.$commentCounter.$tokens{endOfToken},value=>$commentValue,alignMarkUp=>$alignMarkUpBlock});

                            # replace comment with token
                            "%".$tokens{trailingComment}.$commentCounter.$tokens{endOfToken};
                       /xsmeg;
    if (@trailingComments) {
        $logger->trace("Trailing comments stored in:") if ($is_tt_switch_active);
        $logger->trace( Dumper( \@trailingComments ) ) if ($is_tt_switch_active);
    }
    else {
        $logger->trace("No trailing comments found") if ($is_t_switch_active);
    }

    # check for body ENDING with trailing comment
    # see, for example, test-cases/specials/env1.tex with EndFinishesWithLineBreak: 1
    ${$self}{fileFinishesWithTrailingComment}
        = ( $is_m_switch_active and ${$self}{body} =~ m/$trailingCommentRegExp\h*\Z/s ? 1 : 0 );
    $logger->trace("file finishes with trailing comment, no line break after it")
        if ( $is_t_switch_active and ${$self}{fileFinishesWithTrailingComment} );
    return;
}

sub put_trailing_comments_back_in {
    my $self = shift;
    return unless ( @trailingComments > 0 );

    $logger->trace("*Returning trailing comments to body") if $is_t_switch_active;

    # check for body ENDING with trailing comment
    # see, for example, test-cases/specials/env1.tex with EndFinishesWithLineBreak: 1
    if ( ${$self}{fileFinishesWithTrailingComment} ) {
        $logger->trace("file finishes with trailing comment, no line break after it") if ($is_t_switch_active);
        ${$self}{body} =~ s/($trailingCommentRegExp)\s*$/$1\n/s;
    }

    # loop through trailing comments in reverse so that, for example,
    # latexindenttrailingcomment1 doesn't match the first
    # part of latexindenttrailingcomment18, which would result in an 8 left over (bad)
    while ( my $comment = pop @trailingComments ) {
        my $trailingcommentID    = ${$comment}{id};
        my $trailingcommentValue = ${$comment}{value};

        # the -m switch can modify max characters per line, and trailing comment IDs can
        # be split across lines
        if (    $is_m_switch_active
            and ${ $mainSetting{modifyLineBreaks}{textWrapOptions} }{huge} ne "overflow"
            and ${$self}{body} !~ m/%$trailingcommentID/m )
        {
            $logger->trace(
                "$trailingcommentID not found in body using /m matching, assuming it has been split across line (see modifyLineBreaks: textWrapOptions)"
            ) if ($is_t_switch_active);
            my $trailingcommentIDwithLineBreaks;

            # construct a reg exp that contains possible line breaks in between each character
            if ( ${ $mainSetting{modifyLineBreaks}{textWrapOptions} }{separator} ne '' ) {
                $trailingcommentIDwithLineBreaks = join(
                    "\\" . ${ $mainSetting{modifyLineBreaks}{textWrapOptions} }{separator} . "?",
                    split( //, $trailingcommentID )
                );
            }
            else {
                $trailingcommentIDwithLineBreaks = join( "(?:\\h|\\R)*", split( //, $trailingcommentID ) );
            }
            my $trailingcommentIDwithLineBreaksRegExp = qr/$trailingcommentIDwithLineBreaks/s;

            # replace the line-broken trailing comment ID with a non-broken trailing comment ID
            ${$self}{body} =~ s/%\R?$trailingcommentIDwithLineBreaksRegExp/%$trailingcommentID/s;
        }
        if (${$self}{body} =~ m/%$trailingcommentID
                              (
                                  (?!                       # not immediately preceded by 
                                      $notPrecededByRegExp  # \
                                      %                     # %
                                  ).*?
                              )                             # captured into $1
                              (\h*)?$                
                          /mx and $1 ne ''
            )
        {
            $logger->trace("Comment not at end of line $trailingcommentID, moving it to end of line")
                if $is_t_switch_active;
            ${$self}{body} =~ s/%$trailingcommentID(.*)$/$1%$trailingcommentValue/m;
        }
        else {
            ${$self}{body} =~ s/%$trailingcommentID/%$trailingcommentValue/;
        }
        $logger->trace("replace %$trailingcommentID with %$trailingcommentValue") if ($is_tt_switch_active);
    }
    return;
}

1;
