<?php

/**
 * This file is part of Gitonomy.
 *
 * (c) Alexandre Salomé <alexandre.salome@gmail.com>
 * (c) Julien DIDIER <genzo.wm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Gitonomy\Git\Parser;

use Gitonomy\Git\Diff\File;
use Gitonomy\Git\Diff\FileChange;

class DiffParser extends ParserBase
{
    public $files;

    protected function doParse()
    {
        $this->files = [];

        while (!$this->isFinished()) {
            // 1. title
            $vars = $this->consumeRegexp("/diff --git \"?(a\/.*?)\"? \"?(b\/.*?)\"?\n/");
            $oldName = $vars[1];
            $newName = $vars[2];
            $oldIndex = null;
            $newIndex = null;
            $oldMode = null;
            $newMode = null;

            // 2. mode
            if ($this->expects('new file mode ')) {
                $newMode = $this->consumeTo("\n");
                $this->consumeNewLine();
                $oldMode = null;
            }
            if ($this->expects('old mode ')) {
                $oldMode = $this->consumeTo("\n");
                $this->consumeNewLine();
                $this->consume('new mode ');
                $newMode = $this->consumeTo("\n");
                $this->consumeNewLine();
            }
            if ($this->expects('deleted file mode ')) {
                $oldMode = $this->consumeTo("\n");
                $newMode = null;
                $this->consumeNewLine();
            }

            if ($this->expects('similarity index ')) {
                $this->consumeRegexp('/\d{1,3}%\n/');
                $this->consume('rename from ');
                $this->consumeTo("\n");
                $this->consumeNewLine();
                $this->consume('rename to ');
                $this->consumeTo("\n");
                $this->consumeNewLine();
            }

            // 4. File informations
            $isBinary = false;
            if ($this->expects('index ')) {
                $oldIndex = $this->consumeShortHash();
                $this->consume('..');
                $newIndex = $this->consumeShortHash();
                if ($this->expects(' ')) {
                    $vars = $this->consumeRegexp('/\d{6}/');
                    $newMode = $oldMode = $vars[0];
                }
                $this->consumeNewLine();

                //verifying if the file was deleted or created
                if ($this->expects('--- ')) {
                    $oldName = $this->consumeTo("\n") === '/dev/null' ? '/dev/null' : $oldName;
                    $this->consumeNewLine();
                    $this->consume('+++ ');
                    $newName = $this->consumeTo("\n") === '/dev/null' ? '/dev/null' : $newName;
                    $this->consumeNewLine();
                } elseif ($this->expects('Binary files ')) {
                    $vars = $this->consumeRegexp('/"?(.*?)"? and "?(.*?)"? differ\n/');
                    $isBinary = true;
                    $oldName = $vars[1];
                    $newName = $vars[2];
                }
            }

            $oldName = $oldName === '/dev/null' ? null : substr($oldName, 2);
            $newName = $newName === '/dev/null' ? null : substr($newName, 2);

            $oldIndex = $oldIndex !== null ?: '';
            $newIndex = $newIndex !== null ?: '';
            $oldIndex = preg_match('/^0+$/', $oldIndex) ? null : $oldIndex;
            $newIndex = preg_match('/^0+$/', $newIndex) ? null : $newIndex;
            $file = new File($oldName, $newName, $oldMode, $newMode, $oldIndex, $newIndex, $isBinary);

            // 5. Diff
            while ($this->expects('@@ ')) {
                $vars = $this->consumeRegexp('/-(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?/');
                $rangeOldStart = (int) $vars[1];
                $rangeOldCount = (int) $vars[2];
                $rangeNewStart = (int) $vars[3];
                $rangeNewCount = isset($vars[4]) ? (int) $vars[4] : (int) $vars[2]; // @todo Ici, t'as pris un gros raccourci mon loulou
                $this->consume(' @@');
                $this->consumeTo("\n");
                $this->consumeNewLine();

                // 6. Lines
                $lines = [];
                while (true) {
                    if ($this->expects(' ')) {
                        $lines[] = [FileChange::LINE_CONTEXT, $this->consumeTo("\n")];
                    } elseif ($this->expects('+')) {
                        $lines[] = [FileChange::LINE_ADD, $this->consumeTo("\n")];
                    } elseif ($this->expects('-')) {
                        $lines[] = [FileChange::LINE_REMOVE, $this->consumeTo("\n")];
                    } elseif ($this->expects("\ No newline at end of file")) {
                        // Ignore this case...
                    } else {
                        break;
                    }

                    $this->consumeNewLine();
                }

                $change = new FileChange($rangeOldStart, $rangeOldCount, $rangeNewStart, $rangeNewCount, $lines);

                $file->addChange($change);
            }

            $this->files[] = $file;
        }
    }
}
