package LatexIndent::FileContents;

#	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::GetYamlSettings qw/%mainSetting/;
use LatexIndent::Switches        qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/;
use LatexIndent::LogFile         qw/$logger/;
use LatexIndent::Verbatim        qw/%verbatimStorage/;
use Data::Dumper;
use Exporter qw/import/;
our @EXPORT_OK = qw/find_file_contents_environments_and_preamble/;
our @ISA       = "LatexIndent::Document";                            # class inheritance, Programming Perl, pg 321
our $fileContentsCounter;

sub find_file_contents_environments_and_preamble {
    my $self = shift;

    return if $mainSetting{indentPreamble};

    # store the file contents blocks in an array which, depending on the value
    # of indentPreamble, will be put into the verbatim hash, or otherwise
    # stored as children to be operated upon
    my @fileContentsStorageArray;

    # fileContents environments
    $logger->trace('*Searching for FILE CONTENTS environments (see fileContentsEnvironments)') if $is_t_switch_active;
    $logger->trace( Dumper( \%{ $mainSetting{fileContentsEnvironments} } ) ) if ($is_tt_switch_active);
    while ( my ( $fileContentsEnv, $yesno ) = each %{ $mainSetting{fileContentsEnvironments} } ) {

        if ( !$yesno ) {
            $logger->trace(" *not* looking for $fileContentsEnv as $fileContentsEnv:$yesno");
            next;
        }

        $logger->trace("looking for $fileContentsEnv environments") if $is_t_switch_active;

        # the trailing * needs some care
        if ( $fileContentsEnv =~ m/\*$/ ) {
            $fileContentsEnv =~ s/\*$//;
            $fileContentsEnv .= '\*';
        }

        my $fileContentsRegExp = qr/
                        (
                        \\begin\{
                                ($fileContentsEnv) # environment name captured into $2
                               \}                  # begin statement captured into $1
                        )
                        (
                            .*?                    # non-greedy match (body) into $3
                        )                            
                        (
                        \\end\{\2\}                # end statement captured into $4
                        \h*                        # possible horizontal spaces
                        )                    
                        (\R)?                      # possibly followed by a line break
                    /sx;

        while ( ${$self}{body} =~ m/$fileContentsRegExp/sx ) {

            # create a new Environment object
            my $fileContentsBlock = LatexIndent::FileContents->new(
                begin           => $1,
                body            => $3,
                end             => $4,
                name            => $2,
                linebreaksAtEnd => {
                    begin => 0,
                    body  => 0,
                    end   => $5 ? 1 : 0,
                },
                modifyLineBreaksYamlName => "filecontents",
            );

            # give unique id
            $fileContentsBlock->create_unique_id;

            # text wrapping can make the ID split across lines
            ${$fileContentsBlock}{idRegExp} = ${$fileContentsBlock}{id};

            if ( $is_m_switch_active and ${ $mainSetting{modifyLineBreaks}{textWrapOptions} }{huge} ne "overflow" ) {
                my $IDwithLineBreaks = join( "\\R?\\h*", split( //, ${$fileContentsBlock}{id} ) );
                ${$fileContentsBlock}{idRegExp} = qr/$IDwithLineBreaks/s;
            }

            # the replacement text can be just the ID, but the ID might have a line break at the end of it
            ${$fileContentsBlock}{replacementText} = ${$fileContentsBlock}{id};

            # the above regexp, when used below, will remove the trailing linebreak in ${$self}{linebreaksAtEnd}{end}
            # so we compensate for it here
            ${$fileContentsBlock}{replacementText} .= "\n" if ( ${$fileContentsBlock}{linebreaksAtEnd}{end} );

            # store the fileContentsBlock, and determine location afterwards
            push( @fileContentsStorageArray, $fileContentsBlock );

            # log file output
            $logger->trace("*found: ${$fileContentsBlock}{name} \t\ttype FILECONTENTS environment")
                if $is_t_switch_active;

            # remove the environment block, and replace with unique ID
            ${$self}{body} =~ s/$fileContentsRegExp/${$fileContentsBlock}{replacementText}/sx;

            $logger->trace("replaced with ID: ${$fileContentsBlock}{id}") if $is_tt_switch_active;
        }
    }

    # determine if body of document contains \begin{document} -- if it does, then assume
    # that the body has a preamble
    my $preambleRegExp = qr/
                        (
                         .*?
                        )
                        (\R?\h*\\begin\{document\})
                /sx;
    my $preamble = q();

    my $needToStorePreamble = 0;

    # try and find the preamble
    my $lookForPreamble = ${ $mainSetting{lookForPreamble} }{ ${$self}{fileExtension} };
    $lookForPreamble = 1 if ( ${$self}{fileName} eq "-" and ${ $mainSetting{lookForPreamble} }{STDIN} );

    if ( ${$self}{body} =~ m/$preambleRegExp/sx and $lookForPreamble and !$mainSetting{indentPreamble} ) {

        $logger->trace(
            "\\begin{document} found in body (after searching for filecontents)-- assuming that a preamble exists")
            if $is_t_switch_active;

        # create a new Verbatim object
        my $verbatimBlock = LatexIndent::Verbatim->new(
            begin                   => q(),
            body                    => $1,
            end                     => q(),
            name                    => "preamble",
            type                    => "preamble",
            horizontalTrailingSpace => q(),
            linebreaksAtEnd         => {
                begin => q(),
                body  => q(),
                end   => q(),
            },
            afterbit                 => q(),
            modifyLineBreaksYamlName => "preamble",
        );

        # log file output
        $logger->trace("*found: preamble, storing as verbatim (indentPreamble: 0)") if $is_t_switch_active;

        $verbatimBlock->unprotect_blank_lines
            if ( $is_m_switch_active and ${ $mainSetting{modifyLineBreaks} }{preserveBlankLines} );

        # there are common tasks for each of the verbatim objects
        $verbatimBlock->verbatim_common_tasks;

        # verbatim children go in special hash
        $verbatimStorage{ ${$verbatimBlock}{id} } = $verbatimBlock;

        # replace filecontents in preamble body
        foreach (@fileContentsStorageArray) {
            ${$verbatimBlock}{body} =~ s/${$_}{id}/${$_}{begin}${$_}{body}${$_}{end}/s;
        }

        # remove preamble, and replace with unique ID
        ${$self}{body} =~ s/$preambleRegExp/${$verbatimBlock}{replacementText}$2/s;

        # filecontents *not* in preamble
        foreach (@fileContentsStorageArray) {
            ${$self}{body} =~ s/${$_}{id}/${$_}{begin}${$_}{body}${$_}{end}/s;
        }

    }
    else {
        $logger->trace("*preamble not stored, so putting filecontents back in")
            if ( scalar @fileContentsStorageArray > 0 and $is_t_switch_active );
        foreach (@fileContentsStorageArray) {
            $logger->trace("${$_}{name} put back in document") if $is_t_switch_active;
            ${$self}{body} =~ s/${$_}{id}/${$_}{begin}${$_}{body}${$_}{end}/s;
        }
    }

    return;
}

sub create_unique_id {
    my $self = shift;

    $fileContentsCounter++;
    ${$self}{id} = "$tokens{filecontents}$fileContentsCounter$tokens{endOfToken}";
    return;
}

1;
