#include "baseserv.h"
#include "xml.h"
#include <wx/config.h>
#include <wx/filename.h>
#include <wx/tokenzr.h>

#ifdef WIN32
#include <wx/fileconf.h>
#endif

#define		LF_TABGEN 3
#define		LF_TABCOM	4
#define		LF_TAB		5

//////////////////////////////////////////////////////////
// Date utilities
///////////////////////////////////////////////////////////

static const byte _days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int last_day(int month, int year)
  // parse_filastrok(
  //    "trenta giorni ha novembre
  //    con april, giugno e settembre
  //    son ventotto case uno
  //    per default ce n'ha trentuno");
{
  int d = _days_in_month[month-1];
  if (month == 2 && (year % 4) == 0)
    d++;
  return d;
}

bool isdate(const wxChar * s)
{ 
  const int len = strlen(s);
  if (len != 8 && len != 10)
    return false;
  
  int d = 0, m = 0, y = 0, i;
  if (len == 8)
  {          
    for (i = 0; i < 8; i++)
      if (!isdigit(s[i])) break;
    if (i == 8)
    {
      wxString str(s);
      d = atoi(((const char *)str)+6); str[6] = '\0';
      m = atoi(((const char *)str)+4); str[4] = '\0';
      y = atoi(((const char *)str)+0);
    }  
  } 
  else
    if (len == 10)
    {
      if (s[2] == s[5] && !isdigit(s[2]))
      {
        d = atoi(s);
        m = atoi(&s[3]);
        y = atoi(&s[6]);
      }
    } 
  
  if (d < 1 || d > 31 ||
      m < 1 || m > 12 ||
      y < 0)
    return false;

  return d <= last_day(m,y);
}

long date2ansi(const wxChar * s)
{
  long val = 0L;
  
  const int len = (s && *s >= '0') ? strlen(s) : 0;
  if (len != 8 && len != 10)  // Chi si azzarda ad aggiungere len != 6 ?
    return 0L;

  if (len == 8)
  {          
    int i;
    for (i = 0; i < 8; i++)
      if (!isdigit(s[i])) break;
    if (i == 8)
      val = atol(s);
  } 
  else
  {
    if (s[2] == s[5] && !isdigit(s[2]))
    {
      const int d = atoi(s);
      const int m = atoi(&s[3]);
      const int y = atoi(&s[6]);
      val = d + m + 100 +  y * 10000;
    }
  } 
	return val;
}

///////////////////////////////////////////////////////////
// TPostmanServer
///////////////////////////////////////////////////////////

class TPostmanServer : public TBaseServerApp
{
  wxArrayString m_Editors;
  wxArrayString m_FirmEditors;
	int						m_Logicnum;
	int						m_Firm;
	int						m_LastFirm;
	wxArrayString	m_TableName;
	int           m_Err;
	wxString			m_DataPath;

protected:
	virtual void ProcessHttpGet(wxString cmd, wxSocketBase& outs);
	bool IsMagicName(wxString& strFilename) const;
	virtual const wxChar* GetAppName() const;
	void AbsoluteNameName(wxString & name);
	const wxString Table2Editor(const wxString & table);
	bool LoadEditors(const wxChar * filename, wxArrayString & editors);
	void SaveXml2IniParagraph(wxFileConfig & ini, TXmlItem& xmlItem);
	void SaveTransaction(const TXmlItem& xmlMethod, wxString & filename);
	void AdjustTransaction(wxString & filename);
	const wxString EditorCommand();
	void ExecuteTransaction(wxString & filename);
	void LoadXmlParagraph(TXmlItem & xmlItem, wxString & paragraph, wxString & filename);
	void LoadTransaction(wxString & filename, TXmlItem & xmlAnswer);
	void Log(TXmlItem& xmlItem, wxString & filename);
  virtual void SoapProcessMethod(const TXmlItem& xmlMethod, TXmlItem& xmlAnswer);
	virtual int GetDefaultPort() const;

 public:
 	virtual bool Initialization();
  
	TPostmanServer() : m_Logicnum(0), m_Err(0), m_Firm(0), m_LastFirm(0) {}
};

void TPostmanServer::ProcessHttpGet(wxString cmd, wxSocketBase& outs)
{
  const int stop = cmd.Find(" HTTP");
	wxString str = cmd.Mid(4, stop-4).Trim();

	if (str == "/")
		str += "index.htm";
	wxString strFilename = GetDocumentRoot() + str;

	IsMagicName(strFilename);
	SendFile(strFilename, outs);
}

