/////////////////////////////////////////////////////////////////////////////
//    License (GPLv2+):
//    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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/////////////////////////////////////////////////////////////////////////////
/** 
 * @file  DiffList.h
 *
 * @brief Declaration file for DiffList class
 */
// RCS ID line follows -- this is updated by CVS
// $Id: DiffList.h 3936 2006-12-10 14:26:56Z sdottaka $

#ifndef _DIFFLIST_H_
#define _DIFFLIST_H_

/**
 * @brief One difference defined by linenumbers.
 *
 * This struct defines one set of different lines "diff".
 * @p begin0, @p end0, @p begin1 & @p end1 are linenumbers
 * in original files. Other struct members point to linenumbers
 * calculated by WinMerge after adding empty lines to make diffs
 * be in line in screen.
 *
 * @note @p blank0 & @p blank1 are -1 if there are no blank lines
 */
struct DIFFRANGE
{
	int begin[3];	/**< First diff line in original file1,2,3 */
	int end[3];	/**< Last diff line in original file1,2,3 */
	int dbegin[3];	/**< Synchronised (ghost lines added) first diff line in file1,2,3 */
	int dend[3];	/**< Synchronised (ghost lines added) last diff line in file1,2,3 */
	int blank[3];		/**< Number of blank lines in file1,2,3 */
	BYTE op;		/**< Operation done with this diff */
	DIFFRANGE() { memset(this, 0, sizeof(*this)); }
};

/**
 * @brief Relation from left side (0) to right side (1) of a DIFFRANGE
 *
 * Map lines from file1 to file2
 */
struct DiffMap : public CArray<int, int>
{
	enum { BAD_MAP_ENTRY = -999999999, GHOST_MAP_ENTRY = 888888888 };

	// boilerplate ctr, copy ctr
	DiffMap() { }
	DiffMap(const DiffMap & src) { *this = src; }
	// Simple copy assignment
	DiffMap & operator=(const DiffMap & src)
	{
		this->SetSize(src.GetSize());
		for (int i=0; i<this->GetSize(); ++i)
			this->SetAt(i, src.GetAt(i));
		return *this;
	}
	/**
	 * @brief Put DiffMap into known. starting, unfilled state
	 */
	void InitDiffMap(int nlines)
	{
		SetSize(nlines);
		for (int i=0; i<nlines; ++i)
		{
			// sentry value so we can check later that we set them all
			SetAt(i, BAD_MAP_ENTRY);
		}
	}
};

/**
 * @brief DIFFRANGE with links for chain of non-trivial entries
 *
 * Next and prev are array indices used by the owner (DiffList)
 */
struct DiffRangeInfo
{
	DIFFRANGE diffrange;
	int next; /**< link (array index) for doubly-linked chain of non-trivial DIFFRANGEs */
	int prev; /**< link (array index) for doubly-linked chain of non-trivial DIFFRANGEs */

	DiffRangeInfo() { InitLinks(); }
	DiffRangeInfo(const DIFFRANGE & di) : diffrange(di) { InitLinks(); }
	void InitLinks() { next = prev = -1; }
};

/**
 * @brief Operations in diffranges.
 * DIFFRANGE structs op-member can have these values
 */
enum
{
	OP_NONE = 0,
	OP_1STONLY,
	OP_2NDONLY,
	OP_3RDONLY,
	OP_DIFF,
	OP_TRIVIAL
};

enum
{
	THREEWAYDIFFTYPE_LEFTMIDDLE  = 0,
	THREEWAYDIFFTYPE_LEFTRIGHT,
	THREEWAYDIFFTYPE_MIDDLERIGHT,
	THREEWAYDIFFTYPE_LEFTONLY,
	THREEWAYDIFFTYPE_MIDDLEONLY,
	THREEWAYDIFFTYPE_RIGHTONLY,
};

/**
 * @brief Class for storing differences in files (difflist).
 *
 * This class stores diffs in list and also offers diff-related
 * functions to e.g. check if linenumber is inside diff.
 *
 * There are two kinds of diffs:
 * - significant diffs are 'normal' diffs we want to merge and browse
 * - non-significant diffs are diffs ignored by linefilters
 */
class DiffList
{
public:
	DiffList();
	void Clear();
	int GetSize() const;
	int GetSignificantDiffs() const;
	void AddDiff(const DIFFRANGE & di);
	BOOL IsDiffSignificant(int nDiff) const;
	BOOL GetDiff(int nDiff, DIFFRANGE & di) const;
	BOOL SetDiff(int nDiff, const DIFFRANGE & di);
	int LineRelDiff(UINT nLine, UINT nDiff) const;
	BOOL LineInDiff(UINT nLine, UINT nDiff) const;
	int LineToDiff(UINT nLine) const;
	BOOL GetPrevDiff(int nLine, int & nDiff) const;
	BOOL GetNextDiff(int nLine, int & nDiff) const;
	BOOL HasSignificantDiffs() const;
	int PrevSignificantDiffFromLine(UINT nLine) const;
	int NextSignificantDiffFromLine(UINT nLine) const;
	int FirstSignificantDiff() const;
	int NextSignificantDiff(int nDiff) const;
	int PrevSignificantDiff(int nDiff) const;
	int LastSignificantDiff() const;
	const DIFFRANGE * FirstSignificantDiffRange() const;
	const DIFFRANGE * LastSignificantDiffRange() const;
	int PrevSignificant3wayDiffFromLine(UINT nLine, int nDiffType) const;
	int NextSignificant3wayDiffFromLine(UINT nLine, int nDiffType) const;
	int FirstSignificant3wayDiff(int nDiffType) const;
	int NextSignificant3wayDiff(int nDiff, int nDiffType) const;
	int PrevSignificant3wayDiff(int nDiff, int nDiffType) const;
	int LastSignificant3wayDiff(int nDiffType) const;
	const DIFFRANGE * FirstSignificant3wayDiffRange(int nDiffType) const;
	const DIFFRANGE * LastSignificant3wayDiffRange(int nDiffType) const;

	const DIFFRANGE * DiffRangeAt(int nDiff) const;

	void ConstructSignificantChain(); // must be called after diff list is entirely populated
	void Swap(int index1, int index2);
	void GetExtraLinesCounts(int nFiles, int extras[]);

private:
	CArray<DiffRangeInfo,DiffRangeInfo> m_diffs; /**< Difference list */
	int m_firstSignificant; /**< Index of first significant diff in m_diffs */
	int m_lastSignificant; /**< Index of last significant diff in m_diffs */
	int m_firstSignificantLeftMiddle;
	int m_firstSignificantLeftRight;
	int m_firstSignificantMiddleRight;
	int m_firstSignificantLeftOnly;
	int m_firstSignificantMiddleOnly;
	int m_firstSignificantRightOnly;
	int m_lastSignificantLeftMiddle;
	int m_lastSignificantLeftRight;
	int m_lastSignificantMiddleRight;
	int m_lastSignificantLeftOnly;
	int m_lastSignificantMiddleOnly;
	int m_lastSignificantRightOnly;
};

#endif // _DIFFLIST_H_
