///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1999, Microsoft Corp. All rights reserved.
//
// FILE
//
//    inf2db.h
//
// SYNOPSIS
//    import the information stored in an INF file into a MSJet4 relational
//    database
//
//
// MODIFICATION HISTORY
//
//    02/12/1999    Original version.
//
///////////////////////////////////////////////////////////////////////////////
// inf2db.cpp : Defines the entry point for the console application.
//
#include "precomp.hpp"
#include "inf2db.h"

///////////////////////////////////////////////////////////////////////////////
//
//  Main
//
///////////////////////////////////////////////////////////////////////////////
extern "C"
void __cdecl wmain(int argc, wchar_t* argv[])
{
    HINF           lHINF;

    HRESULT     hres = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        cerr << "Unable to initialize COM!\n";
    }
    else
    {
        CDatabase               Database;

        ////////////////////////////////////////////////////////////////
        // Call the process command (process the command-line arguments)
        ////////////////////////////////////////////////////////////////
        hres = ProcessCommand(argc, argv, &lHINF, Database);
        if (FAILED(hres))
        {
            g_FatalError = true;
        }
        else
        {
             ////////////////////////////////////////////////
             // Call the process function to parse the file
             ////////////////////////////////////////////////
            hres = Process(lHINF, Database);
            if (FAILED(hres))
            {
                g_FatalError = true;
            }

            //////////////////////////
            // then call uninitialize
            //////////////////////////
            hres = Uninitialize(&lHINF, Database);

            if (g_FatalError)
            {
                cerr << "Fatal Error: check the Trace file.";
                cerr << "Import operation aborted.\n";
            }
            else
            {
                cerr << "Import successful.\n";
            }
        }
    }
    CoUninitialize();
}


//////////////////////////////////////////////////////////////////////////////
//
// Unitialize
//
//////////////////////////////////////////////////////////////////////////////
HRESULT Uninitialize(HINF *phINF, CDatabase& Database)
{
   _ASSERTE(phINF != NULL);

   SetupCloseInfFile(*phINF);

   return Database.Uninitialize(g_FatalError);
}


//////////////////////////////////////////////////////////////////////////////
//
// Process
//
//////////////////////////////////////////////////////////////////////////////
HRESULT Process(const HINF& hINF, CDatabase& Database)
{

    // get the number of tables
    LONG lTotalTableNumber = SetupGetLineCountW(
                                    // handle to the INF file
                                    hINF,
                                    // the section in which to count lines
                                    TABLE_SECTION
                                    );


    bool            bError = false; //needs to be outside the if, loop...
    HRESULT         hres;

    if (lTotalTableNumber > 0)
    {
    #ifdef DEBUG
        TracePrintf("Info: number of tables = %d\n",lTotalTableNumber);
    #endif

        INFCONTEXT        lTableContext;
        // if <>0 do a loop on the tables
        BOOL bOK = SetupFindFirstLineW(
                                       hINF,
                                       TABLE_SECTION,
                                       NULL,
                                       &lTableContext
                                       );

        for (
             LONG lTableCounter = 0;
             lTableCounter < lTotalTableNumber;
             lTableCounter++
             )
        {
            // read the Table name in the Tables section
            WCHAR lTableName[SIZELINEMAX];

            bOK = SetupGetLineTextW(
                                   &lTableContext,
                                   NULL,
                                   NULL,
                                   NULL,
                                   lTableName,
                                   SIZELINEMAX,
                                   NULL
                                   );

            // fetch the next line's context
            if (!bOK)
            {
                g_FatalError = true;
                bError = true;
                break;
            }

            // Rowset pointer
            CComPtr<IRowset>  lpRowset;

            //////////////////////
            // create one rowset
            //////////////////////
            hres = Database.InitializeRowset(lTableName, &lpRowset);

            if (FAILED(hres))
            {
                g_FatalError = true;
                bError = true;
                break;
            }
            else
            {
                // create the simpletable
                CSimpleTableEx    lSimpleTable;

                lSimpleTable.Attach(lpRowset);
                lSimpleTable.MoveFirst();

                //////////////////////////////////////////////////////
                // now one table and all its columns are well known.
                // empty database assumed
                // process the rows
                //////////////////////////////////////////////////////
                hres = ProcessAllRows(
                                      hINF,
                                      lSimpleTable,
                                      lTableName
                                      );

                if (FAILED(hres))
                {
                    // process rows should not fail, even if no rows are read
                    bError       = true;
                    g_FatalError = true;
                }
            }

            //////////////////////////////////////////////////////
            // Read the next table's name (if any)
            //////////////////////////////////////////////////////
            bOK = SetupFindNextLine(
                                      &lTableContext,
                                      &lTableContext
                                     );
            if (!bOK)
            {
                if ((lTableCounter + 1) < lTotalTableNumber)
                {
                   // find next line should not crash. fatal error
                   g_FatalError = true;
                   bError       = true;
                   break;
                }
                break;
            } //end of tables
        }
    }
    else
    {// no tables: do nothing
        TracePrintf("Info: No [tables] section in the inf file\n");
        // bError = true;
    }

    if (bError)
    {
        LPVOID              lpMsgBuf;
        FormatMessageW(
                      FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL,
                      GetLastError(),
                      // Default language
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      (LPWSTR) &lpMsgBuf,
                      0,
                      NULL
                      );

        // Display the string.
        TracePrintf("Error: %S",lpMsgBuf);

        // Free the buffer.
        LocalFree( lpMsgBuf );
        hres = E_FAIL;
    }

    return      hres;
}


