//---------------------------------------------------------------------------

//#define STL_fstream
#include <clx.h>
#pragma hdrstop
#include <map>
#include <string>
#ifdef STL_fstream
  #include <fstream>
#else
  #include <stdio.h>
#endif
using namespace std;

#include "SQL.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.xfm"
TSQLModule *SQLModule;


#ifndef STL
template<class key_t, class value_t>
class MiniMap
{
public:
  MiniMap();
  class TooManyEntries;
  value_t& Add(const key_t& key, const value_t& value);
  Delete(const key_t& key) {} // not implemented
  value_t& operator[](const key_t& key);
  int size() {return m_size};
  bool hasKey(const key_t& key);
protected:
  static const int MAX_ENTRIES = 20;
  key_t keys[MAX_ENTRIES];
  value_t values[MAX_ENTRIES];
  int m_size;
};

template<class key_t, class value_t>
MiniMap<key_t, value_t>::MiniMap()
{
  m_size = 0;
}

template<class key_t, class value_t>
value_t& MiniMap<key_t, value_t>::Add(const key_t& key, const value_t& value)
{
  if (m_size + 1 > MAX_ENTRIES)
    throw TooManyEntries();

  int index = m_size;
  m_size++;
  keys[index] = key;
  values[index] = value;
  return values[index];
}

template<class key_t, class value_t>
value_t& MiniMap<key_t, value_t>::operator[](const key_t& key)
{
  for (int i = 0; i < m_size; i++)
    if (keys[i] == key)
      return values[i];
  return Add(key, value_t());
}

template<class key_t, class value_t>
bool MiniMap<key_t, value_t>::hasKey(const key_t& key)
{
  for (int i = 0; i < m_size; i++)
    if (keys[i] == key)
      return true;
  return false;
}

int max(int a, int b)
{
  return a > b ? a : b;
}

#endif


//---------------------------------------------------------------------------
__fastcall TSQLModule::TSQLModule(TComponent* Owner)
        : TDataModule(Owner)
{
}
//---------------------------------------------------------------------------
/* finds a unique ID for a given table by searching the max existing id
   and increasing that. this does not garantee uniqueness when ids have
   deleted, but retain referenced in other columns, and it fails, if
   2 independant callers try to get an id at the same time, but that won't
   happen here, at least as long as this is the only app writing these tables.
   But there's another gotcha: the TSQLClientDataSet of the caller
   caches new records without writing them to the server, so adding several
   rows subsequently won't change the server. To prevent giving out the same
   ID several times, I store the IDs I gave out. */
int __fastcall TSQLModule::GetNewID(const AnsiString& table,
                                    const AnsiString& idcolumn)
{
   static map<AnsiString, int> oldids; /* IDs I gave out in previous calls,
           so that I don't give them out again.
           indexed by table (assuming only one ID column per table)
           value is the highest ID I gave out */

   SearchDataSet->Active = false;
   SearchDataSet->CommandText = "SELECT MAX(" + idcolumn + ") FROM " + table;
   SearchDataSet->Active = true;
   SearchDataSet->First();
   int maxid_db = SearchDataSet->FieldValues["MAX(" + idcolumn + ")"];
   int result;
   if (oldids.find(idcolumn) != oldids.end())
      result = max(maxid_db + 1, oldids[idcolumn] + 1);
   else
      result = maxid_db + 1;
   oldids[idcolumn] = result;
   return result;
}
//---------------------------------------------------------------------------
typedef map<string, string> settings_t;
void ReadConfigFile(const char* filename, settings_t& settings)
{
#ifdef STL_fstream
  fstream file(filename, ios::in);
  if (!file.is_open())
    return;

  string line, key, value;
  while (!file.eof())
  {
    getline(file, line);
#else
  FILE* file = fopen(filename, "r");
  if (!file)
    return;

  string line, key, value;
  const int BUFSIZE = 1000;
  char cstr[BUFSIZE];
  while(fgets(cstr, BUFSIZE, file))
  {
    line = cstr;
    line = line.substr(0, line.length() - 1);
#endif

    if(line[0] == '#' || line.empty())
      continue;

    int split = line.find('=');
    if (split == -1)
      continue;

    key = line.substr(0, split);
    value = line.substr(split + 1, line.length() - split);

    settings[key] = value;
  }
#ifndef STL_fstream
  fclose(file);
#endif
}
void ReadConfig(settings_t& settings)
{
  ReadConfigFile("/usr/local/share/mythtv/mysql.txt", settings);
  ReadConfigFile("/usr/share/mythtv/mysql.txt", settings);
  ReadConfigFile("/etc/mythtv/mysql.txt", settings);
  string homefile = getenv("HOME");
  homefile += "/.mythtv/mysql.txt";
  ReadConfigFile(homefile.c_str(), settings);
}

void __fastcall TSQLModule::DataModuleCreate(TObject *Sender)
{
  settings_t settings;
  ReadConfig(settings);
#ifdef STL_fstream
  hostname = settings["DBHostName"].c_str();
  databasename = settings["DBName"].c_str();
  username = settings["DBUserName"].c_str();
  password = settings["DBPassword"].c_str();
#else
  hostname = settings["DBHostName"].c_str();
  databasename = settings["DBName"].c_str();
  username = settings["DBUserName"].c_str();
  password = settings["DBPassword"].c_str();
#endif
  if (hostname.IsEmpty())
  {
    MessageDlg("Database login",
               "mysql.txt cound not be found and read. "
               "Please create a valid config file. "
               "See the MythTV docs for more info.",
               mtError,
               TMsgDlgButtons() << mbAbort,
               0, mbAbort);
    exit(1);
  }

  Conn->Params->Values["HostName"] = hostname;
  Conn->Params->Values["Database"] = databasename;
  Conn->Params->Values["User_Name"] = username;
  Conn->Params->Values["Password"] = password;
}
//---------------------------------------------------------------------------

