CDirEntry OSFilesMGFind(CDirEntry *needle_entry, CDirEntry *haystack_list)
{
        while (haystack_list)
        {
                if (!StrCompare(needle_entry->name, haystack_list->name))
                        return haystack_list;
                haystack_list = haystack_list->next;
        }

        return NULL;
}

U0 OSFilesMergeInner(CDirEntry *tmpde1, CDirEntry *tmpde2, I64 *_fuf_flags, I64 *_df_flags)
{
        CDirEntry       *tmpde;
        U8                      *new;

        while (tmpde1 && !(*_df_flags & DF_ABORT_ALL_FILES))
        {
                tmpde = OSFilesMGFind(tmpde1, tmpde2);
                if (!tmpde)
                {
                        "$BROWN$Does Not Exist:%s$FG$\n", tmpde1->full_name;
                        new = StrNew(tmpde1->full_name);
                        new[0] = tmpde2->full_name[0]; // shortcut to quickly get drive letter
                        Copy(tmpde1->full_name, new);
                        Free(new);
                }
                else
                {
                        if (tmpde1->attr & RS_ATTR_DIR)
                                OSFilesMergeInner(tmpde1->sub, tmpde->sub, _fuf_flags, _df_flags);
                        else
                        {
                                if (AbsI64(tmpde1->datetime - tmpde->datetime) > CDATE_FREQ * 2) {//slop
                                        "%s", tmpde1->full_name;
                                        '\n';
                                        if (Bt(_fuf_flags, FUf_DIFF))
                                        {
                                                if (FilesFindMatch(tmpde1->full_name, FILEMASK_TXT))
                                                        Diff(tmpde->full_name, tmpde1->full_name, _df_flags);
                                        }
                                }
                        }
                }
                tmpde1 = tmpde1->next;
        }
}

U0 OSFilesMerge(U8 *dst_files_find_mask="/*", U8 *src_files_find_mask="/*", U8 *fu_flags=NULL)
{ // See Merge.
        I64                      df_flags = 0, fuf_flags = 0;
        CDirEntry       *tmpde1 = NULL, *tmpde2 = NULL;

        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r");
        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
        if (fuf_flags & ~(FUG_FILES_FIND | FUF_DIFF))
                throw('FUF');
        PrintWarn("This is based strictly on file dates.\n");
        tmpde1 = FilesFind(src_files_find_mask, fuf_flags & FUG_FILES_FIND);
        tmpde2 = FilesFind(dst_files_find_mask, fuf_flags & FUG_FILES_FIND);
        fuf_flags &= FUF_DIFF;
        OSFilesMergeInner(tmpde1, tmpde2, &fuf_flags, &df_flags);
        DirTreeDel(tmpde1);
        DirTreeDel(tmpde2);
}


U0 OSMerge(U8 dst_drv, U8 src_drv=':')
{
        U8 *dst = MStrPrint("%C:/", dst_drv);
        U8 *src = MStrPrint("%C:/", src_drv);

        CopyTree(src, "B:/");
        OSFilesMerge("B:/", dst, "+d");
        "Format %C:/ ? ", dst_drv;
        Format(dst_drv);
        CopyTree("B:/", dst);

        BootHDIns(dst_drv);
}

U0 OSUpgrade()
{
        "\n\nTODO: OSUpgrade (base off OSInstall)\n\n";
}

U0 InstallDrive(U8 drv_let)
{
        U8 *st;

        while (!DriveCheck(blkdev.let_to_drive[drv_let - 'A'], FALSE))
                Sleep(1);
        Sleep(1000);

        ExePrint("CopyTree(\"::/\",\"%C:/\");",                 drv_let);
        ExePrint("DirMake(\"%C:/Tmp\");",                               drv_let);
        ExePrint("DirMake(\"%C:/Tmp/ScreenShots\");",   drv_let);
        ExePrint("DirMake(\"%C:/Home\");",                              drv_let);

        st = MStrPrint("%C:/Home/DoDistro.CC", drv_let);
        if (!FileFind(st))
                Copy("::/Misc/DoDistro.CC", st);
        Free(st);

        st = MStrPrint("%C:/Home/MakeHome.CC", drv_let);
        if (!FileFind(st))
                Copy("::/MakeHome.CC", st);
        Free(st);
}

