using BallanceTASEditor.Core.TASStruct; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BallanceTASEditor.Core.FileOperation { public abstract class RevocableOperation { public RevocableOperation() { hasBeenDone = false; } private bool hasBeenDone; public virtual void Do(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { if (hasBeenDone) throw new Exception("Try to call operation.do when the operation has been done."); hasBeenDone = true; } public virtual void Undo(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { if (!hasBeenDone) throw new Exception("Try to call operation.undo when the operation has not been done."); hasBeenDone = false; } } public class SetOperation : RevocableOperation { private SelectionRange field; private SelectionRange absoluteRange; private bool? isSet; private uint internalOffset; private List changedItems; public SetOperation(SelectionRange _field, SelectionRange _absoluteRange, bool? _isSet) : base() { field = _field; absoluteRange = _absoluteRange; isSet = _isSet; // calc offset first for following operation internalOffset = 0; for (int i = (int)field.start; i <= (int)field.end; i++) { internalOffset |= ConstValue.Mapping[(FrameDataField)i]; } changedItems = new List(); } public override void Do(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Do(ref mMem, ref mPointer, ref mPointerIndex); if (mPointer == null) return; changedItems.Clear(); foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { // backup item first changedItems.Add(item.Value.keystates); if (isSet == null) item.Value.ReverseKeyStates(internalOffset); else if (isSet == true) item.Value.SetKeyStates(internalOffset); else if (isSet == false) item.Value.UnsetKeyStates(internalOffset); } } public override void Undo(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Undo(ref mMem, ref mPointer, ref mPointerIndex); if (mPointer == null) return; int counter = 0; foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { // restore data item.Value.keystates = changedItems[counter]; counter++; } } } public class RemoveOperation : RevocableOperation { private SelectionRange absoluteRange; private LinkedList removedItems; private LinkedListNode oldPointer; private long oldPointerIndex; private LinkedListNode removeStartNode; public RemoveOperation(SelectionRange _absoluteRange) : base() { absoluteRange = _absoluteRange; removedItems = new LinkedList(); } public override void Do(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Do(ref mMem, ref mPointer, ref mPointerIndex); if (mPointer == null) return; // init backups list and backups 2 data // and backups remove start node(ps: if it is null, mean removed from head) removedItems.Clear(); oldPointer = mPointer; oldPointerIndex = mPointerIndex; removeStartNode = absoluteRange.start == 0 ? null : mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.start - 1); // find proper pointer after remove first. but we do not apply it in there. // if state is true, it mean the deleted content is placed before pointer previously. we should consider pointer data and we should correct them. LinkedListNode newPointer; long newPointerIndex; if (mPointerIndex >= absoluteRange.start) { // if point within removed content, we need to shift it to the head of removed content, // otherwise we only need to minus index with the length of removed content. if (absoluteRange.Within(mPointerIndex)) { // this contains 3 situation // if full delete, mPointer is null and mPointerIndex is invalid(with wrong data: -1) // if delete from head, mPointer and mPointerIndex all are valid. but it is the tail of removed content // otherwise, just find the head of removed content and shift to it. if (absoluteRange.start == 0 && absoluteRange.end == mMem.Count - 1) { // fully remove newPointer = null; newPointerIndex = -1; } else if (absoluteRange.start == 0) { // remove from head newPointerIndex = 0; newPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.end + 1); } else { // simple remove newPointerIndex = absoluteRange.start - 1; newPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.start - 1); } } else { newPointer = mPointer; newPointerIndex = mPointerIndex - absoluteRange.GetCount(); } } else { // not affected situation newPointer = mPointer; newPointerIndex = mPointerIndex; } // the real remove operation foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { mMem.Remove(item); removedItems.AddLast(item); // backups node; } // apply gotten new pointer mPointer = newPointer; mPointerIndex = newPointerIndex; } public override void Undo(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Undo(ref mMem, ref mPointer, ref mPointerIndex); // may recovered from empty list //if (mPointer == null) return; // re-insert data foreach (var item in removedItems.IterateFullReversed()) { removedItems.Remove(item); if (removeStartNode == null) { // insert at first mMem.AddFirst(item); } else { // insert after this node mMem.AddAfter(removeStartNode, item); } } // reset pointer mPointer = oldPointer; mPointerIndex = oldPointerIndex; } } public class AddOperation : RevocableOperation { private long absolutePos; private long count; private float deltaTime; private bool isAddBefore; private LinkedListNode addStartNode; private LinkedListNode oldPointer; private long oldPointerIndex; public AddOperation(long _absolutePos, long _count, float _deltaTime, bool _isAddBefore) : base() { absolutePos = _absolutePos; count = _count; deltaTime = _deltaTime; isAddBefore = _isAddBefore; } public override void Do(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Do(ref mMem, ref mPointer, ref mPointerIndex); if (count <= 0) return; // backups 2 data oldPointer = mPointer; oldPointerIndex = mPointerIndex; // real add operation if (mPointer == null) { // backups start pointer addStartNode = null; // add into blank list, absolutePos and isAddBefore parameters are invalid // specially process for (long i = 0; i < count; i++) { mMem.AddFirst(new FrameData(deltaTime, 0)); } mPointer = mMem.First; mPointerIndex = 0; } else { // normal add LinkedListNode node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos); // backups start pointer addStartNode = node; if (isAddBefore) { for (long i = 0; i < count; i++) { mMem.AddBefore(node, new FrameData(deltaTime, 0)); } } else { for (long i = 0; i < count; i++) { mMem.AddAfter(node, new FrameData(deltaTime, 0)); } } // if the items are added before pointer, the index should add with the count of added items // but pointer don't need to be shifted. if ((isAddBefore && mPointerIndex >= absolutePos) || (!isAddBefore && mPointerIndex > absolutePos)) mPointerIndex += count; } } public override void Undo(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Undo(ref mMem, ref mPointer, ref mPointerIndex); if (count <= 0) return; if (addStartNode == null) { // original state is blank // just clear mmem is ok mMem.Clear(); } else { if (isAddBefore) { for (long i = 0; i < count; i++) { mMem.Remove(addStartNode.Previous); } } else { for (long i = 0; i < count; i++) { mMem.Remove(addStartNode.Next); } } } // re-set pointer mPointer = oldPointer; mPointerIndex = oldPointerIndex; } } public class InsertOperation : RevocableOperation { private long absolutePos; private LinkedList data; private bool isInsertBefore; private bool isOverwritten; private LinkedListNode addStartNode; private bool isBlankList; private LinkedListNode oldPointer; private long oldPointerIndex; // because insert including remove oeration(overwritten mode) // so we need include this for code re-use private RemoveOperation internalRemoveOper; private const long LINKEDLIST_HEAD = -1; private const long LINKEDLIST_TAIL = -2; public InsertOperation(long _absolutePos, LinkedList _data, bool _isInsertBefore, bool _isOverwritten) : base() { absolutePos = _absolutePos; data = _data; isInsertBefore = _isInsertBefore; isOverwritten = _isOverwritten; } public override void Do(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Do(ref mMem, ref mPointer, ref mPointerIndex); if (data.Count == 0) return; // because this oper have internal oper, so we need backup data after potential remove oper // so in there, no object need to be backuped // if the list is blank, overwritten is invalid, just normal add them. if (mPointer == null) { // backups oldPointer = mPointer; oldPointerIndex = mPointerIndex; addStartNode = null; isBlankList = true; foreach (var item in data.IterateFull()) { mMem.AddFirst(item.Value); } mPointer = mMem.First; mPointerIndex = 0; } else { LinkedListNode node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos); // absolutePos is class member and shouldn't be changed. // but in overwritten mode, this value need to be changed so we create a temp value in there // to instead the fucntion of original variable var modifiedAbsolutePos = absolutePos; // if list is not a blank list, we should consider overwritten // if in overwritten mode, we need to overwrite data from selected item. // otherwise, not in overwritten mode, just normally add them just like add operation. if (isOverwritten) { // in overwritten mode, if follwoing item is not enough to fufill the count of overwritten data // normally add them // we use delete and add method to do this // now, try init internal remove oper if in overwritten mode // first, we need compare the length of remained item located in mMem and the length of added item // then construct remove oper long remainLength; if (isInsertBefore) remainLength = absolutePos + 1; else remainLength = mMem.Count - absolutePos; long dataLength = data.Count; long expectedLength = dataLength > remainLength ? remainLength : dataLength; long expectedPos; if (isInsertBefore) expectedPos = absolutePos - expectedLength + 1; else expectedPos = absolutePos + expectedLength - 1; if (isInsertBefore) internalRemoveOper = new RemoveOperation(new SelectionRange(expectedPos, absolutePos)); else internalRemoveOper = new RemoveOperation(new SelectionRange(absolutePos, expectedPos)); node = isInsertBefore ? node.Next : node.Previous; internalRemoveOper.Do(ref mMem, ref mPointer, ref mPointerIndex); // now, we can treat it as normal insert(without overwritten) // but with one exception: absolutePos // we need re calc absolutePos bucause we have called remove oper if (isInsertBefore) { if (node == null) modifiedAbsolutePos = LINKEDLIST_TAIL; else modifiedAbsolutePos = absolutePos + 1 - expectedLength; } else { if (node == null) modifiedAbsolutePos = LINKEDLIST_HEAD; else modifiedAbsolutePos -= 1; } } // backups oldPointer = mPointer; oldPointerIndex = mPointerIndex; addStartNode = node; isBlankList = false; if (isInsertBefore) { foreach (var item in data.IterateFull()) { if (node == null) mMem.AddLast(item.Value); else mMem.AddBefore(node, item.Value); } } else { foreach (var item in data.IterateFullReversed()) { if (node == null) mMem.AddFirst(item.Value); else mMem.AddAfter(node, item.Value); } } if (modifiedAbsolutePos != LINKEDLIST_TAIL && modifiedAbsolutePos != LINKEDLIST_HEAD) { if ((isInsertBefore && mPointerIndex >= modifiedAbsolutePos) || (!isInsertBefore && mPointerIndex > modifiedAbsolutePos)) mPointerIndex += data.Count; } else if (modifiedAbsolutePos == LINKEDLIST_HEAD) mPointerIndex += data.Count; // remove have chance to remove entire list // so we need restore pointer in that situation if (mPointer == null) { mPointer = mMem.First; mPointerIndex = mPointer == null ? -1 : 0; } } } public override void Undo(ref LinkedList mMem, ref LinkedListNode mPointer, ref long mPointerIndex) { base.Undo(ref mMem, ref mPointer, ref mPointerIndex); if (data.Count == 0) return; if (isBlankList) { // original state is blank // just clear mmem is ok mMem.Clear(); // re-set pointer mPointer = oldPointer; mPointerIndex = oldPointerIndex; } else { // in overwrite or not in overwrite mode, we all need to remove inserted data first if (isInsertBefore) { for (long i = 0; i < data.Count; i++) { if (addStartNode == null) mMem.RemoveLast(); else mMem.Remove(addStartNode.Previous); } } else { for (long i = 0; i < data.Count; i++) { if (addStartNode == null) mMem.RemoveFirst(); else mMem.Remove(addStartNode.Next); } } // re-set pointer mPointer = oldPointer; mPointerIndex = oldPointerIndex; // if we use overwrite mode, we need re-add lost data if (isOverwritten) { internalRemoveOper.Undo(ref mMem, ref mPointer, ref mPointerIndex); } } } } }