<?php
/*
 * MantisBT - A PHP based bugtracking system
 *
 * MantisBT 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 2 of the License, or
 * (at your option) any later version.
 *
 * MantisBT 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @copyright Copyright MantisBT Team - mantisbt-dev@lists.sourceforge.net
 */

/**
 * The coding standard for MantisBT.
 *
 * @see https://mantisbt.org/wiki/doku.php/mantisbt:coding_guidelines#code_blocks
 *
 * A overview for the cs-fixer rules
 * @see https://cs.symfony.com/doc/rules
 * @see https://mlocati.github.io/php-cs-fixer-configurator/
 */

use Symplify\EasyCodingStandard\Config\ECSConfig;

return ECSConfig::configure()
    ->withPaths([
        // __DIR__ . '/',
        // __DIR__ . '/api',
        // __DIR__ . '/core',
        // __DIR__ . '/tests',
        // __DIR__ . '/plugins/MantisCoreFormatting',
        __DIR__ . '/tests/fixer_test_malformed.php',
        // __DIR__ . '/ecs.php',
    ])
    ->withSkip([
        \PhpCsFixer\Fixer\Whitespace\SpacesInsideParenthesesFixer::class => [
            __DIR__ . '/ecs.php',
        ],
        __DIR__ . '/build',
        __DIR__ . '/config',
        __DIR__ . '/lang',
        __DIR__ . '/library',
    ])
    /**
     * General spacing
     *  Indentation: Tab
     *  Line endings: LF "\n"
     */
    ->withSpacing(
        /* indentation: */ \Symplify\EasyCodingStandard\ValueObject\Option::INDENTATION_TAB,

        /*
         * devy - comment will removed if the question is answered
         *
         * @see LineEndingFixer; "\n" is the default for the LineEndingFixer
         * @see LineEndingsSniff, [ 'eolChar' => "\n" ]
         *
         * todo both fixers do the job without setting this value.
         *      Check if this is required.
         */
        // /* lineEnding: */ "\n",
    )
    // PHP language level 7.4
    // ->withPhpCsFixerSets(php74Migration: true)
    ->withRules([
        /**
         * Basic: Encoding
         *
         * PHP code MUST use only UTF-8 without BOM (remove BOM).
         *
         * @see https://cs.symfony.com/doc/rules/basic/encoding.html
         */
        \PhpCsFixer\Fixer\Basic\EncodingFixer::class,

        /**
         * Whitespace: Line encoding
         *
         * All PHP files must use same line ending. Default is "\n"
         *
         * Sniff: LineEndingsSniff with ['eolChar' => "\n"]
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/line_ending.html
         */
        \PhpCsFixer\Fixer\Whitespace\LineEndingFixer::class,

        /**
         * Whitespace: No trailing whitespaces
         *
         * Remove trailing whitespace at the end of non-blank lines.
         *
         * "$foo = 'bar'···" > "$foo = 'bar'"
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/no_trailing_whitespace.html
         */
        \PhpCsFixer\Fixer\Whitespace\NoTrailingWhitespaceFixer::class,

        /**
         * Whitespace: Single blank line at eof
         *
         * A PHP file without end tag must always end with a single empty line feed.
         *
         * Sniff: PSR2.Files.EndFileNewline
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/single_blank_line_at_eof.html
         */
        \PhpCsFixer\Fixer\Whitespace\SingleBlankLineAtEofFixer::class,

        /**
         * Whitespaces: No whitespace in blank lines
         *
         * Remove trailing whitespace at the end of blank lines.
         *
         * <input>
         * ···
         * $a = 1;"
         * </input>
         * <output>
         *
         *  $a = 1;"
         * </output>
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/no_whitespace_in_blank_line.html
         */
        \PhpCsFixer\Fixer\Whitespace\NoWhitespaceInBlankLineFixer::class,

        /**
         * String: Single quotes
         *
         * Convert double quotes to single quotes for simple strings.
         *
         * Configurable. Default is keep double-quoted strings if they contain a
         * single-quoted string.
         *
         * $a = "sample"                       > $a = 'sample'
         * $b = "sample with 'single-quotes'"  > $b = "sample with 'single-quotes'"
         *
         * Sniff: Squiz.Strings.DoubleQuoteUsage
         *
         * @see https://cs.symfony.com/doc/rules/string_notation/single_quote.html
         */
        \PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer::class,

        /**
         * PHP tag: Full opening tag
         *
         * "<?" > "<?php"
         *
         * Sniff: Generic.PHP.DisallowShortOpenTag
         *
         * @see https://cs.symfony.com/doc/rules/php_tag/full_opening_tag.html
         */
        \PhpCsFixer\Fixer\PhpTag\FullOpeningTagFixer::class,

        /**
         * PHP tag: Echo tag syntax
         *
         * Configurable. Default is "format" = "short", "long_function" = "echo"
         *
         * "<?= …" > "<?php echo …"
         *
         * @see https://cs.symfony.com/doc/rules/php_tag/echo_tag_syntax.html
         */
        \PhpCsFixer\Fixer\PhpTag\EchoTagSyntaxFixer::class,

        /**
         * PHP tag: No closing tag
         *
         * The closing ?> tag MUST be omitted from files containing only PHP.
         *
         * Sniff: PSR2.Files.ClosingTag
         *
         * @see https://cs.symfony.com/doc/rules/php_tag/no_closing_tag.html
         */
        \PhpCsFixer\Fixer\PhpTag\NoClosingTagFixer::class,

        /**
         * Casing: Constant case: lower
         *
         * The PHP constants true, false, and null MUST be written
         * using the correct casing.
         *
         * Configurable. Default is "lower"
         *
         * "$a = FALse" > "a = false"
         *
         * Sniff: Generic.PHP.LowerCaseConstant
         *
         * @see https://cs.symfony.com/doc/rules/casing/constant_case.html
         */
        \PhpCsFixer\Fixer\Casing\ConstantCaseFixer::class,

        /**
         * Casing: Lowercase keywords
         *
         * PHP keywords MUST be in lower case.
         *
         * "FOREACH( $a AS $B )" > "foreach( $a as $B )"
         *
         * Sniff: Generic.PHP.LowerCaseKeyword
         *
         * @see https://cs.symfony.com/doc/rules/casing/lowercase_keywords.html
         */
        \PhpCsFixer\Fixer\Casing\LowercaseKeywordsFixer::class,

        /**
         * Function: No spaces after function name
         *
         * When making a method or function call, there MUST NOT be a space
         * between the method or function name and the opening parenthesis.
         *
         * "foo ( test ( 3 ) );"  > "foo( test( 3 ) )"
         *
         * Sniffs: Function
         *
         * @see https://cs.symfony.com/doc/rules/function_notation/no_spaces_after_function_name.html
         */
        \PhpCsFixer\Fixer\FunctionNotation\NoSpacesAfterFunctionNameFixer::class,

        /**
         * Function: No space after function declaration
         *
         * Spaces should be properly placed in a function declaration.
         *
         * Configurable. Default match MantisBT CS
         *
         * "function foo () {}" > "function foo() {}"
         *
         * @see https://cs.symfony.com/doc/rules/function_notation/function_declaration.html
         */
        \PhpCsFixer\Fixer\FunctionNotation\FunctionDeclarationFixer::class,

        /**
         * Operator: Space around operators
         *
         * Configurable. Default is "single_space"
         *
         * "$a=1+2;" > "$a = 1 + 2;"
         *
         * Sniff: OperatorSpacingSniff
         *
         * @see https://cs.symfony.com/doc/rules/operator/binary_operator_spaces.html
         */
        \PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer::class,

        /**
         * Array: Array syntax
         *
         * Configurable. Default is "short" syntax.
         *
         * array(1,2) > [1,2];
         *
         * @see https://cs.symfony.com/doc/rules/array_notation/array_syntax.html
         */
        \PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer::class,

        /**
         * Array: Trim array spaces
         *
         * Arrays should be formatted like function/method arguments,
         * without leading or trailing single line space.
         *
         * [ ]     > []
         * [ 1,2 ] > [1,2]
         *
         * @see https://cs.symfony.com/doc/rules/array_notation/trim_array_spaces.html
         */
        \PhpCsFixer\Fixer\ArrayNotation\TrimArraySpacesFixer::class,

        /**
         * Array: No whitespace before comma in array
         *
         * In array declaration, there MUST NOT be a whitespace before
         * each comma.
         *
         * [1 , 2 , 8] > [1, 2, 3]
         *
         * @see https://cs.symfony.com/doc/rules/array_notation/no_whitespace_before_comma_in_array.html
         */
        \PhpCsFixer\Fixer\ArrayNotation\NoWhitespaceBeforeCommaInArrayFixer::class,

        /**
         * Array: Whitespace after comma
         *
         * In array declaration, there MUST be a whitespace after each comma.
         *
         * Configurable.
         *
         * [1,2,3] > [1, 2, 3]
         *
         * "ensure_single_space" => false
         * ['one', 'two', 'three']
         * [1,     2,     3]
         *
         * @see https://cs.symfony.com/doc/rules/array_notation/whitespace_after_comma_in_array.html
         */
        \PhpCsFixer\Fixer\ArrayNotation\WhitespaceAfterCommaInArrayFixer::class,

        /**
         * Whitespace: Array indention
         *
         * Each element of an array must be indented exactly once.
         *
         * <input>
         *     $foo = [
         *      'bar' => [
         *        'baz' => true,
         *      ],
         *    ];
         * </input>
         * <output>
         *  $foo = [
         *      'bar' => [
         *          'baz' => true,
         *      ],
         *  ];
         * </output>
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/array_indentation.html
         */
        \PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer::class,

        /**
         * Control structure: Continuation position: same line
         *
         * Configurable. Default is "same_line"
         *
         * <input>
         * if( $baz == true ) {
         *     echo "foo";
         * }
         * else {
         *     echo "bar";
         * }
         * </input>
         * <output>
         * if( $baz == true ) {
         *     echo "foo";
         * } else {
         *     echo "bar";
         * }
         * </output>
         *
         * @see https://cs.symfony.com/doc/rules/control_structure/control_structure_continuation_position.html
         */
        \PhpCsFixer\Fixer\ControlStructure\ControlStructureContinuationPositionFixer::class,

        /**
         * Control structure: Braces
         *
         * The body of each control structure MUST be enclosed within braces.
         *
         * <input>
         * if( $foo === $bar )
         *     echo 'same'
         * </input>
         * <output>
         *  if( $foo === $bar ) {
         *      echo 'same'
         *  }
         * </output>
         *
         * Sniff: InlineControlStructureSniff
         *
         * @see https://cs.symfony.com/doc/rules/control_structure/control_structure_braces.html
         */
        \PhpCsFixer\Fixer\ControlStructure\ControlStructureBracesFixer::class,

        /**
         * Language construct: Single space around a language construct
         *
         * @todo consider about it.
         *
         * @see https://cs.symfony.com/doc/rules/language_construct/single_space_around_construct.html
         */
        // \PhpCsFixer\Fixer\LanguageConstruct\SingleSpaceAroundConstructFixer::class,

        /**
         * Whitespace: Statement indention
         *
         * Each statement must be indented.
         *
         * @note: Issues with mixed indention (tab and whitespaces)?
         *
         * @see https://cs.symfony.com/doc/rules/whitespace/statement_indentation.html
         */
        \PhpCsFixer\Fixer\Whitespace\StatementIndentationFixer::class,

        /**
         * Comment: Spacing of single line comment
         *
         * #comment > # comment
         *
         * @see https://cs.symfony.com/doc/rules/comment/single_line_comment_spacing.html
         */
        \PhpCsFixer\Fixer\Comment\SingleLineCommentSpacingFixer::class,
    ])

    /**
     * File: Line endings
     *
     * Line endings MUST be "\n".
     *
     * @see LineEndingFixer
     *
     * The LineEndingFixer does the same, but is not configurable,
     * "\n" is the default. This sniff is a little more explicit.
     *
     * @see https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/src/Standards/Generic/Sniffs/Files/LineEndingsSniff.php
     */
    //  ->withConfiguredRule(\PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineEndingsSniff::class, [
    //      'eolChar' => "\n",
    //  ])

    /**
     * Basic: Position of braces
     *
     * Opening braces on same line
     *
     * "Class Name {}"
     * "function name() {}"
     *
     * @see https://cs.symfony.com/doc/rules/basic/braces_position.html
     */
    ->withConfiguredRule(\PhpCsFixer\Fixer\Basic\BracesPositionFixer::class, [
        'classes_opening_brace' => 'same_line',
        'functions_opening_brace' => 'same_line',
    ])

    /**
     * Whitespace: Spaces inside parentheses
     *
     * "function foo($bar, $baz)" > "function foo( $bar, $baz )"
     * "if($bar === $baz)"        > "if( $bar === $baz )"
     * "foo( )"                   > "foo()"
     *
     * Sniffs:
     *  - build/CodeSniffer/Mantis/Sniffs/ControlStructures/ControlSignatureSniff.php
     *  - ControlStructureSpacingSniff
     *  - FunctionDeclarationArgumentSpacingSniff
     *
     * @see https://cs.symfony.com/doc/rules/whitespace/spaces_inside_parentheses.html
     */
    ->withConfiguredRule(\PhpCsFixer\Fixer\Whitespace\SpacesInsideParenthesesFixer::class, [
        'space' => 'single',
    ])

    /**
     * Sniff: control structure spacing
     *
     * this will fix only control structures
     *  if ($baz == true) > if ( $baz == true )
     *
     * but not
     * function('boo') > function( 'boo' )
     *
     * for this the sniff FunctionDeclarationArgumentSpacingSniff is needed
     *
     * @see FunctionDeclarationArgumentSpacingSniff
     *
     * Both are fixed with one
     * @see SpacesInsideParenthesesFixer
     */
    //  ->withConfiguredRule(PHP_CodeSniffer\Standards\PSR2\Sniffs\ControlStructures\ControlStructureSpacingSniff::class, [
    //      'requiredSpacesAfterOpen' => 1,
    //      'requiredSpacesBeforeClose' => 1,
    //  ])
    //  ->withConfiguredRule(
    //      \PHP_CodeSniffer\Standards\Squiz\Sniffs\Functions\FunctionDeclarationArgumentSpacingSniff::class, [
    //          'equalsSpacing' => 1,
    //          'requiredSpacesAfterOpen' => 1,
    //          'requiredSpacesBeforeClose' => 1
    //      ]
    //  )

    // Sniff concat spaces
    // @see ConcatSpaceFixer
    //  ->withConfiguredRule(\PHP_CodeSniffer\Standards\Squiz\Sniffs\Strings\ConcatenationSpacingSniff::class, [
    //      'spacing' => 1,
    //      'ignoreNewlines' => true,
    //  ])

    /**
     * Operator: Concat spaces
     *
     * Spacing to apply around concatenation operator.
     *
     * @see https://cs.symfony.com/doc/rules/operator/concat_space.html
     */
    ->withConfiguredRule(\PhpCsFixer\Fixer\Operator\ConcatSpaceFixer::class, [
        'spacing' => 'one',
    ])

    // single_line_comment_style
    // https://mlocati.github.io/php-cs-fixer-configurator/#version:3.52|fixer:single_line_comment_style
    ->withConfiguredRule(\PhpCsFixer\Fixer\Comment\SingleLineCommentStyleFixer::class, [
        'comment_types' => ['asterisk'],
    ])

    /**
     * Cast: No space after cast
     *
     * "$bar = ( string )  $a;" > "$bar = (string)$a;"
     *
     * Sniff: NoSpaceAfterCast
     *
     * @see https://mlocati.github.io/php-cs-fixer-configurator/#version:3.52|fixer:cast_spaces
     */
    ->withConfiguredRule(
        \PhpCsFixer\Fixer\CastNotation\CastSpacesFixer::class, [
            'space' => 'none'
        ]
    )

    /**
     * Whitespace: No extra blank line
     *
     * <input>
     * function foo() {
     *
     *     return 'bar';
     * }
     * </input>
     * <output>
     *  function foo() {
     *      return 'bar';
     *  }
     * </output>
     *
     * Sniff: OpeningFunctionBraceKernighanRitchie
     *
     * @see https://cs.symfony.com/doc/rules/whitespace/no_extra_blank_lines.html
     */
    ->withConfiguredRule(
        \PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer::class, [
        'tokens' => [
            # no space after opening braces
            'parenthesis_brace_block',
            'extra'
        ]]
    )

    /**
     * Function: Argument spaces
     *
     * @todo check
     *
     * Sniff: PEAR.Functions.FunctionCallSignature [allowMultipleArguments => false]
     *
     * @see https://cs.symfony.com/doc/rules/function_notation/method_argument_space.html#example-5
     */
    // ->withConfiguredRule(\PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer::class, [
    //     'on_multiline' => 'ensure_single_line'
    // ])
;