Bool VMPartDisk(CTask *task, I64 ata_port)
{
        if (ata_port > -1)
        {
                XTalkWait(task, "DiskPart(,0.5,0.5);\nC\n%d\nY", ata_port); // DOUBLE CHECK INFILE
                return TRUE;
        }
        else
                return FALSE;
}

U0 VMInstallDrive(CTask *task, U8 drv_let, I64 ata_port, I64 atapi_port)
{// DOUBLE CHECK INFILE
        InstallDrive(drv_let);
        XTalkWait(task, "BootHDIns('%C');\n\nB\n0x20000\n", drv_let);
        if (ata_port > -1)
                XTalkWait(task, "C\n%d\n", ata_port);
        if (atapi_port > -1)
                XTalkWait(task, "T%d\n", atapi_port);
        XTalkWait(task, "\n1024\n768\n\n\n"); //Exit Drives,  set Screen Resolution, skip Disk Cache and Options
}

U0 VMInstallWiz()
{
        CTask           *task;
        I64                      i, atapi_port = -1, ata_port = -1;
        CAHCIPort       *port;

        task = User;
        TaskWait(task);
        task->border_src        = BDS_CONST;
        task->border_attr       = LTGRAY << 4 + DriveTextAttrGet(':') & 15;
        task->text_attr         = LTGRAY << 4 + BLUE;
        task->win_inhibit       = WIG_TASK_DEFAULT - WIF_SELF_BORDER;
        WinHorz(Fs->win_left, Fs->win_right, task);
        WinVert(Fs->win_top,  (Fs->win_top + Fs->win_bottom) >> 2 - 1, task);
        WinVert(task->win_bottom + 3, Fs->win_bottom);
        WinToTop(Fs);


        ////////////////////////////////////
        SATARep;
        for (i = 0; i < AHCI_MAX_PORTS; i++)
        {
                if (Bt(&blkdev.ahci_hba->ports_implemented, i))
                {
                        port = &blkdev.ahci_hba->ports[i];
                        if (port->signature == AHCI_PxSIG_ATA)
                        {
                                ata_port = i;
                                break;
                        }
                }
        }
        for (i = 0; i < AHCI_MAX_PORTS; i++)
        {
                if (Bt(&blkdev.ahci_hba->ports_implemented, i))
                {
                        port = &blkdev.ahci_hba->ports[i];
                        if (port->signature == AHCI_PxSIG_ATAPI)
                        {
                                atapi_port = i;
                                break;
                        }
                }
        }

        if (VMPartDisk(task, ata_port))
        {
                VMInstallDrive(task, 'C', ata_port, atapi_port);
                VMInstallDrive(task, 'D', ata_port, atapi_port);
                BootMHDIns('C');
        }

        ////////////////////////////////////

        WinVert(task->win_top, Fs->win_bottom);
        Kill(task);
}