//////////////////////////////////////////////////////////////////////////////
//
// ProcessAllRows
//
//////////////////////////////////////////////////////////////////////////////
HRESULT ProcessAllRows(
                       const HINF&       hINF,
                       CSimpleTableEx&  pSimpleTable,
                       const WCHAR*     pTableName
                       )
{
   _ASSERTE(pTableName != NULL);

    ////////////////////
    // process the rows.
    ////////////////////

   wstring              lwsRowSection;
   lwsRowSection        += pTableName;

   LONG                 lRowCounter = 1;

   INFCONTEXT           lRowContext;

    #ifdef DEBUG
       TracePrintf("Info: %S",lwsRowSection.c_str());
    #endif

    ////////////////////////////////////////////////////
    // if <>0 do a loop on the lines (ie row names)
    ////////////////////////////////////////////////////
    BOOL bOK = SetupFindFirstLineW(
                           hINF,
                           // section in which to find a line
                           lwsRowSection.c_str(),
                           NULL,          // optional, key to search for
                           &lRowContext  // context of the found line
                           );

    HRESULT              hres = S_OK;

    if (!bOK)
    {
        //no such section (end of section = end of rows)
    }
    else
    {

        LONG                lTotalLinesNumber = 0; //safer
        //////////////////////////////////////////////////////
        // Get the Number of lines in that section. (& check)
        //////////////////////////////////////////////////////
        lTotalLinesNumber = SetupGetLineCountW(
                                          hINF, // handle to the INF file
                                   // the section in which to count lines
                                          lwsRowSection.c_str()
                                          );


        //////////////////////////////
        // Read eerything (loop)
        //////////////////////////////
        for (
             LONG lLinesCounter = 0;
             lLinesCounter < lTotalLinesNumber;
             lLinesCounter++
            )
        {

            #ifdef DEBUG
              TracePrintf("Info: for loop: %d", lLinesCounter);
            #endif

            ///////////////////////////////////////////////
            // read the Table name in the Tables section
            ///////////////////////////////////////////////
            WCHAR lLineName[SIZELINEMAX];

            bOK = SetupGetLineTextW(
                                    &lRowContext,
                                    NULL,
                                    NULL,
                                    NULL,
                                    lLineName,
                                    SIZELINEMAX,
                                    NULL
                                   );

            if (!bOK)
            {
                g_FatalError = true;
                TracePrintf("Error: SetupGetLineText Failed "
                               "in ProcessAllRows");
            }
            else //everything is ok, process the corresponding row
            {
                ///////////////////////
                // process the rows
                ///////////////////////
                hres = ProcessOneRow(
                                      hINF,
                                      pSimpleTable,
                                      lLineName
                                    );
                if (FAILED(hres)) { g_FatalError = true; }

            }

            //////////////////////////////////
            // fetch the next line's context
            //////////////////////////////////
            bOK = SetupFindNextLine(
                                    // starting context in an INF file
                                    &lRowContext,
                                    // context of the next line
                                    &lRowContext
                                   );
            if (!bOK)
            {
                ////////////////////////////////////////////////
                // end of the lines
                // compare the counter to the max to make sure
                // that last line is ok
                ////////////////////////////////////////////////
                if((lLinesCounter + 1) < lTotalLinesNumber)
                {
                    // too early
                    g_FatalError = true;
                    TracePrintf("Error: FindNext Line failed."
                                  "Not enough lines in the section %S",
                                  lwsRowSection.c_str());

                }
            }
        }
    }

    return      hres;
}


