/******************************************************
 *     ROMVault2 is written by Gordon J.              *
 *     Contact gordon@romvault.com                    *
 *     Copyright 2014                                 *
 ******************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using ROMVault2.IO;
using System.Threading;
using ROMVault2.Properties;
using ROMVault2.RvDB;
using ROMVault2.SupportedFiles;
using ROMVault2.SupportedFiles.Zip;

namespace ROMVault2
{

    public static class FixFiles
    {
        private static Stopwatch _cacheSaveTimer;

        private static string _error;


#if !NEWFINDFIX
        private static List<RvFile> _lstRomTableSortedCRCSize;
        private static List<RvFile> _lstRomTableSortedSHA1CHD;
#endif
        private static List<RvBase> _processList;

        private static int _fixed;
        private static int _reportedFixed;

        private static BackgroundWorker _bgw;

        private static void ReportProgress(object prog)
        {
            _bgw.ReportProgress(0, prog);
        }

        public static void PerformFixes(object sender, DoWorkEventArgs e)
        {
            try
            {

                _cacheSaveTimer = new Stopwatch();
                _cacheSaveTimer.Reset();
                if (Settings.CacheSaveTimerEnabled)
                    _cacheSaveTimer.Start();

                _bgw = sender as BackgroundWorker;
                if (_bgw == null) return;

                Program.SyncCont = e.Argument as SynchronizationContext;
                if (Program.SyncCont == null)
                {
                    _bgw = null;
                    return;
                }

                ReportProgress(new bgwText("Fixing Files"));

                int totalFixes = 0;
                _fixed = 0;
                _reportedFixed = 0;
                for (int i = 0; i < DB.DirTree.ChildCount; i++)
                {
                    RvDir tdir = (RvDir)DB.DirTree.Child(i);
                    totalFixes += CountFixDir(tdir, tdir.Tree.Checked == RvTreeRow.TreeSelect.Selected);
                }
                ReportProgress(new bgwSetRange(totalFixes));

#if !NEWFINDFIX

                DBHelper.GetSelectedFilesSortCRCSize(out _lstRomTableSortedCRCSize);
                DBHelper.GetSelectedFilesSortSHA1CHD(out _lstRomTableSortedSHA1CHD);

                ReportError.ReportList(_lstRomTableSortedCRCSize);
#endif
                _processList = new List<RvBase>();

                for (int i = 0; i < DB.DirTree.ChildCount; i++)
                {
                    RvDir tdir = (RvDir)DB.DirTree.Child(i);
                    ReturnCode returnCode = FixDir(tdir, tdir.Tree.Checked == RvTreeRow.TreeSelect.Selected);
                    if (returnCode != ReturnCode.Good)
                        break;

                    if (_bgw.CancellationPending) break;
                }

#if !NEWFINDFIX
                _lstRomTableSortedCRCSize = null;
                _lstRomTableSortedSHA1CHD = null;
#endif

                ReportProgress(new bgwText("Updating Cache"));
                DB.Write();
                ReportProgress(new bgwText("Complete"));

                _bgw = null;
                Program.SyncCont = null;

            }
            catch (Exception exc)
            {
                ReportError.UnhandledExceptionHandler(exc);

                if (_bgw != null) ReportProgress(new bgwText("Updating Cache"));
                DB.Write();
                if (_bgw != null) ReportProgress(new bgwText("Complete"));

                _bgw = null;
                Program.SyncCont = null;
            }
        }

        private static int CountFixDir(RvDir dir, bool lastSelected)
        {
            int count = 0;

            bool thisSelected = lastSelected;
            if (dir.Tree != null)
                thisSelected = dir.Tree.Checked == RvTreeRow.TreeSelect.Selected;

            for (int j = 0; j < dir.ChildCount; j++)
            {
                RvBase child = dir.Child(j);

                switch (child.FileType)
                {
                    case FileType.Zip:
                        if (!thisSelected)
                            continue;
                        RvDir tZip = (RvDir)child;
                        count += tZip.DirStatus.CountCanBeFixed();

                        break;

                    case FileType.Dir:

                        count += CountFixDir((RvDir)child, thisSelected);
                        break;

                    case FileType.File:
                        if (!thisSelected)
                            continue;
                        if (child.RepStatus == RepStatus.CanBeFixed)
                            count++;
                        break;
                }
            }
            return count;
        }

        private static ReturnCode FixDir(RvDir dir, bool lastSelected)
        {
            Debug.WriteLine(dir.FullName);
            bool thisSelected = lastSelected;
            if (dir.Tree != null)
                thisSelected = dir.Tree.Checked == RvTreeRow.TreeSelect.Selected;


            List<RvBase> lstToProcess = new List<RvBase>();
            for (int j = 0; j < dir.ChildCount; j++)
                lstToProcess.Add(dir.Child(j));

            foreach (RvBase child in lstToProcess)
            {
                ReturnCode returnCode = FixBase(child, thisSelected);
                if (returnCode != ReturnCode.Good)
                    return returnCode;

                while (_processList.Count > 0)
                {
                    returnCode = FixBase(_processList[0], true);
                    if (returnCode != ReturnCode.Good)
                        return returnCode;
                    _processList.RemoveAt(0);
                }

                if (_fixed != _reportedFixed)
                {
                    ReportProgress(new bgwProgress(_fixed));
                    _reportedFixed = _fixed;
                }
                if (_bgw.CancellationPending) break;
            }
            // here we check to see if the directory we just scanned should be deleted
            CheckDeleteObject(dir);
            return ReturnCode.Good;
        }

        private static ReturnCode FixBase(RvBase child, bool thisSelected)
        {
            // skip any files that have already been deleted
            if (child.RepStatus == RepStatus.Deleted)
                return ReturnCode.Good;


            if (_cacheSaveTimer.Elapsed.Minutes > Settings.CacheSaveTimePeriod)
            {
                ReportProgress("Saving Cache");
                DB.Write();
                ReportProgress("Saving Cache Complete");
                _cacheSaveTimer.Reset();
                _cacheSaveTimer.Start();
            }

            ReturnCode returnCode = ReturnCode.LogicError;
            switch (child.FileType)
            {
                case FileType.Zip:
                    if (!thisSelected)
                        return ReturnCode.Good;

                    if (!String.IsNullOrEmpty(child.FileName))
                    {
                        string strDir = child.Parent.FullName;
                        File.Move(Path.Combine(strDir, child.FileName + ".zip"), Path.Combine(strDir, child.Name + ".zip"));
                        child.FileName = null;
                    }

                    do
                    {
                        returnCode = FixZip((RvDir)child);
                    } while (returnCode == ReturnCode.StartOver);
                    break;

                case FileType.Dir:
                    if (thisSelected)
                    {
                        if (!String.IsNullOrEmpty(child.FileName))
                        {
                            string strDir = child.Parent.FullName;
                            System.IO.Directory.Move(Path.Combine(strDir, child.FileName), Path.Combine(strDir, "__RomVault.tmpDir"));
                            Directory.Move(Path.Combine(strDir, "__RomVault.tmpDir"), Path.Combine(strDir, child.Name));
                            child.FileName = null;
                        }
                    }

                    returnCode = FixDir((RvDir)child, thisSelected);
                    return returnCode;

                case FileType.File:
                    if (!thisSelected)
                        return ReturnCode.Good;

                    do
                    {
                        returnCode = FixFile((RvFile)child);
                    } while (returnCode == ReturnCode.StartOver);
                    break;
            }
            switch (returnCode)
            {
                case ReturnCode.Good:
                    // all good, move alone.
                    break;
                case ReturnCode.RescanNeeded:
                    ReportError.Show(_error);
                    break;
                case ReturnCode.LogicError:
                    ReportError.UnhandledExceptionHandler(_error);
                    break;
                case ReturnCode.FileSystemError:
                    ReportError.Show(_error);
                    break;
                case ReturnCode.FindFixes:
                    ReportError.Show("You Need to Find Fixes before Fixing. (Incorrect File Status's found for fixing.)");
                    break;
                default:
                    ReportError.UnhandledExceptionHandler(Resources.FixFiles_FixDirChildren_Unknown_result_type + returnCode);
                    break;
            }
            return returnCode;

        }


        private static ReturnCode FixFile(RvFile fixFile)
        {
            switch (fixFile.RepStatus)
            {
                case RepStatus.Unknown:
                    return ReturnCode.FindFixes;


                case RepStatus.UnScanned:
                    return ReturnCode.Good;

                case RepStatus.Missing:
                    // nothing can be done so moving right along
                    return ReturnCode.Good;


                case RepStatus.Correct:
                    // this is correct nothing to be done here
                    FixFileCheckName(fixFile);
                    return ReturnCode.Good;


                case RepStatus.NotCollected:
                    // this is correct nothing to be done here
                    return ReturnCode.Good;

                // Unknown

                case RepStatus.Ignore:
                    // this is correct nothing to be done here
                    return ReturnCode.Good;

                // Corrupt 

                case RepStatus.InToSort:
                    // this is correct nothing to be done here
                    return ReturnCode.Good;


                case RepStatus.Delete:
                    return FixFileDelete(fixFile);

                case RepStatus.MoveToSort:
                    return FixFileMoveToSort(fixFile);

                case RepStatus.MoveToCorrupt:
                    return FixFileMoveToCorrupt(fixFile);

                case RepStatus.CanBeFixed:
                case RepStatus.CorruptCanBeFixed:
                    return FixFileCanBeFixed(fixFile);

                case RepStatus.NeededForFix:
                    // this file can be left as is, it will be used to fix a file, and then marked to be deleted.
                    return ReturnCode.Good;

                    // this is for a corrupt CHD already in ToSort
                case RepStatus.Corrupt:
                    return ReturnCode.Good;

                case RepStatus.Rename:
                    // this file will be used and mark to be deleted in the CanBeFixed
                    // so nothing to be done to it here
                    return ReturnCode.Good;


                default:
                    ReportError.UnhandledExceptionHandler(Resources.FixFiles_FixFile_Unknown_fix_file_type + fixFile.RepStatus + " Dat Status = " + fixFile.DatStatus + " GotStatus " + fixFile.GotStatus);
                    return ReturnCode.LogicError;
            }
        }

        private static void FixFileCheckName(RvFile fixFile)
        {
            if (!String.IsNullOrEmpty(fixFile.FileName))
            {
                string sourceFullName = Path.Combine(fixFile.Parent.FullName, fixFile.FileName);
                if (!File.SetAttributes(sourceFullName, FileAttributes.Normal))
                {
                    int error = Error.GetLastError();
                    ReportProgress(new bgwShowError(sourceFullName, "Error Setting File Attributes to Normal. Before Case correction Rename. Code " + error));
                }

                File.Move(sourceFullName, fixFile.FullName);
                fixFile.FileName = null;
            }
        }

        private static ReturnCode FixFileDelete(RvFile fixFile)
        {
            ReturnCode retCode = DoubleCheckDelete(fixFile);
            if (retCode != ReturnCode.Good)
                return retCode;

            string filename = fixFile.FullName;
            if (File.Exists(filename))
            {
                if (!File.SetAttributes(filename, FileAttributes.Normal))
                {
                    int error = Error.GetLastError();
                    ReportProgress(new bgwShowError(filename, "Error Setting File Attributes to Normal. Before Delete. Code " + error));
                }
                File.Delete(filename);
            }
            // here we just deleted a file so also delete it from the DB,
            // and recurse up deleting unnedded DIR's
            CheckDeleteObject(fixFile);
            return ReturnCode.Good;
        }

        private static ReturnCode FixFileMoveToSort(RvFile fixFile)
        {
            string fixFileFullName = fixFile.FullName;

            string toSortFullName = Path.Combine(Settings.ToSort(), fixFile.Name);
            string toSortFileName = fixFile.Name;
            int fileC = 0;
            while (File.Exists(toSortFullName))
            {
                fileC++;
                toSortFullName = Path.Combine(Settings.ToSort(), fixFile.Name + fileC);
                toSortFileName = fixFile.Name + fileC;
            }

            //create new tosort record
            // FileInfo toSortFile = new FileInfo(toSortFullName);
            RvFile toSortRom = new RvFile(FileType.File)
            {
                Name = toSortFileName,
                Size = fixFile.Size,
                CRC = fixFile.CRC,
                //TimeStamp = toSortFile.LastWriteTime,
                DatStatus = DatStatus.InToSort
            };

            ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixFileFullName), "", Path.GetFileName(fixFileFullName), fixFile.Size, "-->", "ToSort", "", fixFile.Name));

            ZipFile tempZipOut = null;
            RvFile foundFile;
            ReturnCode returnCode = FixFileCopy.CopyFile(fixFile, ref tempZipOut, toSortFullName, toSortRom, false, out _error, out foundFile);
            switch (returnCode)
            {
                case ReturnCode.Good: // correct reply to continue;
                    break;
                default:
                    return returnCode;
            }

            string fixFilePath = fixFile.FullName;
            if (!File.SetAttributes(fixFilePath, FileAttributes.Normal))
            {
                int error = Error.GetLastError();
                ReportProgress(new bgwShowError(fixFilePath, "Error Setting File Attributes to Normal. Before Delete Moving ToSort. Code " + error));
            }
            File.Delete(fixFilePath);

            // here we just deleted a file so also delete it from the DB,
            // and recurse up deleting unnedded DIR's
            CheckDeleteObject(fixFile);

            RvDir toSort = (RvDir)DB.DirTree.Child(1);
            toSort.ChildAdd(toSortRom);

            return ReturnCode.Good;

        }

        private static ReturnCode FixFileMoveToCorrupt(RvFile fixFile)
        {
            string corruptDir = Path.Combine(Settings.ToSort(), "Corrupt");
            if (!Directory.Exists(corruptDir))
            {
                Directory.CreateDirectory(corruptDir);
            }

            string fixFileFullName = fixFile.FullName;

            string toSortCorruptFullName = Path.Combine(corruptDir, fixFile.Name);
            string toSortCorruptFileName = fixFile.Name;
            int fileC = 0;
            while (File.Exists(toSortCorruptFullName))
            {
                fileC++;
                toSortCorruptFileName = fixFile.Name + fileC;
                toSortCorruptFullName = Path.Combine(corruptDir, toSortCorruptFileName);
            }

            //create new tosort record
            // FileInfo toSortCorruptFile = new FileInfo(toSortCorruptFullName);
            RvFile toSortCorruptRom = new RvFile(FileType.File)
            {
                Name = toSortCorruptFileName,
                Size = fixFile.Size,
                CRC = fixFile.CRC,
                //TimeStamp = toSortFile.LastWriteTime,
                DatStatus = DatStatus.InToSort
            };

            _bgw.ReportProgress(0, new bgwShowFix(Path.GetDirectoryName(fixFileFullName), "", Path.GetFileName(fixFileFullName), fixFile.Size, "-->", "Corrupt", "", fixFile.Name));

            ZipFile tempZipOut = null;
            RvFile foundFile;
            ReturnCode returnCode = FixFileCopy.CopyFile(fixFile, ref tempZipOut, toSortCorruptFullName, toSortCorruptRom, false, out _error, out foundFile);
            switch (returnCode)
            {
                case ReturnCode.Good: // correct reply to continue;
                    break;
                default:
                    return returnCode;
            }

            string fixFilePath = fixFile.FullName;
            if (!File.SetAttributes(fixFilePath, FileAttributes.Normal))
            {
                int error = Error.GetLastError();
                _bgw.ReportProgress(0, new bgwShowError(fixFilePath, "Error Setting File Attributes to Normal. Before Delete Moving ToSort. Code " + error));
            }
            File.Delete(fixFilePath);

            // here we just deleted a file so also delete it from the DB,
            // and recurse up deleting unnedded DIR's
            CheckDeleteObject(fixFile);

            RvDir toSort = (RvDir)DB.DirTree.Child(1);
            int indexcorrupt;
            RvDir rvCorruptDir = new RvDir(FileType.Dir) { Name = "Corrupt", DatStatus = DatStatus.InToSort };
            int found = toSort.ChildNameSearch(rvCorruptDir, out indexcorrupt);
            if (found != 0)
            {
                rvCorruptDir.GotStatus = GotStatus.Got;
                indexcorrupt = toSort.ChildAdd(rvCorruptDir);
            }

            ((RvDir)toSort.Child(indexcorrupt)).ChildAdd(toSortCorruptRom);

            return ReturnCode.Good;
        }


        private static ReturnCode FixFilePreCheckFixFile(RvFile fixFile)
        {
            string fileName = fixFile.FullName;

            // find all files in the DB with this name
            // there could be another file if:
            // there is a wrong file with the same name that can just be deleted
            // there is a wrong file with the same name that needs moved to ToSort
            // there is a wrong file with the same name that is needed to fix another file
            List<RvBase> testList = new List<RvBase>();

            RvDir parent = fixFile.Parent;
            int index;
            // start by finding the first file in the DB. (This should always work, as it will at least find the current file that CanBeFixed
            if (parent.ChildNameSearch(fixFile, out index) != 0)
            {
                ReportError.Show("Logic error trying to find the file we are fixing " + fileName);
                return ReturnCode.LogicError;
            }
            testList.Add(parent.Child(index++));

            // now loop to see if there are any more files with the same name. (This is a case insensative compare)                        
            while (index < parent.ChildCount && DBHelper.CompareName(fixFile, parent.Child(index)) == 0)
            {
                testList.Add(parent.Child(index));
                index++;
            }

            // if we found more that one file in the DB then we need to process the incorrect file first.
            if (testList.Count > 1)
            {
                foreach (RvBase testChild in testList)
                {
                    if (testChild == fixFile)
                        continue;

                    if (testChild.DatStatus != DatStatus.NotInDat)
                    {
                        ReportError.Show(Resources.FixFiles_FixFile_Trying_to_fix_a_file_that_already_exists + fileName);
                        return ReturnCode.LogicError;
                    }

                    RvFile testFile = testChild as RvFile;
                    if (testFile == null)
                    {
                        ReportError.Show("Did not find a file logic error while fixing duplicate named file. in FixFile");
                        return ReturnCode.LogicError;
                    }

                    switch (testFile.RepStatus)
                    {
                        case RepStatus.Delete:
                            {
                                ReturnCode ret = FixFileDelete(testFile);
                                if (ret != ReturnCode.Good)
                                    return ret;
                                break;
                            }
                        case RepStatus.MoveToSort:
                            {
                                ReturnCode ret = FixFileMoveToSort(testFile);
                                if (ret != ReturnCode.Good)
                                    return ret;
                                break;
                            }
                        case RepStatus.MoveToCorrupt:
                            {
                                ReturnCode ret = FixFileMoveToCorrupt(testFile);
                                if (ret != ReturnCode.Good)
                                    return ret;
                                break;
                            }
                        case RepStatus.NeededForFix:
                        case RepStatus.Rename:
                            {
                                // so now we have found the file with the same case insensative name and can rename it to something else to get it out of the way for now.
                                // need to check that the .tmp filename does not already exists.
                                File.SetAttributes(testChild.FullName, FileAttributes.Normal);
                                File.Move(testChild.FullName, testChild.FullName + ".tmp");

                                if (!parent.FindChild(testChild, out index))
                                {
                                    ReportError.Show("Unknown file status in Matching File found of " + testFile.RepStatus);
                                    return ReturnCode.LogicError;
                                }
                                parent.ChildRemove(index);
                                testChild.Name = testChild.Name + ".tmp";
                                parent.ChildAdd(testChild);
                                break;
                            }
                        default:
                            {
                                ReportError.Show("Unknown file status in Matching File found of " + testFile.RepStatus);
                                return ReturnCode.LogicError;
                            }
                    }
                }
            }
            else
            {
                // if there is only one file in the DB then it must be the current file that CanBeFixed
                if (testList[0] != fixFile)
                {
                    ReportError.Show("Logic error trying to find the file we are fixing " + fileName + " DB found file does not match");
                    return ReturnCode.LogicError;
                }
            }
            return ReturnCode.Good;
        }

        private static ReturnCode FixFileCanBeFixed(RvFile fixFile)
        {
            string fixFileFullName = fixFile.FullName;
            CheckCreateParent(fixFile.Parent);

            // check to see if there is already a file with the name of the fixFile, and move it out the way.
            ReturnCode rc = FixFilePreCheckFixFile(fixFile);
            if (rc != ReturnCode.Good)
                return rc;


            // now we can fix the file.
            ZipFile tempZipOut = null;
            RvFile foundFile;
            ReturnCode returnCode;

            if (DBHelper.IsZeroLengthFile(fixFile))
            {
                RvFile fileIn = new RvFile(FileType.File);
                returnCode = FixFileCopy.CopyFile(fileIn, ref tempZipOut, fixFile.FullName, fixFile, false, out _error, out foundFile);

                switch (returnCode)
                {
                    case ReturnCode.Good: // correct reply to continue;
                        break;
                    default:
                        _error = fixFile.FullName + " " + fixFile.RepStatus + " " + returnCode + " : " + _error;
                        ReCheckFile(fixFile);
                        return ReturnCode.StartOver;
                }
                _fixed++;
                return ReturnCode.Good;
            }

#if NEWFINDFIX
                List<RvFile> lstFixRomTable = new List<RvFile>();
                List<RvFile> family = fixFile.MyFamily.Family;
                for (int iFind = 0; iFind < family.Count; iFind++)
                {
                    if (family[iFind].GotStatus == GotStatus.Got && FindFixes.CheckIfMissingFileCanBeFixedByGotFile(fixFile, family[iFind]))
                        lstFixRomTable.Add(family[iFind]);
                }
            RvFile fixingFile = lstFixRomTable[0];
#else

            // search for the database for the file to be used to repair this file:
            List<RvFile> lstFixRomTableCRC;
            DBHelper.RomSearchFindFixes(fixFile, _lstRomTableSortedCRCSize, out lstFixRomTableCRC);

            List<RvFile> lstFixRomTableSHA1CHD;
            DBHelper.RomSearchFindFixesSHA1CHD(fixFile, _lstRomTableSortedSHA1CHD, out lstFixRomTableSHA1CHD);

            if (lstFixRomTableCRC.Count == 0 && lstFixRomTableSHA1CHD.Count == 0)
            {
                // thought we could fix the file, turns out we cannot
                fixFile.GotStatus = GotStatus.NotGot;
                return ReturnCode.Good;
            }

            RvFile fixingFile =
                lstFixRomTableCRC.Count > 0 ?
                lstFixRomTableCRC[0] :
                lstFixRomTableSHA1CHD[0];
#endif
            string fts = fixingFile.FullName;
            ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixFileFullName), "", Path.GetFileName(fixFileFullName), fixFile.Size, "<--", Path.GetDirectoryName(fts), Path.GetFileName(fts), fixingFile.Name));



            returnCode = FixFileCopy.CopyFile(fixingFile, ref tempZipOut, fixFile.FullName, fixFile, false, out _error, out foundFile);

            switch (returnCode)
            {
                case ReturnCode.Good: // correct reply to continue;
                    break;

                case ReturnCode.SourceCRCCheckSumError:
                    {
                        ReportProgress(new bgwShowFixError("CRC Error"));
                        // the file we used for fix turns out to be corrupt

                        // mark the source file as Corrupt
                        fixingFile.GotStatus = GotStatus.Corrupt;

                        // recheck for the fix
                        ReCheckFile(fixFile);

                        CheckReprocess(fixingFile);

                        // and go back one and try again.
                        return ReturnCode.StartOver;
                    }


                case ReturnCode.SourceCheckSumError:
                    {
                        // the file we used for fix turns out not not match its own DAT's correct MD5/SHA1
                        // (Problem with logic here is that it could still match the file being fixed, but this case is not correctly handled)
                        ReportProgress(new bgwShowFixError("Failed"));


                        // remove the file we thought we correctly had (The file that we where trying to use for the fix)
                        if (fixingFile.FileRemove() == EFile.Delete)
                        {
                            _error = "Should not mark for delete as it is in a DAT";
                            return ReturnCode.LogicError;
                        }

                        // possibly use a check here to see if the index of the found file is futher down the zip and so we can just contine
                        // instead of restarting.

                        // add in the actual file we found
                        fixingFile.Parent.ChildAdd(foundFile);
                        AddFoundFile(foundFile);

                        // recheck for the fix
                        ReCheckFile(fixFile);

                        CheckReprocess(fixingFile);

                        // and go back one and try again.
                        return ReturnCode.StartOver;
                    }
                case ReturnCode.DestinationCheckSumError:
                    {
                        ReportProgress(new bgwShowFixError("Failed"));

                        // recheck for the fix
                        ReCheckFile(fixFile);
                        return ReturnCode.StartOver;
                    }
                default:
                    return returnCode;
            }


            CheckReprocessClearList();
            // Check the files that we found that where used to fix this file, and if they not listed as correct files, they can be set to be deleted.
            
#if NEWFINDFIX
            foreach (RvFile file in lstFixRomTable)
            {
                if (file.RepStatus != RepStatus.NeededForFix && file.RepStatus != RepStatus.Rename) continue;
                file.RepStatus = RepStatus.Delete;
                CheckReprocess(file, true);
            }
#else

            foreach (RvFile file in lstFixRomTableCRC)
            {
                if (file.RepStatus != RepStatus.NeededForFix && file.RepStatus != RepStatus.Rename) continue;
                file.RepStatus = RepStatus.Delete;
                CheckReprocess(file, true);
            }
            foreach (RvFile file in lstFixRomTableSHA1CHD)
            {
                if (file.RepStatus != RepStatus.NeededForFix && file.RepStatus != RepStatus.Rename) continue;
                file.RepStatus = RepStatus.Delete;
                CheckReprocess(file, true);
            }
#endif
            CheckReprocessFinalCheck();

            _fixed++;

            return ReturnCode.Good;
        }



        private static ReturnCode FixZip(RvDir fixZip)
        {
            //Check for error status
            if (fixZip.DirStatus.HasUnknown())
                return ReturnCode.FindFixes; // Error

            bool needsTrrntzipped = fixZip.ZipStatus != ZipStatus.TrrntZip && fixZip.GotStatus == GotStatus.Got && fixZip.DatStatus == DatStatus.InDatCollect && (Settings.FixLevel == eFixLevel.TrrntZipLevel1 || Settings.FixLevel == eFixLevel.TrrntZipLevel2 || Settings.FixLevel == eFixLevel.TrrntZipLevel3);

            // file corrupt and not in tosort
            //      if file cannot be fully fixed copy to corrupt
            //      process zipfile

            if (fixZip.GotStatus == GotStatus.Corrupt && fixZip.DatStatus != DatStatus.InToSort)
            {
                ReturnCode movReturnCode = MoveZiptoCorrupt(fixZip);
                if (movReturnCode != ReturnCode.Good)
                    return movReturnCode;
            }

            // has fixable
            //      process zipfile

            else if (fixZip.DirStatus.HasFixable())
            {
                // do nothing here but continue on to process zip.
            }

            // need trrntzipped
            //      process zipfile

            else if (needsTrrntzipped)
            {
                // do nothing here but continue on to process zip.
            }


            // got empty zip that should be deleted
            //      process zipfile
            else if (fixZip.GotStatus == GotStatus.Got && fixZip.GotStatus != GotStatus.Corrupt && !fixZip.DirStatus.HasAnyFiles())
            {
                // do nothing here but continue on to process zip.
            }

            // else
            //      skip this zipfile
            else
            {
                // nothing can be done to return
                return ReturnCode.Good;
            }



            string fixZipFullName = fixZip.TreeFullName;

            if (!fixZip.DirStatus.HasFixable() && needsTrrntzipped)
                ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixZipFullName), Path.GetFileName(fixZipFullName), "", 0, "TrrntZipping", "", "", ""));


            CheckCreateParent(fixZip.Parent);
            ReportError.LogOut("");
            ReportError.LogOut(fixZipFullName + " : " + fixZip.RepStatus);
            ReportError.LogOut("------------------------------------------------------------");
            Debug.WriteLine(fixZipFullName + " : " + fixZip.RepStatus);
            ReportError.LogOut("Zip File Status Before Fix:");
            for (int intLoop = 0; intLoop < fixZip.ChildCount; intLoop++)
                ReportError.LogOut((RvFile)fixZip.Child(intLoop));
            ReportError.LogOut("");

            ZipFile tempZipOut = null;

            ZipFile toSortCorruptOut = null;
            ZipFile toSortZipOut = null;

            RvDir toSortGame = null;
            RvDir toSortCorruptGame = null;

            ReturnCode returnCode;
            List<RvFile> fixZipTemp = new List<RvFile>();

            for (int iRom = 0; iRom < fixZip.ChildCount; iRom++)
            {
                RvFile zipFileFixing = new RvFile(FileType.ZipFile);
                fixZip.Child(iRom).CopyTo(zipFileFixing);

                if (iRom == fixZipTemp.Count)
                    fixZipTemp.Add(zipFileFixing);
                else
                    fixZipTemp[iRom] = zipFileFixing;

                ReportError.LogOut(zipFileFixing.RepStatus + " : " + fixZip.Child(iRom).FullName);

                switch (zipFileFixing.RepStatus)
                {
                    #region Nothing to copy
                    // any file we do not have or do not want in the destination zip
                    case RepStatus.Missing:
                    case RepStatus.NotCollected:
                    case RepStatus.Rename:
                    case RepStatus.Delete:
                        if (!
                              (
                            // got the file in the original zip but will be deleting it
                                (zipFileFixing.DatStatus == DatStatus.NotInDat && zipFileFixing.GotStatus == GotStatus.Got) ||
                                (zipFileFixing.DatStatus == DatStatus.NotInDat && zipFileFixing.GotStatus == GotStatus.Corrupt) ||
                                (zipFileFixing.DatStatus == DatStatus.InDatMerged && zipFileFixing.GotStatus == GotStatus.Got) ||
                                (zipFileFixing.DatStatus == DatStatus.InToSort && zipFileFixing.GotStatus == GotStatus.Got) ||
                                (zipFileFixing.DatStatus == DatStatus.InToSort && zipFileFixing.GotStatus == GotStatus.Corrupt) ||

                                // do not have this file and cannot fix it here
                                (zipFileFixing.DatStatus == DatStatus.InDatCollect && zipFileFixing.GotStatus == GotStatus.NotGot) ||
                                (zipFileFixing.DatStatus == DatStatus.InDatBad && zipFileFixing.GotStatus == GotStatus.NotGot) ||
                                (zipFileFixing.DatStatus == DatStatus.InDatMerged && zipFileFixing.GotStatus == GotStatus.NotGot)
                                )
                            )
                            ReportError.SendAndShow(Resources.FixFiles_FixZip_Error_in_Fix_Rom_Status + zipFileFixing.RepStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.DatStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.GotStatus);

                        if (zipFileFixing.RepStatus == RepStatus.Delete)
                        {
                            returnCode = DoubleCheckDelete(zipFileFixing);
                            if (returnCode != ReturnCode.Good)
                                goto ZipOpenFailed;
                        }

                        zipFileFixing.GotStatus = GotStatus.NotGot;
                        break;
                    #endregion
                    #region Copy from Original to Destination
                    // any files we are just moving from the original zip to the destination zip
                    case RepStatus.Correct:
                    case RepStatus.InToSort:
                    case RepStatus.NeededForFix:
                    case RepStatus.Corrupt:
                        {
                            if (!
                                  (
                                    (zipFileFixing.DatStatus == DatStatus.InDatCollect && zipFileFixing.GotStatus == GotStatus.Got) ||
                                    (zipFileFixing.DatStatus == DatStatus.InDatMerged && zipFileFixing.GotStatus == GotStatus.Got) ||
                                    (zipFileFixing.DatStatus == DatStatus.NotInDat && zipFileFixing.GotStatus == GotStatus.Got) ||
                                    (zipFileFixing.DatStatus == DatStatus.InToSort && zipFileFixing.GotStatus == GotStatus.Got) ||
                                    (zipFileFixing.DatStatus == DatStatus.InToSort && zipFileFixing.GotStatus == GotStatus.Corrupt)
                                  )
                                )
                                ReportError.SendAndShow(Resources.FixFiles_FixZip_Error_in_Fix_Rom_Status + zipFileFixing.RepStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.DatStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.GotStatus);

                            RvFile foundFile;

                            bool rawcopy = (zipFileFixing.RepStatus == RepStatus.InToSort) || (zipFileFixing.RepStatus == RepStatus.Corrupt);
                            // Correct      rawcopy=false
                            // NeededForFix rawcopy=false
                            // InToSort     rawcopy=true
                            // Corrupt      rawcopy=true
                            RepStatus originalStatus = zipFileFixing.RepStatus;

                            returnCode = FixFileCopy.CopyFile(
                                (RvFile)fixZip.Child(iRom),
                                ref tempZipOut,
                                Path.Combine(fixZip.Parent.FullName, "__RomVault.tmp"),
                                zipFileFixing, rawcopy,
                                out _error, out foundFile);

                            switch (returnCode)
                            {
                                case ReturnCode.Good: // correct reply to continue;
                                    if (originalStatus == RepStatus.NeededForFix)
                                        zipFileFixing.RepStatus = RepStatus.NeededForFix;
                                    break;
                                case ReturnCode.SourceCRCCheckSumError:
                                    {
                                        RvFile tFile = (RvFile)fixZip.Child(iRom);
                                        tFile.GotStatus = GotStatus.Corrupt;
                                        ReCheckFile(tFile);

                                        //decrease index so this file gets reprocessed
                                        iRom--;

                                        continue;
                                    }
                                case ReturnCode.SourceCheckSumError:
                                    {
                                        // Set the file in the zip that we thought we correctly had as missing
                                        RvFile tFile = (RvFile)fixZip.Child(iRom);
                                        if (tFile.FileRemove() == EFile.Delete)
                                        {
                                            _error = "Should not mark for delete as it is in a DAT";
                                            return ReturnCode.LogicError;
                                        }

                                        // Add in at the current location the incorrect file. (This file will be reprocessed and then at some point deleted.)
                                        fixZip.ChildAdd(foundFile, iRom);
                                        AddFoundFile(foundFile);

                                        ReCheckFile(tFile);

                                        //decrease index so this file gets reprocessed
                                        iRom--;

                                        continue;
                                    }
                                // not needed as source and destination are the same
                                // case ReturnCode.DestinationCheckSumError:  
                                default:
                                    _error = zipFileFixing.FullName + " " + zipFileFixing.RepStatus + " " + returnCode + " : " + _error;
                                    goto ZipOpenFailed;
                            }
                        }
                        break;
                    #endregion

                    #region Case.CanBeFixed
                    case RepStatus.CanBeFixed:
                    case RepStatus.CorruptCanBeFixed:
                        {
                            if (!(zipFileFixing.DatStatus == DatStatus.InDatCollect && (zipFileFixing.GotStatus == GotStatus.NotGot || zipFileFixing.GotStatus == GotStatus.Corrupt)))
                                ReportError.SendAndShow(Resources.FixFiles_FixZip_Error_in_Fix_Rom_Status + zipFileFixing.RepStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.DatStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.GotStatus);

                            ReportError.LogOut("Fixing File:");
                            ReportError.LogOut(zipFileFixing);

                            string strPath = fixZip.Parent.FullName;
                            string tempZipFilename = Path.Combine(strPath, "__RomVault.tmp");


                            if (DBHelper.IsZeroLengthFile(zipFileFixing))
                            {
                                RvFile fileIn = new RvFile(FileType.ZipFile) { Size = 0 };
                                RvFile foundFile;
                                returnCode = FixFileCopy.CopyFile(fileIn, ref tempZipOut, tempZipFilename, zipFileFixing, false, out _error, out foundFile);

                                switch (returnCode)
                                {
                                    case ReturnCode.Good: // correct reply to continue;
                                        break;
                                    default:
                                        _error = zipFileFixing.FullName + " " + zipFileFixing.RepStatus + " " + returnCode + " : " + _error;
                                        goto ZipOpenFailed;
                                }
                                break;
                            }

#if NEWFINDFIX
                            List<RvFile> lstFixRomTable = new List<RvFile>();
                            List<RvFile> family = zipFileFixing.MyFamily.Family;
                            for (int iFind = 0; iFind < family.Count; iFind++)
                            {
                                if (family[iFind].GotStatus == GotStatus.Got && FindFixes.CheckIfMissingFileCanBeFixedByGotFile(zipFileFixing, family[iFind]))
                                    lstFixRomTable.Add(family[iFind]);
                            }
#else
                            List<RvFile> lstFixRomTable;
                            DBHelper.RomSearchFindFixes(zipFileFixing, _lstRomTableSortedCRCSize, out lstFixRomTable);
#endif

                            ReportError.LogOut("Found Files To use for Fixes:");
                            foreach (RvFile t in lstFixRomTable)
                                ReportError.LogOut(t);

                            if (lstFixRomTable.Count > 0)
                            {

                                string ts = lstFixRomTable[0].Parent.FullName;
                                string sourceDir;
                                string sourceFile;
                                if (lstFixRomTable[0].FileType == FileType.ZipFile)
                                {
                                    sourceDir = Path.GetDirectoryName(ts);
                                    sourceFile = Path.GetFileName(ts);
                                }
                                else
                                {
                                    sourceDir = ts;
                                    sourceFile = "";
                                }
                                ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixZipFullName), Path.GetFileName(fixZipFullName), zipFileFixing.Name, zipFileFixing.Size, "<--", sourceDir, sourceFile, lstFixRomTable[0].Name));

                                RvFile foundFile;
                                returnCode = FixFileCopy.CopyFile(lstFixRomTable[0], ref tempZipOut, tempZipFilename, zipFileFixing, false, out _error, out foundFile);
                                switch (returnCode)
                                {
                                    case ReturnCode.Good: // correct reply so continue;
                                        break;

                                    case ReturnCode.SourceCRCCheckSumError:
                                        {
                                            ReportProgress(new bgwShowFixError("CRC Error"));
                                            // the file we used for fix turns out to be corrupt

                                            RvFile tFile = (RvFile)fixZip.Child(iRom);

                                            // mark the source file as Corrupt
                                            lstFixRomTable[0].GotStatus = GotStatus.Corrupt;

                                            // recheck for the fix
                                            ReCheckFile(tFile);

                                            // if the file being used from the fix is actually from this file then we have a big mess, and we are just going to
                                            // start over on this zip.
                                            if (lstFixRomTable[0].Parent == fixZip)
                                            {
                                                returnCode = ReturnCode.StartOver;
                                                goto ZipOpenFailed;
                                            }

                                            // add the fixing source zip into the processList so that it is also reprocessed and we just changed it.
                                            if (!_processList.Contains(lstFixRomTable[0].Parent))
                                                _processList.Add(lstFixRomTable[0].Parent);

                                            // and go back one and try again.
                                            iRom--;
                                            continue;
                                        }


                                    case ReturnCode.SourceCheckSumError:
                                        {
                                            ReportProgress(new bgwShowFixError("Failed"));
                                            // the file we used for fix turns out not not match its own DAT's correct MD5/SHA1
                                            // (Problem with logic here is that it could still match the file being fixed, but this case is not correctly handled)

                                            RvFile tFile = (RvFile)fixZip.Child(iRom);

                                            // remove the file we thought we correctly had (The file that we where trying to use for the fix)
                                            if (lstFixRomTable[0].FileRemove() == EFile.Delete)
                                            {
                                                _error = "Should not mark for delete as it is in a DAT";
                                                return ReturnCode.LogicError;
                                            }

                                            // possibly use a check here to see if the index of the found file is futher down the zip and so we can just contine
                                            // instead of restarting.

                                            // add in the actual file we found
                                            lstFixRomTable[0].Parent.ChildAdd(foundFile);
                                            AddFoundFile(foundFile);

                                            // recheck for the fix
                                            ReCheckFile(tFile);

                                            // if the file being used from the fix is actually from this file then we have a big mess, and we are just going to
                                            // start over on this zip.
                                            if (lstFixRomTable[0].Parent == fixZip)
                                            {
                                                returnCode = ReturnCode.StartOver;
                                                goto ZipOpenFailed;
                                            }

                                            // add the fixing source zip into the processList so that it is also reprocessed and we just changed it.
                                            if (!_processList.Contains(lstFixRomTable[0].Parent))
                                                _processList.Add(lstFixRomTable[0].Parent);

                                            // and go back one and try again.
                                            iRom--;
                                            continue;
                                        }
                                    case ReturnCode.DestinationCheckSumError:
                                        {
                                            ReportProgress(new bgwShowFixError("Failed"));
                                            // the file we used for fix turns out not to have the correct MD5/SHA1 
                                            RvFile tFile = (RvFile)fixZip.Child(iRom);

                                            // recheck for the fix
                                            ReCheckFile(tFile);

                                            // if the file being used from the fix is actually from this file then we have a big mess, and we are just going to
                                            // start over on this zip.
                                            // The need for this is that the file being pulled in from inside this zip will be marked as Rename
                                            // and so would then automatically be deleted, in the case this exception happens, this source file instead
                                            // should be set to move to tosort. 
                                            if (lstFixRomTable[0].Parent == fixZip)
                                            {
                                                returnCode = ReturnCode.StartOver;
                                                goto ZipOpenFailed;
                                            }

                                            // and go back one and try again.
                                            iRom--;
                                            continue;
                                        }
                                    default:
                                        //_error = zipFileFixing.FullName + " " + zipFileFixing.RepStatus + " " + returnCode + Environment.NewLine + _error;
                                        goto ZipOpenFailed;
                                }

                                //Check to see if the files used for fix, can now be set to delete
                                CheckReprocessClearList();

                                foreach (RvFile tFixRom in lstFixRomTable)
                                {
                                    if (tFixRom.RepStatus == RepStatus.NeededForFix)
                                    {
                                        tFixRom.RepStatus = RepStatus.Delete;
                                        ReportError.LogOut("Setting File Status to Delete:");
                                        ReportError.LogOut(tFixRom);
                                        CheckReprocess(tFixRom, true);
                                    }
                                }
                                CheckReprocessFinalCheck();

                                _fixed++;
                            }
                            else
                                // thought we could fix it, turns out we cannot
                                zipFileFixing.GotStatus = GotStatus.NotGot;
                        }
                        break;
                    #endregion
                    #region Case.MoveToSort
                    case RepStatus.MoveToSort:
                        {
                            if (!(zipFileFixing.DatStatus == DatStatus.NotInDat && zipFileFixing.GotStatus == GotStatus.Got))
                                ReportError.SendAndShow(Resources.FixFiles_FixZip_Error_in_Fix_Rom_Status + zipFileFixing.RepStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.DatStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.GotStatus);

                            ReportError.LogOut("Moving File out to ToSort:");
                            ReportError.LogOut(zipFileFixing);
                            // move the rom out to the To Sort Directory

                            if (toSortGame == null)
                            {
                                string toSortFullName = Path.Combine(Settings.ToSort(), fixZip.Name + ".zip");
                                string toSortFileName = fixZip.Name;
                                int fileC = 0;
                                while (File.Exists(toSortFullName))
                                {
                                    fileC++;
                                    toSortFullName = Path.Combine(Settings.ToSort(), fixZip.Name + fileC + ".zip");
                                    toSortFileName = fixZip.Name + fileC;
                                }

                                toSortGame = new RvDir(FileType.Zip)
                                                 {
                                                     Name = toSortFileName,
                                                     DatStatus = DatStatus.InToSort,
                                                     GotStatus = GotStatus.Got
                                                 };
                            }

                            RvFile toSortRom = new RvFile(FileType.ZipFile)
                                                      {
                                                          Name = zipFileFixing.Name,
                                                          Size = zipFileFixing.Size,
                                                          CRC = zipFileFixing.CRC,
                                                          SHA1 = zipFileFixing.SHA1,
                                                          MD5 = zipFileFixing.MD5
                                                      };
                            toSortRom.SetStatus(DatStatus.InToSort, GotStatus.Got);
                            toSortRom.FileStatusSet(
                                FileStatus.SizeFromHeader | FileStatus.SizeVerified |
                                FileStatus.CRCFromHeader | FileStatus.CRCVerified |
                                FileStatus.SHA1FromHeader | FileStatus.SHA1Verified |
                                FileStatus.MD5FromHeader | FileStatus.MD5Verified
                                , zipFileFixing);

                            toSortGame.ChildAdd(toSortRom);

                            string destination = Path.Combine(Settings.ToSort(), toSortGame.Name + ".zip");
                            ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixZipFullName), Path.GetFileName(fixZipFullName), zipFileFixing.Name, zipFileFixing.Size, "-->", "ToSort", Path.GetFileName(destination), toSortRom.Name));

                            RvFile foundFile;
                            returnCode = FixFileCopy.CopyFile((RvFile)fixZip.Child(iRom), ref toSortZipOut, destination, toSortRom, true, out _error, out foundFile);
                            switch (returnCode)
                            {
                                case ReturnCode.Good: // correct reply to continue;
                                    break;
                                //raw copying so Checksums are not checked
                                //case ReturnCode.SourceCRCCheckSumError: 
                                //case ReturnCode.SourceCheckSumError:
                                //case ReturnCode.DestinationCheckSumError: 
                                default:
                                    _error = zipFileFixing.FullName + " " + zipFileFixing.RepStatus + " " + returnCode + " : " + _error;
                                    goto ZipOpenFailed;
                            }
                            zipFileFixing.GotStatus = GotStatus.NotGot; // Changes RepStatus to Deleted
                        }
                        break;
                    #endregion
                    #region Case.MoveToCorrupt
                    case RepStatus.MoveToCorrupt:
                        {
                            if (!((zipFileFixing.DatStatus == DatStatus.InDatCollect || zipFileFixing.DatStatus == DatStatus.NotInDat) && zipFileFixing.GotStatus == GotStatus.Corrupt))
                                ReportError.SendAndShow(Resources.FixFiles_FixZip_Error_in_Fix_Rom_Status + zipFileFixing.RepStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.DatStatus + Resources.FixFiles_FixZip_Colon + zipFileFixing.GotStatus);

                            ReportError.LogOut("Moving File to Corrupt");
                            ReportError.LogOut(zipFileFixing);

                            string toSortFullName;
                            if (toSortCorruptGame == null)
                            {
                                string corruptDir = Path.Combine(Settings.ToSort(), "Corrupt");
                                if (!Directory.Exists(corruptDir))
                                {
                                    Directory.CreateDirectory(corruptDir);
                                }

                                toSortFullName = Path.Combine(corruptDir, fixZip.Name + ".zip");
                                string toSortFileName = fixZip.Name;
                                int fileC = 0;
                                while (File.Exists(toSortFullName))
                                {
                                    fileC++;
                                    toSortFullName = Path.Combine(corruptDir, fixZip.Name + fileC + ".zip");
                                    toSortFileName = fixZip.Name + fileC;
                                }

                                toSortCorruptGame = new RvDir(FileType.Zip)
                                                        {
                                                            Name = toSortFileName,
                                                            DatStatus = DatStatus.InToSort,
                                                            GotStatus = GotStatus.Got
                                                        };
                            }
                            else
                            {
                                string corruptDir = Path.Combine(Settings.ToSort(), "Corrupt");
                                toSortFullName = Path.Combine(corruptDir, toSortCorruptGame.Name + ".zip");
                            }

                            RvFile toSortCorruptRom = new RvFile(FileType.ZipFile)
                                                             {
                                                                 Name = zipFileFixing.Name,
                                                                 Size = zipFileFixing.Size,
                                                                 CRC = zipFileFixing.CRC
                                                             };
                            toSortCorruptRom.SetStatus(DatStatus.InToSort, GotStatus.Corrupt);
                            toSortCorruptGame.ChildAdd(toSortCorruptRom);

                            ReportProgress(new bgwShowFix(Path.GetDirectoryName(fixZipFullName), Path.GetFileName(fixZipFullName), zipFileFixing.Name, zipFileFixing.Size, "-->", "Corrupt", Path.GetFileName(toSortFullName), zipFileFixing.Name));

                            RvFile foundFile;
                            returnCode = FixFileCopy.CopyFile((RvFile)fixZip.Child(iRom), ref toSortCorruptOut, toSortFullName, toSortCorruptRom, true, out _error, out foundFile);
                            switch (returnCode)
                            {
                                case ReturnCode.Good: // correct reply to continue;
                                    break;

                                // doing a raw copy so not needed
                                // case ReturnCode.SourceCRCCheckSumError: 
                                // case ReturnCode.SourceCheckSumError:
                                // case ReturnCode.DestinationCheckSumError: 
                                default:
                                    _error = zipFileFixing.FullName + " " + zipFileFixing.RepStatus + " " + returnCode + " : " + _error;
                                    goto ZipOpenFailed;
                            }
                            zipFileFixing.GotStatus = GotStatus.NotGot;
                        }
                        break;
                    #endregion
                    default:



                        ReportError.UnhandledExceptionHandler("Unknown file status found " + zipFileFixing.RepStatus + " while fixing file " + fixZip.Name + " Dat Status = " + zipFileFixing.DatStatus + " GotStatus " + zipFileFixing.GotStatus);
                        break;
                }
            }


            #region if ToSort Zip Made then close the zip and add this new zip to the Database
            if (toSortGame != null)
            {
                toSortZipOut.ZipFileClose();

                toSortGame.TimeStamp = toSortZipOut.TimeStamp;
                toSortGame.DatStatus = DatStatus.InToSort;
                toSortGame.GotStatus = GotStatus.Got;
                toSortGame.ZipStatus = toSortZipOut.ZipStatus;

                RvDir toSort = (RvDir)DB.DirTree.Child(1);
                toSort.ChildAdd(toSortGame);
            }
            #endregion

            #region if Corrupt Zip Made then close the zip and add this new zip to the Database
            if (toSortCorruptGame != null)
            {
                toSortCorruptOut.ZipFileClose();

                toSortCorruptGame.TimeStamp = toSortCorruptOut.TimeStamp;
                toSortCorruptGame.DatStatus = DatStatus.InToSort;
                toSortCorruptGame.GotStatus = GotStatus.Got;

                RvDir toSort = (RvDir)DB.DirTree.Child(1);
                int indexcorrupt;
                RvDir corruptDir = new RvDir(FileType.Dir) { Name = "Corrupt", DatStatus = DatStatus.InToSort };
                int found = toSort.ChildNameSearch(corruptDir, out indexcorrupt);
                if (found != 0)
                {
                    corruptDir.GotStatus = GotStatus.Got;
                    indexcorrupt = toSort.ChildAdd(corruptDir);
                }
                ((RvDir)toSort.Child(indexcorrupt)).ChildAdd(toSortCorruptGame);
            }
            #endregion



            #region Process original Zip
            string filename = fixZip.FullName;
            if (File.Exists(filename))
            {
                if (!File.SetAttributes(filename, FileAttributes.Normal))
                {
                    int error = Error.GetLastError();
                    ReportProgress(new bgwShowError(filename, "Error Setting File Attributes to Normal. Deleting Original Fix File. Code " + error));
                }
                try
                {
                    File.Delete(filename);
                }
                catch (Exception)
                {
                    int error = Error.GetLastError();
                    _error = "Error While trying to delete file " + filename + ". Code " + error;

                    if (tempZipOut != null && tempZipOut.ZipOpen != ZipOpenType.Closed)
                        tempZipOut.ZipFileClose();

                    return ReturnCode.RescanNeeded;
                }

            }
            #endregion

            bool checkDelete = false;
            #region process the temp Zip rename it to the original Zip
            if (tempZipOut != null && tempZipOut.ZipOpen != ZipOpenType.Closed)
            {
                string tempFilename = tempZipOut.ZipFilename;
                tempZipOut.ZipFileClose();

                if (tempZipOut.LocalFilesCount() > 0)
                {
                    // now rename the temp fix file to the correct filename
                    File.Move(tempFilename, filename);
                    FileInfo nFile = new FileInfo(filename);
                    RvDir tmpZip = new RvDir(FileType.Zip)
                                       {
                                           Name = Path.GetFileNameWithoutExtension(filename),
                                           TimeStamp = nFile.LastWriteTime
                                       };
                    tmpZip.SetStatus(fixZip.DatStatus, GotStatus.Got);

                    fixZip.FileAdd(tmpZip);
                    fixZip.ZipStatus = tempZipOut.ZipStatus;
                }
                else
                {
                    File.Delete(tempFilename);
                    checkDelete = true;
                }
            }
            else
                checkDelete = true;
            #endregion

            #region Now put the New Game Status information into the Database.
            int intLoopFix = 0;
            foreach (RvFile tmpZip in fixZipTemp)
            {
                tmpZip.CopyTo(fixZip.Child(intLoopFix));

                if (fixZip.Child(intLoopFix).RepStatus == RepStatus.Deleted)
                    if (fixZip.Child(intLoopFix).FileRemove() == EFile.Delete)
                    {
                        fixZip.ChildRemove(intLoopFix);
                        continue;
                    }

                intLoopFix++;
            }
            #endregion

            if (checkDelete)
                CheckDeleteObject(fixZip);

            ReportError.LogOut("");
            ReportError.LogOut("Zip File Status After Fix:");
            for (int intLoop = 0; intLoop < fixZip.ChildCount; intLoop++)
                ReportError.LogOut((RvFile)fixZip.Child(intLoop));
            ReportError.LogOut("");

            return ReturnCode.Good;


        ZipOpenFailed:
            if (tempZipOut != null) tempZipOut.ZipFileCloseFailed();
            if (toSortZipOut != null) toSortZipOut.ZipFileCloseFailed();
            if (toSortCorruptOut != null) toSortCorruptOut.ZipFileCloseFailed();
            return returnCode;

        }

        private static void AddFoundFile(RvFile foundFile)
        {
            if ((foundFile.Size == null || foundFile.CRC == null) && foundFile.Size != 0) return;

            int intIndex;
            int intRes = DBHelper.RomSearchCRCSize(foundFile, _lstRomTableSortedCRCSize, out intIndex);
            if (intRes == 0)
                _lstRomTableSortedCRCSize.Insert(intIndex, foundFile);
        }

        private static void ReCheckFile(RvFile searchFile)
        {
#if NEWFINDFIX
            FindFixesNew.ListCheck(searchFile.MyFamily);
#else
            int index;
            int length;

            DBHelper.RomSearchFindMatchingFiles(searchFile, _lstRomTableSortedCRCSize, out index, out length);

            for (int i = index; i < index + length; i++)
                _lstRomTableSortedCRCSize[i].RepStatusReset();

            FindFixes.ListCheck(_lstRomTableSortedCRCSize, index, length);
#endif
        }

        private static ReturnCode DoubleCheckDelete(RvFile fileDeleting)
        {
            if (!Settings.DoubleCheckDelete)
                return ReturnCode.Good;


            ReportError.LogOut("Double Check deleting file ");
            ReportError.LogOut(fileDeleting);

            if (DBHelper.IsZeroLengthFile(fileDeleting))
                return ReturnCode.Good;

#if NEWFINDFIX
            List<RvFile> lstFixRomTable = new List<RvFile>();
            List<RvFile> family = fileDeleting.MyFamily.Family;
            for (int iFind = 0; iFind < family.Count; iFind++)
            {
                if (family[iFind].GotStatus == GotStatus.Got && FindFixes.CheckIfMissingFileCanBeFixedByGotFile(fileDeleting, family[iFind]))
                    lstFixRomTable.Add(family[iFind]);
            }
#else
            List<RvFile> lstFixRomTableCRCSize;
            List<RvFile> lstFixRomTableSHA1CHD;

            DBHelper.RomSearchFindFixes(fileDeleting, _lstRomTableSortedCRCSize, out lstFixRomTableCRCSize);
            DBHelper.RomSearchFindFixesSHA1CHD(fileDeleting, _lstRomTableSortedSHA1CHD, out lstFixRomTableSHA1CHD);

            List<RvFile> lstFixRomTable = new List<RvFile>();
            lstFixRomTable.AddRange(lstFixRomTableCRCSize);
            lstFixRomTable.AddRange(lstFixRomTableSHA1CHD);
#endif
            RvFile fileToCheck = null;
            int i = 0;
            while (i < lstFixRomTable.Count && fileToCheck == null)
            {

                switch (lstFixRomTable[i].RepStatus)
                {
                    case RepStatus.Delete:
                        i++;
                        break;
                    case RepStatus.Correct:
                    case RepStatus.InToSort:
                    case RepStatus.Rename:
                    case RepStatus.NeededForFix:
                    case RepStatus.MoveToSort:
                    case RepStatus.Ignore:
                        fileToCheck = lstFixRomTable[i];
                        break;
                    default:

                        ReportError.LogOut("Double Check Delete Error Unknown " + lstFixRomTable[i].FullName + " " + lstFixRomTable[i].RepStatus);
                        ReportError.UnhandledExceptionHandler("Unknown double check delete status " + lstFixRomTable[i].RepStatus);
                        break;
                }
            }
            //ReportError.LogOut("Found Files when double check deleting");
            //foreach (RvFile t in lstFixRomTable)
            //    ReportError.LogOut(t);

            if (fileToCheck == null)
            {
                ReportError.UnhandledExceptionHandler("Double Check Delete could not find the correct file. (" + fileDeleting.FullName + ")");
                //this line of code never gets called because the above line terminates the program.
                return ReturnCode.LogicError;
            }

            //if it is a file then 
            // check it exists and the filestamp matches
            //if it is a ZipFile then
            // check the parent zip exists and the filestamp matches

            switch (fileToCheck.FileType)
            {
                case FileType.ZipFile:
                    {
                        string fullPathCheckDelete = fileToCheck.Parent.FullName;
                        if (!File.Exists(fullPathCheckDelete))
                        {
                            _error = "Deleting " + fileDeleting.FullName + " Correct file not found. Resan for " + fullPathCheckDelete;
                            return ReturnCode.RescanNeeded;
                        }
                        FileInfo fi = new FileInfo(fullPathCheckDelete);
                        if (fi.LastWriteTime != fileToCheck.Parent.TimeStamp)
                        {
                            _error = "Deleting " + fileDeleting.FullName + " Correct file timestamp not found. Resan for " + fileToCheck.FullName;
                            return ReturnCode.RescanNeeded;
                        }
                        break;
                    }
                case FileType.File:
                    {
                        string fullPathCheckDelete = fileToCheck.FullName;
                        if (!File.Exists(fullPathCheckDelete))
                        {
                            _error = "Deleting " + fileDeleting.FullName + " Correct file not found. Resan for " + fullPathCheckDelete;
                            return ReturnCode.RescanNeeded;
                        }
                        FileInfo fi = new FileInfo(fullPathCheckDelete);
                        if (fi.LastWriteTime != fileToCheck.TimeStamp)
                        {
                            _error = "Deleting " + fileDeleting.FullName + " Correct file timestamp not found. Resan for " + fileToCheck.FullName;
                            return ReturnCode.RescanNeeded;
                        }
                        break;
                    }
                default:
                    ReportError.UnhandledExceptionHandler("Unknown double check delete status " + fileToCheck.RepStatus);
                    break;
            }


            return ReturnCode.Good;
        }

        private static ReturnCode MoveZiptoCorrupt(RvDir fixZip)
        {
            string fixZipFullName = fixZip.FullName;
            if (!File.Exists(fixZipFullName))
            {
                _error = "File for move to corrupt not found " + fixZip.FullName;
                return ReturnCode.RescanNeeded;
            }
            FileInfo fi = new FileInfo(fixZipFullName);
            if (fi.LastWriteTime != fixZip.TimeStamp)
            {
                _error = "File for move to corrupt timestamp not correct " + fixZip.FullName;
                return ReturnCode.RescanNeeded;
            }

            string corruptDir = Path.Combine(Settings.ToSort(), "Corrupt");
            if (!Directory.Exists(corruptDir))
            {
                Directory.CreateDirectory(corruptDir);
            }

            RvDir toSort = (RvDir)DB.DirTree.Child(1);
            int indexcorrupt;
            RvDir corruptDirNew = new RvDir(FileType.Dir) { Name = "Corrupt", DatStatus = DatStatus.InToSort };
            int found = toSort.ChildNameSearch(corruptDirNew, out indexcorrupt);
            if (found != 0)
            {
                corruptDirNew.GotStatus = GotStatus.Got;
                indexcorrupt = toSort.ChildAdd(corruptDirNew);
            }

            string toSortFullName = Path.Combine(corruptDir, fixZip.Name + ".zip");
            string toSortFileName = fixZip.Name;
            int fileC = 0;
            while (File.Exists(toSortFullName))
            {
                fileC++;
                toSortFullName = Path.Combine(corruptDir, fixZip.Name + fileC + ".zip");
                toSortFileName = fixZip.Name + fileC;
            }

            if (!File.SetAttributes(fixZipFullName, FileAttributes.Normal))
            {
                int error = Error.GetLastError();
                ReportProgress(new bgwShowError(fixZipFullName, "Error Setting File Attributes to Normal. Before Moving To Corrupt. Code " + error));
            }


            File.Copy(fixZipFullName, toSortFullName);
            FileInfo toSortCorruptFile = new FileInfo(toSortFullName);

            RvDir toSortCorruptGame = new RvDir(FileType.Zip)
            {
                Name = toSortFileName,
                DatStatus = DatStatus.InToSort,
                TimeStamp = toSortCorruptFile.LastWriteTime,
                GotStatus = GotStatus.Corrupt
            };
            ((RvDir)toSort.Child(indexcorrupt)).ChildAdd(toSortCorruptGame);

            return ReturnCode.Good;
        }

        private static void CheckCreateParent(RvBase parent)
        {
            if (parent == DB.DirTree)
                return;

            string parentDir = parent.FullName;
            if (Directory.Exists(parentDir) && parent.GotStatus == GotStatus.Got) return;

            CheckCreateParent(parent.Parent);
            if (!Directory.Exists(parentDir))
                Directory.CreateDirectory(parentDir);
            parent.GotStatus = GotStatus.Got;
        }

        private static void CheckDeleteObject(RvBase tBase)
        {
            if (tBase.RepStatus == RepStatus.Deleted)
                return;

            // look at the directories childrens status's to figure out if the directory should be deleted.
            if (tBase.FileType == FileType.Dir)
            {
                RvDir tDir = tBase as RvDir;
                if (tDir == null || tDir.ChildCount != 0) return;
                // check if we are at the root of the tree so that we do not delete RomRoot and ToSort
                if (tDir.Parent == DB.DirTree) return;

                string fullPath = tDir.FullName;
                try
                {
                    if (Directory.Exists(fullPath))
                        Directory.Delete(fullPath);
                }
                catch (Exception e)
                {
                    //need to report this to an error window
                    Debug.WriteLine(e.ToString());
                }
            }

            // else check if this tBase should be removed from the DB
            if (tBase.FileRemove() == EFile.Delete)
            {
                RvDir parent = tBase.Parent;

                if (parent == null)
                {
                    ReportError.UnhandledExceptionHandler("Item being deleted had no parent " + tBase.FullName);
                    return; // this never happens as UnhandledException Terminates the program
                }

                int index;

                if (!parent.FindChild(tBase, out index))
                {
                    ReportError.UnhandledExceptionHandler("Could not find self in delete code " + parent.FullName);
                }
                parent.ChildRemove(index);
                CheckDeleteObject(parent);
            }
        }




        private static List<RvDir> _checkList;
        private static void CheckReprocessClearList()
        {
            _checkList = new List<RvDir>();
        }
        private static void CheckReprocess(RvBase tCheck, bool fastProcess = false)
        {
            switch (tCheck.FileType)
            {
                case FileType.File:
                    if (tCheck.RepStatus == RepStatus.Delete && !_processList.Contains(tCheck))
                        _processList.Add(tCheck);
                    break;
                case FileType.ZipFile:

                    RvDir p = tCheck.Parent;
                    if (fastProcess)
                    {
                        if (!_checkList.Contains(p))
                            _checkList.Add(p);
                        break;
                    }
                    if (!_processList.Contains(p))
                    {
                        bool hasdelete = false;
                        bool hasNeededForFix = false;
                        for (int i = 0; i < p.ChildCount; i++)
                        {
                            RvBase f = p.Child(i);

                            if (f.RepStatus == RepStatus.Delete)
                                hasdelete = true;
                            else if (f.RepStatus == RepStatus.NeededForFix || f.RepStatus == RepStatus.Rename)
                            {
                                hasNeededForFix = true;
                                break;
                            }
                        }
                        if (hasdelete && !hasNeededForFix)
                        {
                            Debug.WriteLine(tCheck.Parent.FullName + " adding to process list.");
                            _processList.Add(p);
                        }
                    }
                    break;
                default:
                    ReportError.SendAndShow("Unknow repair file type recheck.");
                    break;

            }
        }
        private static void CheckReprocessFinalCheck()
        {
            foreach (RvDir p in _checkList)
            {

                if (_processList.Contains(p))
                    continue;
                bool hasdelete = false;
                bool hasNeededForFix = false;
                for (int i = 0; i < p.ChildCount; i++)
                {
                    RvBase f = p.Child(i);

                    if (f.RepStatus == RepStatus.Delete)
                        hasdelete = true;
                    else if (f.RepStatus == RepStatus.NeededForFix || f.RepStatus == RepStatus.Rename)
                    {
                        hasNeededForFix = true;
                        break;
                    }
                }
                if (!hasdelete || hasNeededForFix) continue;

                Debug.WriteLine(p.FullName + " adding to process list.");
                _processList.Add(p);
            }
        }
    }
}