#include <iostream>
#include <fstream>
#include <cctype>
#include <limits>
#include <ctime>
#include <cstdlib>

using namespace std;

/********************************************************
 *       Support FUNCTIONS
 ********************************************************/
inline
void get_line(char * s, long m, istream & in = cin, bool cleanup = true)
{
    if (in.tie() != nullptr)
    {
        in.tie()->flush();
    }
    *s = in.get();
    in.getline(*s=='\n'?s:s+1, *s=='\n'?m:m-1);
    if (cleanup && in.fail())
    {
        in.clear();
        in.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    return;
}

inline
long rand_range(long max, long min = 0)
{
    return rand()%(max-min+1)+min;
}

/********************************************************
 *       FILE FUNCTIONS
 ********************************************************/
inline
streamsize filelen(ifstream & f)
{
    streamsize at = f.tellg(), len;
    f.seekg(0, ios_base::end);
    len = f.tellg();
    f.seekg(at);
    return len;
}

inline
streamsize filelen(ofstream & f)
{
    streamsize at = f.tellp(), len;
    f.seekp(0, ios_base::end);
    len = f.tellp();
    f.seekp(at);
    return len;
}

/*
    returns name, (possibly) opened input file, and true if file
    did open successfully
*/
bool open(char name[], long MAX, ifstream & f, bool forever = true);

enum OpenAs { OUTPUT, APPEND };
/*
    returns name, (possibly) opened output file, and true if file
    did open successfully
*/
bool open(char name[], long MAX, ofstream & f, OpenAs mode = OUTPUT,
          bool forever = true);

int main(void)
{
    const short MAX_FNAME = 500;
    char fname[MAX_FNAME];
    ifstream fin;
    ofstream fout;
    streamsize len, to;
    char menu;
    bool done = false;

    srand(static_cast<unsigned>(time(nullptr)));

    do
    {
        cout << "\n______\b\b\b\b\bMENU\n\n"
             << "1) find length of Input file\n"
             << "2) find length within Output file\n"
             << "3) Done\n\nChoice:  ";
        cin >> menu;
        switch (tolower(menu))
        {
            case '1':  case 'i':
            {
                open(fname, MAX_FNAME, fin);
                len = filelen(fin);
                cout << "\nLength:  " << len << endl;
                for (short c = 0; c != 4; c++)
                {
                    fin.seekg(to = rand_range(len));
                    cout << "\nRepositioning randomly to..." << to << ".\n";
                    cout << "\nFile length is still " << filelen(fin)
                         << " and we are still at " << fin.tellg()
                         << ".\n";
                }
                fin.close();
                fin.clear();
            } break;
            case '2':  case 'o':
            {
                open(fname, MAX_FNAME, fout, APPEND);
                len = filelen(fout);
                cout << "\nLength:  " << len << endl;
                for (short c = 0; c != 4; c++)
                {
                    fout.seekp(to = rand_range(len));
                    cout << "\nRepositioning randomly to..." << to << ".\n";
                    cout << "\nFile length is still " << filelen(fout)
                         << " and we are still at " << fout.tellp()
                         << ".\n";
                }
                fout.close();
                fout.clear();
            } break;
            case '3':  case 'd':  case 'q':  case 'x':
            {
                done = true;
            } break;
            default:
            {
                cout << "\nInvalid selection!\a\n\nPlease make a more"
                        " valid choice next time...\n";
            } break;
        }
    } while (!done);

    cout << "Thanks for looking at file lengths with us today!\n";
    return 0;
}

/*
    returns name, (possibly) opened input file, and true if file
    did open successfully
*/
bool open(char name[], long MAX, ifstream & f, bool forever)
{
    cout << "\nEnter name of input file:  ";
    get_line(name, MAX);
    f.open(name);
    while (!f && forever)
    {
        f.close();
        f.clear();
        cout << "\nFailed to open file '" << name << "'...\n\n"
             << "Please try another (more valid) name...\n";
        cout << "\nEnter name of input file:  ";
        get_line(name, MAX);
        f.open(name);
    }
    return !f.fail();
}

/*
    returns name, (possibly) opened output file, and true if file
    did open successfully
*/
bool open(char name[], long MAX, ofstream & f, OpenAs mode, bool forever)
{
    cout << "\nEnter name of output file:  ";
    get_line(name, MAX);
    f.open(name, mode==APPEND?ios_base::app:ios_base::out);
    while (!f && forever)
    {
        f.close();
        f.clear();
        cout << "\nFailed to open file '" << name << "'...\n\n"
             << "Please try another (more valid) name...\n";
        cout << "\nEnter name of output file:  ";
        get_line(name, MAX);
        f.open(name, mode==APPEND?ios_base::app:ios_base::out);
    }
    return !f.fail();
}