//////////////////////////////////////////////////////////////////////////////
//
// ProcessOneRow
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
WINAPI
ProcessOneRow(
    HINF inf,
    CSimpleTableEx& table,
    PCWSTR rowName
    )
{
   // Iterate through the columns in the database and see if the INF file
   // specifies a value for each column.
   for (DBORDINAL i = 0; i < table.GetColumnCount(); ++i)
   {
      // First try with a stack-based buffer.
      WCHAR buffer[1024];
      PWCHAR text = buffer;
      DWORD textSize = sizeof(buffer) / sizeof(buffer[0]);
      BOOL success = SetupGetLineTextW(
                         NULL,
                         inf,
                         rowName,
                         table.GetColumnName(i),
                         text,
                         textSize,
                         &textSize
                         );
      DWORD error = success ? NO_ERROR : GetLastError();

      if (error == ERROR_INSUFFICIENT_BUFFER)
      {
         // The stack-based buffer wasn't big enough, so allocate one on the
         // heap ...
         text = (PWCHAR)HeapAlloc(
                            GetProcessHeap(),
                            0,
                            textSize * sizeof(WCHAR)
                            );
         if (!text) { return E_OUTOFMEMORY; }

         // ... and try again.
         success = SetupGetLineTextW(
                       NULL,
                       inf,
                       rowName,
                       table.GetColumnName(i),
                       text,
                       textSize,
                       &textSize
                       );
         error = success ? NO_ERROR : GetLastError();
      }

      // If we successfully retrieved the line text AND it has at least one
      // character ...
      if (!error && textSize > 1)
      {
         // ... then process based on the column data type.
         switch (table.GetColumnType(i))
         {
            case DBTYPE_I4:
            {
               table.SetValue(i, _wtol(text));
               break;
            }

            case DBTYPE_WSTR:
            {
               table.SetValue(i, text);
               break;
            }

            case DBTYPE_BOOL:
            {
               table.SetValue(i, (VARIANT_BOOL)_wtol(text));
               break;
            }
         }
      }

      // Free the heap-based buffer if necessary.
      if (text != buffer) { HeapFree(GetProcessHeap(), 0, text); }

      switch (error)
      {
         case NO_ERROR:
            // Everything succeeded.
         case ERROR_INVALID_PARAMETER:
            // SETUPAPI didn't like the column name.
         case ERROR_LINE_NOT_FOUND:
            // The INF file didn't provide a value for this column.
            break;

         default:
            // Something went wrong.
            return HRESULT_FROM_WIN32(error);
      }
   }

   // All the columns are populated, so insert the row.
   return table.Insert();
}