bool TPostmanServer::IsMagicName(wxString& strFilename) const
{
	wxFileName fnName(strFilename);
	wxString strName(fnName.GetName().Lower());

	if (strName == "index" || strName == "log")
	{
		strFilename = GetLogFileName();
		return true;
	}

  return false;
}

const wxString TPostmanServer::Table2Editor(const wxString & table)
{
	wxString row;
	wxString module;
	wxString editor;
	wxString name;

	{
		name << "../recdesc/d" << table << ".des";
		name.Lower();

		wxFileName f(name);

		f.MakeAbsolute();
		name = f.GetFullPath();
		wxTextFile file(name);

		if (file.Open())
		{
			for (row = file.GetFirstLine(); !file.Eof(); row = file.GetNextLine())
			{
				row.Trim(false);
				if (row.StartsWith("Module"))
				{
					const int pos = row.Find("=");
					
					if (pos >= 0)
						module = row.Mid(pos + 1);
					module.Trim();
					module.Trim(false);
					break;
				}
			}
		}
	}
	if (module.IsEmpty())
	{
		module = "ba";
		name = "../??tb";
		name << table << ".msk";
		name.Lower();
		
		wxFileName f(name);

		f.MakeAbsolute();
		name = f.GetFullPath();
		wxString foundfile = ::wxFindFirstFile(name, wxFILE);
		while (!foundfile.IsEmpty())
		{
			f = foundfile;
			wxString str(f.GetName().Left(2));
			if (str != "ba")
			{
				module = str;
				break;
			}
			foundfile = ::wxFindNextFile();
		}
	}

	wxFileName f("../install.ini");

	f.MakeAbsolute();
	name = f.GetFullPath();
	wxTextFile file(name);
	wxString wmodule;

	if (file.Open())
	{
		for (row = file.GetFirstLine(); !file.Eof(); row = file.GetNextLine())
		{
			row.Trim(false);
			if (row.StartsWith("["))
				wmodule = row.Mid(1, 2);
			else
				if (row.StartsWith("Edit_"))
				{
					const int pos = row.Find("=");
					
					if (pos >= 0)
					{				
						const wxString table(row.Mid(5, pos - 4));
						const int logicnum = atoi(table);

						if ((logicnum <= LF_TAB && module == wmodule) || table == m_TableName[m_Logicnum])
						{
							editor = row.Mid(pos + 1);	
							editor.Trim(false);
							editor << " ";
							if (m_Logicnum == LF_TABGEN)
								editor << '#';
							else
								if (m_Logicnum == LF_TABCOM)
									editor << '%';
							editor << m_TableName[m_Logicnum];
							if (table == m_TableName[m_Logicnum])
								break;
						}
					}
				}
		}
	}
  return editor;
}

bool TPostmanServer::LoadEditors(const wxChar * filename, wxArrayString & editors)
{
	wxFileName f(filename);

	f.MakeAbsolute();
	wxString name(f.GetFullPath());
	wxTextFile file(name);
	wxString row;
	wxString module;

	if (file.Open())
	{
		for (row = file.GetFirstLine(); !file.Eof(); row = file.GetNextLine())
		{
			wxString editor;

			row.Trim(false);
			if (row.StartsWith("["))
				module = row.Mid(1, 2);
			else
				if (row.StartsWith("Edit_"))
				{
					const int pos = row.Find("=");
					
					if (pos < 0)
						continue;
					
					const int logicnum = atoi(row.Mid(5, pos - 4));

					if (logicnum  < 7 && module != "ba")
						continue;
					editor = row.Mid(pos + 1);	
					editor.Trim(false);
					int nrows2add = logicnum - editors.GetCount() + 1;

					if (nrows2add > 0)
						editors.Add(wxEmptyString, nrows2add);
					editors[logicnum] = editor;
				}
		}
	}
  return editors.GetCount() > 0;
}

int TPostmanServer::GetDefaultPort() const
{
	return GetConfigInt("Port", 8080);
}

void TPostmanServer::SaveXml2IniParagraph(wxFileConfig& ini, TXmlItem& xmlItem)
{
	int items = xmlItem.GetChildren();

	for (int i = 0; i < items; i++)
	{
		TXmlItem * child = xmlItem.GetChild(i);

		wxString key(child->GetAttr("Name"));
		wxString val(child->GetEnclosedText());

		if (key == "Firm")
			m_Firm = atoi(val);

		ini.Write(key, val);
	}
}