U0 RegularInstallWiz()
{
        I64              drv_let;
        U8              *st, *port_st;
        I64              ch, res_num;
        CTask   *task;

        task = User;
        TaskWait(task);
        task->border_src        = BDS_CONST;
        task->border_attr       = LTGRAY << 4 + DriveTextAttrGet(':') & 15;
        task->text_attr         = LTGRAY << 4 + BLUE;
        task->win_inhibit       = WIG_TASK_DEFAULT - WIF_SELF_BORDER;
        WinHorz(Fs->win_left, Fs->win_right, task);
        WinVert(Fs->win_top,  (Fs->win_top + Fs->win_bottom) >> 2 - 1, task);
        WinVert(task->win_bottom + 3, Fs->win_bottom);
        WinToTop(Fs);

        XTalk(task, "Mount;\nC\n");

        "\nSelect the port of the ATA drive to install on.\n";

        "Hard Drive Port: ";
        while (TRUE)
        {
                port_st = StrGet;
                if ((0 <= Str2I64(port_st) < AHCI_MAX_PORTS) &&
                        ((&blkdev.ahci_hba->ports[Str2I64(port_st)])->signature == AHCI_PxSIG_ATA))
                {
                        break;
                }
                Free(port_st);
        }

        XTalkWait(task, "%s\n\n", port_st);

        DriveRep;
        do
        {
                st = StrGet("\nDestination Partition Letter: ");
                if (*st)
                        drv_let = Letter2Letter(*st);
                else
                        drv_let = 0;
                Free(st);
        }
        while (!('A' <= drv_let <= 'Z'));

        '\n';

        "$RED$Format %C Partition?$FG$\n", drv_let;
        if (YorN)
        {
                '\n';
                do
                {
                        "$PURPLE$1$FG$) Use FAT32\n"
                        "$PURPLE$2$FG$) Use RedSea\n"
                        "\nFile System Type: ";
                        ch = CharGet;
                        '\n';
                }
                while (!('1' <= ch <= '2'));

                if (ch == '1')
                        Format(drv_let,, FALSE, FSt_FAT32);
                else
                        Format(drv_let,, FALSE, FSt_REDSEA);
        }
        InstallDrive(drv_let);


        XTalkWait(task, "BootHDIns('%C');\n\nB\n0x20000\nC\n%s\n\n", drv_let, port_st);
        VideoRep;
        res_num = I64Get("Enter desired list num ($PURPLE$<ENTER>$FG$ for max res): ",, 1);
        XTalkWait(task, "%d\n\n\n", res_num); //skip through Disk Cache, Options
        "$RED$Install Master Boot loader?$FG$";
        if (YorN)
        {
                '\n';
                BootMHDIns(drv_let);
        }

        WinVert(task->win_top, Fs->win_bottom);
        Kill(task);



}

U0 DoInstructions()
{
        CTask *task = User;

        AutoComplete;
        WinToTop(Fs);
        WinTileVert;
        XTalk(task, "Ed(\"::/Doc/Install.DD\");\n");
}

Bool DoInstall(Bool prompt_reboot)
{
        I64                                      res = FALSE, vm_install = TRUE, ch;
        CSMBIOSSystemInfo       *sys_info = SMBIOSStructGet(SMBIOSt_SYSTEM);
        U8                                      *company = SMBIOSStr(sys_info, sys_info->manufacturer);

        if (StrCompare(company, "VMware, Inc.") && StrCompare(company, "innotek GmbH") && StrCompare(company, "QEMU"))
        {
                "\n\n\n\n\nAre you installing inside VMware, QEMU, VirtualBox or a similar virtual machine? ";
                vm_install = YorN;
        }
        DocBottom;
        if (vm_install)
        {
                VMInstallWiz();
                res = TRUE;
        }
        else
        {
                "\n\nThis wizard works if you have a partition ready. You can partition the drive or BootHDIns() "
                                "with more options if you do it by hand, not using this wizard.\n\n"
                                "Continue Install Wizard ";
                if (YorN)
                {
                        "\n\nUpgrade an existing install,"
                        "\nor create new Installation? (U/I): ";
                        do
                                ch = ToUpper(CharGet(, FALSE));
                        while (ch != 'U' && ch != 'I');

                        if (ch == 'I')
                        {
                                RegularInstallWiz();
                                res = TRUE;
                        }
                        else if (ch == 'U')
                        {
                                OSUpgrade;
                                prompt_reboot = FALSE;
                                res = TRUE;
                        }
                }
                else
                        prompt_reboot = FALSE;
        }
        if (prompt_reboot)
        {
                "Reboot Now ";
                if (YorN)
                {
                        DiscEject(':');
                        Reboot;
                };
        }
        return res;
}

Bool OSInstall(Bool prompt_reboot=TRUE)
{
        DoInstructions;

        return DoInstall(prompt_reboot);
}

#if __CMD_LINE__
OSInstall(TRUE);
#endif