#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include "list.h"

static char ReMap [256];
#define FOUND       0
#define NOT_FOUND   1
#define ABORT       2

int
GetNewFile ()
{
    char    FileName [160];
    struct  Flist   *pOrig;

    SyncReader ();
    GetInput ("File...> ", FileName, 160);
    if (FileName[0] == 0)  {
        SetUpdate (U_HEAD);
        SetEvent (vSemReader);
        return (0);
    }
    pOrig = vpFlCur;
    AddFileToList (FileName);
    vpFlCur = pOrig;
    SetEvent (vSemReader);
    return (1);
}

void
GetSearchString ()
{
    UpdateHighClear ();
    GetInput ("String.> ", vSearchString, 40);
    InitSearchReMap ();
}


void
InitSearchReMap ()
{
    unsigned  i;

    if (vStatCode & S_NOCASE)
        _strupr (vSearchString);

    /*
     *  Build ReMap
     */
    for (i=0; i < 256; i++)
        ReMap[i] = (char)i;

    if (vStatCode & S_NOCASE)
        for (i='a'; i <= 'z'; i++)
            ReMap[i] = (char)(i - ('a' - 'A'));
}


void
FindString()
{
    char    eof, dir_next;
    long    offset, lrange, line, l, hTopLine;
    struct  Flist   *phCurFile, *pFile;

    if (vSearchString[0] == 0)  {
        SetUpdate (U_HEAD);
        return ;
    }

    SetUpdate (U_NONE);
    DisLn (CMDPOS, (Uchar)(vLines+1), "Searching");
    vStatCode |= S_INSEARCH;
    dir_next   = (char)(vStatCode & S_NEXT);

    /*
     *  Get starting point for search in the current file.
     *  Save current file, and location.
     */
    hTopLine  = vTopLine;
    phCurFile = vpFlCur;

    if (vHighTop >= 0L)
        vTopLine = vHighTop;
    if (vStatCode & S_NEXT)
        vTopLine++;
    if (vTopLine >= vNLine)
        vTopLine = vNLine-1;

    QuickRestore ();      /* Jump to starting line    */

    for (; ;) {
        /*
        *  Make sure starting point is in memory
        */
        while (InfoReady () == 0) {     /* Set extern values  */
            ResetEvent   (vSemMoreData);
            SetEvent   (vSemReader);
            WaitForSingleObject(vSemMoreData, WAITFOREVER);
            ResetEvent(vSemMoreData);
        }

        if (! dir_next) {
            if (vOffTop)
                vOffTop--;
            else if (vpBlockTop->prev) {
                vpBlockTop = vpBlockTop->prev;
                vOffTop    = vpBlockTop->size;
            }
        }

        vTopLine = 1L;


        /*
        *  Do the search.
        *  Use 2 different routines, for speed.
        *
        *  Uses vpBlockTop & vOffTop. They are set up by setting TopLine
        *  then calling InfoReady.
        */
        eof = (char)SearchText (dir_next);

        if (eof != FOUND)
            vTopLine = hTopLine;

        /* Multi-file search?  Yes, go onto next file     */
        if (eof == NOT_FOUND  &&  (vStatCode & S_MFILE)) {
            if (vStatCode & S_NEXT) {
                if ( (pFile = vpFlCur->next) == NULL)
                    break;
                NextFile (0, pFile);        /* Get file       */
                hTopLine = vTopLine;        /* Save position      */
                vTopLine = line = 0;        /* Set search position  */
            } else {
                if ( (pFile = vpFlCur->prev) == NULL)
                    break;
                NextFile (0, pFile);
                hTopLine = vTopLine;
                if (vLastLine == NOLASTLINE) {  /* HACK. if EOF is unkown   */
                    dir_next = S_NEXT;      /* goto prev file, but scan */
                    vTopLine = line = 0;    /* from TOF to EOF      */
                } else {
                    vTopLine = (line = vLastLine) - vLines;
                    dir_next = 0;           /* else, scan from EOF to   */
                    if (vTopLine < 0)       /* TOF.         */
                        vTopLine = 0;
                }
            }
            QuickRestore ();        /* Display 1 page of new    */
            SetUpdate (U_ALL);      /* new file. then set scan  */
            SetUpdate (U_NONE);     /* position       */
            vTopLine = line;
            continue;
        }

        break;          /* Done searching     */
    }

    /*
     *  If not found (or abort), then resotre position
     */
    vStatCode &= ~S_INSEARCH;
    if (eof) {
        if (phCurFile != vpFlCur)   /* Restore file & position      */
            NextFile (0, phCurFile);
        QuickRestore ();

        SetUpdate (U_ALL);      /* Force screen update, to fix  */
        SetUpdate (U_NONE);     /* scroll bar position.     */
        DisLn (CMDPOS, (Uchar)(vLines+1), eof == 1 ? "* Text not found *" : "* Aborting Search *");
        if (eof == 1)
            beep ();
        return ;
    }

    // Search routine adjusts vpBlockTop & vOffTop to next(prev)
    // occurance of string.  Now the line # must be set.

    offset = vpBlockTop->offset + vOffTop;

    lrange = vNLine/4 + 2;
    line   = vNLine/2;
    while (lrange > 4L) {
        l = vprgLineTable[line/PLINES][line%PLINES];
        if (l < offset) {
            if ( (line += lrange) > vNLine)
                line = vNLine;
        } else {
            if ( (line -= lrange) < 0L)
                line = 0L;
        }
        /*  lrange >>= 1;  */
        lrange = (lrange>>1) + 1;
    }
    line += 7;

    while (vprgLineTable[line/PLINES][line%PLINES] > offset)
        line--;

    vHighTop = line;
    vHighLen = 0;

    /*
     *  Was found. Adjust to be in center of CRT
     */
    GoToMark ();
}