void TPostmanServer::SaveTransaction(const TXmlItem& xmlMethod, wxString & filename)
{
	wxString paragraph;
	int items = xmlMethod.GetChildren();

	if (wxFileName::FileExists(filename))
		wxRemoveFile(filename);
	for (int i = 0; i < items; i++)
	{
		TXmlItem * child = xmlMethod.GetChild(i);
		const int logicnum = atoi(child->GetAttr("LogicNumber"));

		paragraph = "/";
		if (logicnum > 0)
		{
			if (m_Logicnum == 0)
				m_Logicnum = logicnum;
			paragraph << logicnum;

			const int rownum = atoi(child->GetAttr("RowNumber"));

			if (rownum > 0)
				paragraph << ',' << rownum;
			int nrows2add = logicnum - m_TableName.GetCount() + 1;

			if (nrows2add > 0)
				m_TableName.Add(wxEmptyString, nrows2add);
			m_TableName[logicnum] = child->GetAttr("TableName");
		}
		else 
			paragraph << child->GetTag();
		wxFileConfig ini("", "", filename, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);
		ini.SetPath(paragraph);
		SaveXml2IniParagraph(ini, *child);
	}
}

void TPostmanServer::AdjustTransaction(wxString & filename)
{
	wxTextFile file(filename);
	bool changed = false;

	if (file.Open())
	{
		for (wxString & row = file.GetFirstLine(); !file.Eof(); row = file.GetNextLine())
		{
			const int pos = row.Find("\\,");
			if (pos > 0)
			{
				row.Remove(pos, 1);
				changed = true;
				size_t rowno = file.GetCurrentLine();
				file.InsertLine(row, rowno);
				file.RemoveLine(rowno + 1);
			}
		} 
		if (changed)
			file.Write();
	}
	else
		WriteLog("Can't create Transaction file");
}

const wxString TPostmanServer::EditorCommand()
{
	const bool firmchanged = m_Firm != m_LastFirm;

	m_LastFirm = m_Firm;
	if (firmchanged)
	{
		m_FirmEditors.empty();
		wxString name(m_DataPath);
		wxChar l = name[name.Len() - 1];

		if (l != '/' && l != '\\')
			name << '/';

		wxString str; 
		
		str << m_Firm;
		while(str.Len() < 5)
			str.insert(0, "0");
		name << str << "a/ditta.ini";
		LoadEditors(name.c_str(), m_FirmEditors);
	}
	wxString table(m_TableName[m_Logicnum]);

	if (m_Logicnum <= LF_TAB && !table.IsEmpty())
	{
		wxString editor(Table2Editor(table));
		
		if (!editor.IsEmpty())
			return editor;
	}
	if (m_Logicnum < (int) m_FirmEditors.GetCount() && !m_FirmEditors[m_Logicnum].IsEmpty())
		return m_FirmEditors[m_Logicnum].c_str();
	if (m_Logicnum < (int) m_Editors.GetCount() && !m_Editors[m_Logicnum].IsEmpty())
		return m_Editors[m_Logicnum].c_str();
	return wxEmptyString;
}

void TPostmanServer::ExecuteTransaction(wxString & filename)
{
	wxString editor(EditorCommand());
	if (!editor.IsEmpty())
	{
		wxFileName cmdname(editor);

		if (cmdname.IsRelative())
		{
			cmdname.MakeAbsolute();
			cmdname.RemoveLastDir();
		}
	
		wxString command(cmdname.GetFullPath());

		const wxString currdir(wxGetCwd());
		
		wxSetWorkingDirectory(cmdname.GetPath());
		command << " -i" <<filename << " -uADMIN";
		m_Err = 0;
		if (::wxExecute(command, wxEXEC_SYNC) < 0)
		{
			wxString str;

			m_Err = 2;
			str << "Errore nell'esecuzione di " << command << m_Err ;
			WriteLog(str);

		}
		wxSetWorkingDirectory(currdir);
	}
}

