/**
*
* @File : RandomAccess.cxx
*
* @Authors : D. Mathieu
*            M. Laporte
*
* @Date : 08/03/2001
*
* @Version : V1.0
*
* @Synopsis :
*
**/
#include <iostream>
#include <string>
#include <exception>
#include <algorithm>        // max()
#include <fstream>
#include <cstdio>           // tmpnam(), tempnam()

#include <fcntl.h>          // O_RDWR, O_EXCL
#include <unistd.h>         // SEEK_SET, off_t
#include <climits>          // L_tmpnam

#include "CExc.h"
#include "CExcFctSyst.h"
#include "nsSysteme.h"

using namespace nsSysteme;

namespace
{
    class CRandomAccess
    {
      public :
        enum { CstMaxSize = 100 };

      private :
        struct SLignes
        {
            unsigned long m_Offset;
            unsigned      m_Size;
            SLignes (off_t Offset = 0L, unsigned Size = 0)
                : m_Offset (Offset), m_Size (Size) {}

        }; // SLignes
 
        string   m_NomFich;
        int      m_fd;
        unsigned m_NbreLignes;
        SLignes  m_Index [CstMaxSize];

      public :
         CRandomAccess (const string & NomFich);
        ~CRandomAccess (void);

        string Get (unsigned i) const;
        void   Set (const string & Str, unsigned i);

    }; // CRandomAccess
  
    CRandomAccess::CRandomAccess (const string & NomFich)
      : m_NomFich (NomFich), m_NbreLignes (0)
    {
        m_fd = Open (NomFich.c_str(), O_RDWR | O_CREAT, 0700);

        ifstream is (m_fd);

        string Str;

        // en C++ standard, le délimiteur n'a pas de valeur par défaut

        for (unsigned long Offset = 0L; getline (is, Str, '\n'); )
        {
            cout << Str << " Taille = " << Str.length () << endl;

            m_Index [m_NbreLignes++] = SLignes (Offset, Str.length ());
            Offset += Str.length () + 1;
        }

    } // CRandomAccess()

    CRandomAccess::~CRandomAccess (void)
    {
        char * NomTemp = ::tempnam ("./", 0);
        const int fdTemp = Open (NomTemp,
                                 O_WRONLY | O_CREAT | O_TRUNC, 0700);
       
        const string CstStrVide;

        const char LF = '\n';

        for (unsigned i = 0; i < m_NbreLignes; ++i)
        {
            if (m_Index [i].m_Size)
                Write (fdTemp, Get (i).c_str (), m_Index [i].m_Size);

            Write (fdTemp, &LF, 1);
        }
        Close (fdTemp);
        Close (m_fd);
       
        Rename (NomTemp, m_NomFich.c_str ());

    } // ~CRandomAccess()
  
    string CRandomAccess::Get (unsigned i) const
    {
        const string CstStrVide;

        if (0 == m_Index [i].m_Size) return CstStrVide;

        Lseek (m_fd, m_Index [i].m_Offset, SEEK_SET);
        char * Chaine = new  char [m_Index [i].m_Size + 1];
        Chaine [Read (m_fd, Chaine, m_Index [i].m_Size)] = '\0';
        string Str (Chaine);
        delete [] Chaine;

        return Str;

    } // Get ()

    void CRandomAccess::Set (const string & Str, unsigned i)
    {
        if (0 == Str.length ())
        {
            m_Index [i] = SLignes();
            return;
        }
        m_Index [i] = SLignes ((Str.length () <= m_Index [i].m_Size
                                 ? Lseek (m_fd, m_Index [i].m_Offset, SEEK_SET)
                                 : Lseek (m_fd, 0, SEEK_END)),
                             Str.length ());

        Write (m_fd, Str.c_str(), Str.length ());

        const char LF = '\n';
        Write (m_fd, &LF, 1);

        m_NbreLignes = max (m_NbreLignes, i + 1);

    } // Set ()

} // namespace anonyme

int std::ppal (int argc, char * argv []) throw (exception)
{
    if (2 != argc) throw CExc (string ("Usage : ") + argv [0] + " <fichier>");

    CRandomAccess Fich (argv [1]);

    cout << "Taper Ctrl + D pour terminer la saisie\n" << endl;

    cout << "Taper l'indice suivi, séparé par un espace, "
            "de la ligne à saisir :" << endl;
    for (int i; cin >> i; )
    {
        // Suppression du séparateur espace
        char Car;
        cin.get (Car);

        string Str;
        getline (cin, Str, '\n');
        cout << "Ajout de " << Str << " en position " << i << endl;
        Fich.Set (Str, i);
        cout << "Taper l'indice suivi, séparé par un espace, "
                "de la ligne à saisir :" << endl;
    }

    return 0;

} // ppal()