﻿// TortoiseSVN - a Windows shell extension for easy version control

// Copyright (C) 2026 - TortoiseSVN

// 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 2
// 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.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#pragma once
#include "TSVNPath.h"
#include <string>
#include <vector>

/**
 * \ingroup TortoiseProc
 * Holds the diff between the BASE version and the working copy version
 * of a text file, together with the lines the user selected for a commit.
 * The class builds the file content with only the selected changes.
 *
 * Both versions are kept as raw bytes. The class does not change the
 * encoding, the BOM or the line endings of the lines it copies.
 */
class CPartialDiff
{
public:
    struct Hunk
    {
        size_t            origStart = 0; ///< first line in the BASE file
        size_t            origLen   = 0; ///< number of removed lines
        size_t            modStart  = 0; ///< first line in the working copy file
        size_t            modLen    = 0; ///< number of added lines
        std::vector<bool> removedSelected;
        std::vector<bool> addedSelected;
    };

    CPartialDiff() = default;

    /**
     * Reads the BASE and the working copy version of the file and
     * calculates the diff. All changes are selected after the call.
     * \return false if the file cannot be used for a partial commit.
     *         GetError() then returns the reason.
     */
    bool Load(const CTSVNPath& wcPath);

    const CTSVNPath&         GetPath() const { return m_path; }
    const CString&           GetError() const { return m_error; }
    const std::vector<Hunk>& GetHunks() const { return m_hunks; }

    size_t GetBaseLineCount() const { return m_baseLines.size(); }
    size_t GetWcLineCount() const { return m_wcLines.size(); }
    /// returns a line of the BASE file in UTF-8, without the line ending
    std::string GetBaseLineUtf8(size_t line) const;
    /// returns a line of the working copy file in UTF-8, without the line ending
    std::string GetWcLineUtf8(size_t line) const;

    void SetRemovedSelected(size_t hunk, size_t index, bool selected);
    void SetAddedSelected(size_t hunk, size_t index, bool selected);
    void SelectHunk(size_t hunk, bool selected);
    void SelectAll(bool selected);

    /// returns the number of selected changed lines and the total number of changed lines
    void   CountSelected(size_t& selected, size_t& total) const;
    bool   IsAllSelected() const;
    bool   IsNoneSelected() const;
    /// returns 1 if all lines of the hunk are selected, 0 if none is selected, -1 otherwise
    int    GetHunkState(size_t hunk) const;

    /**
     * Builds the file content: the BASE file with only the selected changes.
     */
    std::string BuildPartial() const;

    /**
     * Returns true if the working copy file did not change on disk
     * since the call to Load().
     */
    bool IsWcUnchanged() const;

private:
    struct Line
    {
        size_t start;  ///< offset of the line in the buffer
        size_t length; ///< length of the line without the line ending
        size_t eolLen; ///< length of the line ending (0 for the last line without line ending)
    };

    static void SplitLines(const std::string& buf, std::vector<Line>& lines);
    static bool IsTextContent(const std::string& buf);
    std::string ToUtf8(const std::string& buf, const Line& line) const;
    bool        ReadWcFile(std::string& content);

    CTSVNPath         m_path;
    CString           m_error;
    std::string       m_base;
    std::string       m_wc;
    std::vector<Line> m_baseLines;
    std::vector<Line> m_wcLines;
    std::vector<Hunk> m_hunks;
    std::string       m_eol = "\n"; ///< line ending to use after a last line without line ending
    bool              m_isUtf8 = true;
    ULONGLONG         m_wcSize = 0;
    FILETIME          m_wcTime = {};
};