int
SearchText (
    char dir
    )
{
    char  *data;
    char  *data1;
    int     i;
    Uchar   c, d;

    for (; ;) {
        data = vpBlockTop->Data;
        data += vOffTop;

        if (ReMap [(unsigned char)*data] == vSearchString[0]) {
            data1 = data;
            i = vOffTop;
            d = 1;
            for (; ;) {
                c = vSearchString[d++];
                if (c == 0)
                    return (FOUND);

                if (++i >= BLOCKSIZE) {
                    while (vpBlockTop->next == NULL) {
                        vpCur = vpBlockTop;
                        vReaderFlag = F_DOWN;
                        SetEvent   (vSemReader);
                        WaitForSingleObject(vSemMoreData, WAITFOREVER);
                        ResetEvent(vSemMoreData);
                    }
                    i = 0;

                    data1 = vpBlockTop->next->Data;

                } else {

                    data1++;
                }

                if (ReMap [(unsigned char)*data1] != (char)c)
                    break;
            }
        }
        if (dir) {
            vOffTop++;
            if (vOffTop >= BLOCKSIZE) {
                if (vpBlockTop->flag == F_EOF)
                    return (NOT_FOUND);
                fancy_percent ();
                if (_abort ())
                    return (ABORT);
                while (vpBlockTop->next == NULL) {
                    vpCur = vpBlockTop;
                    vReaderFlag = F_DOWN;
                    SetEvent   (vSemReader);
                    WaitForSingleObject(vSemMoreData, WAITFOREVER);
                    ResetEvent(vSemMoreData);
                }
                vOffTop = 0;
                vpBlockTop = vpBlockTop->next;
            }
        } else {
            vOffTop--;
            if (vOffTop < 0) {
                if (vpBlockTop->offset == 0L)
                    return (NOT_FOUND);
                fancy_percent ();
                if (_abort ())
                    return (ABORT);
                while (vpBlockTop->prev == NULL) {
                    vpCur = vpBlockTop;
                    vReaderFlag = F_UP;
                    SetEvent   (vSemReader);
                    WaitForSingleObject(vSemMoreData, WAITFOREVER);
                    ResetEvent(vSemMoreData);
                }
                vOffTop = BLOCKSIZE - 1;
                vpBlockTop = vpBlockTop->prev;
            }
        }
    }
}


void
GoToMark ()
{
    long    line;

    if (vHighTop < 0L)
        return ;

    line = vHighTop;
    UpdateHighClear ();

    vTopLine = 1;
    vHighTop = line;
    line = vHighTop - vLines / 2;

    while (line >= vNLine) {
        if (! (vLastLine == NOLASTLINE)) {  /* Mark is past EOF?  */
            vHighTop = vLastLine - 1;   /* Then set it to EOF.  */
            break;
        }
        if (_abort()) {
            line = vNLine-1;
            break;
        }
        fancy_percent ();     /* Wait for marked line */
        vpBlockTop  = vpCur = vpTail;   /* to be processed  */
        vReaderFlag = F_DOWN;
        ResetEvent     (vSemMoreData);
        SetEvent   (vSemReader);
        WaitForSingleObject(vSemMoreData, WAITFOREVER);
        ResetEvent(vSemMoreData);
    }

    if (line > vLastLine - vLines)
        line = vLastLine - vLines;

    if (line < 0L)
        line = 0L;

    vTopLine = line;
    vHLBot   = vHLTop = 0;
    QuickRestore ();
    SetUpdate (U_ALL);
}


void
GoToLine ()
{
    char    LineNum [10];
    long    line;

    GetInput ("Line #.> ", LineNum, 10);
    if (LineNum[0] == 0)
        return;
    line = atol (LineNum);
    vHighTop = line;
    vHighLen = 0;

    GoToMark ();
}


void
SlimeTOF ()
{
    char    Text [10];
    long    KOff;

    SyncReader ();
    GetInput ("K Off..> ", Text, 40);
    KOff  = atol (Text) * 1024;
    KOff -= KOff % BLOCKSIZE;
    if (Text[0] == 0  ||  KOff == vpFlCur->SlimeTOF) {
        SetEvent (vSemReader);
        return;
    }

    vpFlCur->SlimeTOF = KOff;
    vpFlCur->FileTime.dwLowDateTime = (unsigned)-1;    /* Cause info to be invalid     */
    vpFlCur->FileTime.dwHighDateTime = (unsigned)-1;  /* Cause info to be invalid */
    FreePages (vpFlCur);
    NextFile  (0, NULL);
}