void TPostmanServer::LoadXmlParagraph(TXmlItem& xmlItem, wxString& paragraph, wxString & filename)
{	
	wxString path;
	wxFileConfig ini("", "", filename, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);

	path << "/" << paragraph;
	ini.SetPath(path);
	wxStringTokenizer tok(paragraph, ",");
	wxString tag = tok.GetNextToken();
	const size_t logicnum = atoi(tag);
	const wxChar * attr = logicnum > 0 ? "Field" : "Attr";
	int rownum = 0 ;
	
	if (logicnum > 0 && tok.HasMoreTokens())
		rownum = atoi(tok.GetNextToken());
	if (logicnum > 0)
		tag = "Record";
	
	TXmlItem & child =xmlItem.AddChild(tag);

	if (logicnum > 0)
	{
		child.SetAttr("LogicNumber", logicnum);
		if (logicnum  < m_TableName.GetCount())
			child.SetAttr("TableName", m_TableName[logicnum]);
		
		if (rownum > 0)
			child.SetAttr("RowNumber", rownum);
	}

	wxString s;
	wxString key;
	long index = 0;
	
	for (bool ok = ini.GetFirstEntry(key, index); ok; ok = ini.GetNextEntry (key, index))
	{
		wxString val(ini.Read(key));
	
		if (val.StartsWith("\"") && val.EndsWith("\""))
		{
			val = val.Mid(1);
			val[val.Len()] = '\0';
		}
		else
		{
			val.Trim();
			val.Trim(false);
		}
		if (isdate(val))
			child.AddSoapInt(attr, date2ansi(val)).SetAttr("Name", key);
		else
		{
			if (val.IsNumber())
			{
				if (key == "Error" && m_Err != 0)
					child.AddSoapInt(attr, m_Err).SetAttr("Name", key);
				else
					child.AddSoapInt(attr, atoi(val)).SetAttr("Name", key);
			}
			else
			{
				if (key == "Result" && m_Err != 0)
					child.AddSoapString(attr, "ERROR").SetAttr("Name", key);
				else
					child.AddSoapString(attr, val).SetAttr("Name", key);
			}
		}
	}
}

void TPostmanServer::LoadTransaction(wxString & filename, TXmlItem& xmlAnswer)
{
	wxString paragraph;
	long index = 0;
	wxFileConfig ini("", "", filename, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);

  for (bool ok = ini.GetFirstGroup(paragraph, index); ok; ok = ini.GetNextGroup(paragraph, index))
		LoadXmlParagraph(xmlAnswer, paragraph, filename);
}

void TPostmanServer::Log(TXmlItem& WXUNUSED(xmlItem), wxString & filename)
{
	wxFileConfig ini("", "", filename, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);
	wxString str;
	wxString key;
	long index = 0;

	ini.SetPath("/Transaction");
	str << "Transaction : Command " << m_Editors[m_Logicnum];
	WriteLog(str);
	for (bool ok = ini.GetFirstEntry(key, index); ok; ok = ini.GetNextEntry (key, index))
	{
		wxString val(ini.Read(key));

    str = "              ",
		str << key << " = " << val;
		WriteLog(str);
	}
}

void TPostmanServer::SoapProcessMethod(const TXmlItem& xmlMethod, TXmlItem& xmlAnswer)
{
	const wxString& strMethod = xmlMethod.GetTag();
	if (strMethod == "m:CampoTransaction")
	{
		m_Logicnum = 0;
		wxFileName filename(GetTempFilename("ini"));

		wxString name(filename.GetFullPath());
		SaveTransaction(xmlMethod, name);
		AdjustTransaction(name);
		ExecuteTransaction(name);
		LoadTransaction(name, xmlAnswer);
		Log(xmlAnswer, name);
	}
}

// Implementazione delle due funzioni pure virtuali

const wxChar* TPostmanServer::GetAppName() const
{
	return "Postman";
}

bool TPostmanServer::Initialization()
{
	wxFileName f("../campo.ini");

	f.MakeAbsolute();
	wxString name(f.GetFullPath());
	wxTextFile file(name);
	wxString row;

	if (file.Open())
	{
		for (row = file.GetFirstLine(); !file.Eof(); row = file.GetNextLine())
		{
			if (row.StartsWith("Study"))
			{
				const int pos = row.Find('=');
				if (pos >= 0)
				{
					m_DataPath = row.Mid(pos + 1); 
					m_DataPath.Trim(false);
				}
				break;
			}
		}
	}
	if (m_DataPath.IsEmpty())
	{
		WriteLog("No data path");
		return false;
	}
	if (!LoadEditors("../install.ini", m_Editors))
	{
		WriteLog("No editors found");
		return false;
	}
	return true;
}


// Istanziare l'applicazione principale

IMPLEMENT_APP(TPostmanServer)