//*****************************************
//******** PC Configurator V1.00 **********
//*** (C) - 2001 B.Bowling/A. Grippo ******
//** All derivatives from this software ***
//**  are required to keep this header ****
//*****************************************

static char *rcsId() { return "$Id$"; }

#include "stdafx.h"
#include "megatune.h"
#include "msDatabase.h"
#include "Dconstants.h"
#include "Dreqfuel.h"
#include "DtableUse.h"
#include "math.h"
#include "veconst.h"

#ifdef _DEBUG
#  define new DEBUG_NEW
#  undef THIS_FILE
   static char    THIS_FILE[] = __FILE__;
#endif

extern msDatabase mdb;

//------------------------------------------------------------------------------
// Dconstants dialog

Dconstants::Dconstants(CWnd * pParent /*=NULL*/ )
 : CDialog(Dconstants::IDD, pParent)
{
   //{{AFX_DATA_INIT(Dconstants)
   //}}AFX_DATA_INIT
}

//------------------------------------------------------------------------------

void Dconstants::DoDataExchange(CDataExchange * pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(Dconstants)
	DDX_Control(pDX, IDC_TABLE_USE, m_tableUse);
	DDX_Control(pDX, IDC_FIDLE_LABEL, m_fidleLabel);
	//}}AFX_DATA_MAP
}

//------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP(Dconstants, CDialog)
   //{{AFX_MSG_MAP(Dconstants)
      ON_BN_CLICKED(IDB_REQFUEL,            OnDreqfuel)
      ON_BN_CLICKED(IDC_UP,                 OnUp)
      ON_BN_CLICKED(IDC_DOWN,               OnDown)
      ON_BN_CLICKED(IDC_TABLE_USE,          OnTableUse)

      ON_EN_KILLFOCUS(IDC_REQFUEL,          dbFromFields)
      ON_EN_KILLFOCUS(IDC_INJOPEN,          dbFromFields)
      ON_EN_KILLFOCUS(IDC_BATTFAC,          dbFromFields)
      ON_EN_KILLFOCUS(IDC_INJPWM,           dbFromFields)
      ON_EN_KILLFOCUS(IDC_INJPWMT,          dbFromFields)
      ON_EN_KILLFOCUS(IDC_REQFUELDOWN,      dbFromFields)
      ON_EN_KILLFOCUS(IDC_FASTIDLE,         dbFromFields)

      ON_CBN_SELCHANGE(IDC_ALGORITHM,       dbFromFields)
      ON_CBN_SELCHANGE(IDC_ALTERNATE,       dbFromFields)
      ON_CBN_SELCHANGE(IDC_DIVIDER,         dbFromFields)
      ON_CBN_SELCHANGE(IDC_STROKE,          dbFromFields)
      ON_CBN_SELCHANGE(IDC_PORTTYPE,        dbFromFields)
      ON_CBN_SELCHANGE(IDC_MAPTYPE,         dbFromFields)
      ON_CBN_SELCHANGE(IDC_NCYLINDERS,      dbFromFields)
      ON_CBN_SELCHANGE(IDC_NINJECTORS,      dbFromFields)
      ON_CBN_SELCHANGE(IDC_ENGINETYPE,      dbFromFields)
      ON_CBN_SELCHANGE(IDC_ALGORITHM,       dbFromFields)
      ON_CBN_SELCHANGE(IDC_BAROCORR_ENABLE, dbFromFields)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//------------------------------------------------------------------------------
// Dconstants message handlers

BOOL Dconstants::OnInitDialog()
{
   CDialog::OnInitDialog();

   boxes[0] = static_cast<CEdit *>(GetDlgItem(IDC_REQFUEL));
   boxes[1] = static_cast<CEdit *>(GetDlgItem(IDC_INJOPEN));
   boxes[2] = static_cast<CEdit *>(GetDlgItem(IDC_BATTFAC));
   boxes[3] = static_cast<CEdit *>(GetDlgItem(IDC_INJPWM));
   boxes[4] = static_cast<CEdit *>(GetDlgItem(IDC_INJPWMT));
   boxes[5] = static_cast<CEdit *>(GetDlgItem(IDC_REQFUELDOWN));
   boxes[6] = static_cast<CEdit *>(GetDlgItem(IDC_FASTIDLE));

   cboxes[0] = static_cast<CComboBox *>(GetDlgItem(IDC_ALTERNATE));
   cboxes[0]->SetCurSel(0);
   if (mdb.dualTable()) {
      m_tableUse.ShowWindow(true);
      cboxes[0]->ShowWindow(false);
   }
   else {
      m_tableUse.ShowWindow(false);
      cboxes[0]->ShowWindow(true);
   }

   cboxes[1] = static_cast<CComboBox *>(GetDlgItem(IDC_DIVIDER));
   cboxes[1]->SetCurSel(0);

   cboxes[2] = static_cast<CComboBox *>(GetDlgItem(IDC_STROKE));
   cboxes[2]->SetCurSel(0);

   cboxes[3] = static_cast<CComboBox *>(GetDlgItem(IDC_PORTTYPE));
   cboxes[3]->SetCurSel(0);

   cboxes[4] = static_cast<CComboBox *>(GetDlgItem(IDC_MAPTYPE));
   cboxes[4]->SetCurSel(0);

   cboxes[5] = static_cast<CComboBox *>(GetDlgItem(IDC_NCYLINDERS));
   cboxes[5]->SetCurSel(cboxes[5]->FindString(0, "8"));

   cboxes[6] = static_cast<CComboBox *>(GetDlgItem(IDC_NINJECTORS));
   cboxes[6]->SetCurSel(cboxes[6]->FindString(0, "8"));

   cboxes[7] = static_cast<CComboBox *>(GetDlgItem(IDC_ENGINETYPE));
   cboxes[7]->SetCurSel(cboxes[7]->FindString(0, "Even"));

   cboxes[8] = static_cast<CComboBox *>(GetDlgItem(IDC_ALGORITHM));
   cboxes[8]->SetCurSel(0);

   cboxes[9] = static_cast<CComboBox *>(GetDlgItem(IDC_BAROCORR_ENABLE));
   cboxes[9]->SetCurSel(0);

   if (mdb.controllerVersion < 2.0) {
      // Disable the stuff that doesn't work in 1.0.
      cboxes[7]->EnableWindow(false);
      cboxes[8]->EnableWindow(false);
      cboxes[9]->EnableWindow(false);
   }

   mdb.fixThermoLabel(m_fidleLabel);

   if (!mdb.loaded()) mdb.getConst();
   fieldsFromDb();

   return TRUE;
}

//------------------------------------------------------------------------------

void Dconstants::OnDreqfuel()
{
   mdb.nCylinders = cboxes[5]->GetCurSel() + 1;   // Pass to reqfuel calculator.

   Dreqfuel rfdb;
   if (rfdb.DoModal() == IDOK) {
      char s[10];
      sprintf(s, "%.1f", mdb.reqFuel/10.0);
      boxes[0]->SetWindowText(s);
      cboxes[5]->SetCurSel(mdb.nCylinders - 1);
      dbFromFields();
   }
}

//------------------------------------------------------------------------------

void Dconstants::OnUp()
{
   mdb.getConst();
   fieldsFromDb();
}

//------------------------------------------------------------------------------

void Dconstants::OnDown()
{
   mdb.putConstByte(Dconfig11);
   mdb.putConstByte(Dconfig12);
   mdb.putConstByte(Dconfig13);
   mdb.putConstByte(Dinjopen);
   mdb.putConstByte(Dbattfac);
   mdb.putConstByte(Dinjpwm);
   mdb.putConstByte(Dinjpwmt);
   mdb.putConstByte(Drpmk);
   mdb.putConstByte(Drpmk+1);
   mdb.putConstByte(Dfastidle);
   mdb.putConstByte(Ddivider);
   mdb.putConstByte(Dalternate);
   mdb.putConstByte(Dreq_fuel);

   mdb.burnConst();
}

//------------------------------------------------------------------------------

BEGIN_EVENTSINK_MAP(Dconstants, CDialog)
   //{{AFX_EVENTSINK_MAP(Dconstants)
   //}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

//------------------------------------------------------------------------------

static inline void setFld(CEdit *c, double d) { char s[11]; sprintf(s, "%.1f", d); c->SetWindowText(s); }
static inline void setFld(CEdit *c, int    i) { char s[11]; sprintf(s, "%d",   i); c->SetWindowText(s); }

void Dconstants::fieldsFromDb()
{
   int alternating = mdb.Const(Dalternate);
   //  nSquirts    = nCylinders / divider
   int divid       = mdb.Const(Ddivider);
   mdb.nSquirts    = int(0.00001 + double(mdb.nCylinders) / double(divid));
   mdb.strokeType  = (mdb.Const(Dconfig11) & STROKE_MASK_BITS) >> 2;
   mdb.portType    = (mdb.Const(Dconfig11) & INJECTIONTYPE_MASK_BITS) >> 3;
   mdb.mapType     = int(mdb.Const(Dconfig11));
   mdb.mapType     = mdb.mapType & MAP_MASK_BITS;
   mdb.nCylinders  = 1 + ((mdb.Const(Dconfig11) & NCYL_MASK_BITS) >> 4);
   mdb.nInjectors  = 1 + ((mdb.Const(Dconfig12) & NINJECT_MASK_BITS) >> 4);
   mdb.engType     = mdb.Const(Dconfig13) & 0x01;

   double rfqnum   = mdb.nInjectors / (double(divid) * double((mdb.dualTable() ? 0 : alternating) + 1));
   double reqFuel  = double(mdb.Const(Dreq_fuel)) * rfqnum + 0.0001;

   cboxes[0]->SetCurSel(alternating);
   cboxes[1]->SetCurSel(mdb.nSquirts-1); // Number of squirts.
   cboxes[2]->SetCurSel(mdb.strokeType);
   cboxes[3]->SetCurSel(mdb.portType);
   cboxes[4]->SetCurSel(mdb.mapType);
   cboxes[5]->SetCurSel(mdb.nCylinders - 1);
   cboxes[6]->SetCurSel(mdb.nInjectors - 1);
   cboxes[7]->SetCurSel(mdb.engType);
   cboxes[8]->SetCurSel(mdb.alphaN());
   cboxes[9]->SetCurSel(mdb.baroCorr());

   setFld(boxes[0], reqFuel/10.0);
   setFld(boxes[1], double(mdb.Const(Dinjopen) / 10.0));
   setFld(boxes[2], mdb.Const(Dbattfac) / 60.0);
   setFld(boxes[3], mdb.Const(Dinjpwm));
   setFld(boxes[4], double(mdb.Const(Dinjpwmt) / 10.0));
   setFld(boxes[5], double(mdb.Const(Dreq_fuel)/10.0));
   setFld(boxes[6], mdb.tempFromDb(mdb.Const(Dfastidle) - 40));
}

//------------------------------------------------------------------------------

void Dconstants::dbFromFields()
{
   CString        hhh;
   LPTSTR         pp;
   int            alternating;
   unsigned char  dividerdl;

   unsigned char  itv;
   double         tmp1;
   double         reqfuelsav, reqfueldl, reqfueldlsav;

   mdb.strokeType = cboxes[2]->GetCurSel();
   mdb.portType   = cboxes[3]->GetCurSel();
   mdb.mapType    = cboxes[4]->GetCurSel();
   mdb.nCylinders = cboxes[5]->GetCurSel() + 1;
   mdb.nInjectors = cboxes[6]->GetCurSel() + 1;
   mdb.engType    = cboxes[7]->GetCurSel();
   mdb.alphaN  (cboxes[8]->GetCurSel());
   mdb.baroCorr(cboxes[9]->GetCurSel());

   unsigned char  dlconfig = 0;
   if (mdb.strokeType) dlconfig = static_cast<unsigned char>(dlconfig | 0x04);
   if (mdb.portType)   dlconfig = static_cast<unsigned char>(dlconfig | 0x08);
   if (mdb.mapType)    dlconfig = static_cast<unsigned char>(dlconfig | 0x01);
   dlconfig = static_cast<unsigned char>(dlconfig | ((mdb.nCylinders - 1) << 4));

   unsigned char dlconfig12 = 0;
   dlconfig12 = static_cast<unsigned char>(dlconfig12 | ((mdb.nInjectors - 1) << 4));

   unsigned char dlconfig13 = 0;
   if (mdb.engType   ) dlconfig13 = static_cast<unsigned char>(dlconfig13 | ODDFIRE_MASK_BITS);
   if (mdb.egoType() ) dlconfig13 = static_cast<unsigned char>(dlconfig13 | O2SENSOR_MASK_BITS);
   if (mdb.alphaN()  ) dlconfig13 = static_cast<unsigned char>(dlconfig13 | ALPHAN_MASK_BITS);
   if (mdb.baroCorr()) dlconfig13 = static_cast<unsigned char>(dlconfig13 | BAROC_MASK_BITS);

   mdb.putConstByte(Dconfig11, dlconfig);
   mdb.putConstByte(Dconfig12, dlconfig12);
   mdb.putConstByte(Dconfig13, dlconfig13);

   boxes[0]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 *= 10.0;
   itv   = (unsigned char) tmp1;
   reqfuelsav = tmp1;

   boxes[5]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 *= 10.0;
   itv   = (unsigned char) tmp1;
   reqfueldlsav = tmp1;

   boxes[1]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 *= 10.0;
   itv   = (unsigned char) tmp1;
   if (tmp1 <= 0.0 || tmp1 > 255.0)
      MessageBox("ERROR: Injector Open Time outside of Range (0.1 to 25.5 ms)", "Injector Opening Time", MB_ICONERROR);
   else {
      if (itv < 10 || itv > 13) {
         MessageBox(
            "WARNING!\n\n"
            "You must be an EFI expert to set the value of injector opening time\n"
            "to a value outside the range 1.0-1.3 ms.  Hit F1 for more.\n\n"
            "Unless you have a valid reason for doing so, set it to 1.2 ms back right now.",
            "Injector Opening Time", MB_ICONWARNING);
      }
      mdb.putConstByte(Dinjopen, itv);
   }

   boxes[2]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 *= 6.0 * 10.0;
   itv   = (unsigned char) tmp1;

   if (tmp1 <= 0. || tmp1 > 60.0)
      MessageBox("ERROR: Injector Battery Factor outside of Range (0.01 to 1 V/ms)");
   else {
      if (itv < 6 || itv > 12) {
         MessageBox(
            "WARNING!\n\n"
            "You must be an EFI expert to set the value of battery factor rate\n"
            "to a value outside the range 0.1-0.2 ms.  Hit F1 for more.\n\n"
            "Unless you have a valid reason for doing so, set it back to 0.1 ms right now.",
            "Battery Factor Rate", MB_ICONWARNING);
      }
      mdb.putConstByte(Dbattfac, itv);
   }

   boxes[3]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   itv = (unsigned char) tmp1;
   if (tmp1 <= 0. || tmp1 > 100.0)
      MessageBox("ERROR: Injector PWM Duty Cycle outside of Range (1 to 100 Percent)");
   else
      mdb.putConstByte(Dinjpwm, itv);

   boxes[4]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 *= 10.0;
   itv   = (unsigned char) tmp1;
   if (tmp1 <= 0 || tmp1 > 255)
      MessageBox("ERROR: Injector PWM Threshold Time outside of Range (0.1 to 25.5 ms)");
   else
      mdb.putConstByte(Dinjpwmt, itv);

   unsigned short urpmk = static_cast<unsigned short>(mdb.strokeType == 0 ? 12000 : 6000);
   unsigned short rpmk  = (unsigned short) (urpmk / mdb.nCylinders);
   unsigned char  rpmkd = (unsigned char) ((rpmk & 0xFF00) / 256);
   mdb.putConstByte(Drpmk, rpmkd);

   rpmkd = (unsigned char) ((rpmk & 0x00FF));
   mdb.putConstByte(Drpmk+1, rpmkd);

   boxes[6]->GetWindowText((CString &) hhh);
   pp = hhh.GetBuffer(10); sscanf(pp, "%lf", &tmp1); hhh.ReleaseBuffer();
   tmp1 = mdb.dbFromTemp(tmp1);
   tmp1 += 40.0; itv = static_cast<unsigned char>(tmp1);
   if (tmp1 <= 0. || tmp1 > 255.0)
      MessageBox("ERROR: Fast Idle Threshold outside of Range (-40 to 215 Degrees F)");
   else
      mdb.putConstByte(Dfastidle, itv);

   int divid = cboxes[1]->GetCurSel() + 1; // User-view number of squirts per cycle.

   tmp1 = (0.0001 + double(mdb.nCylinders) / double(divid));
   dividerdl = (unsigned char) tmp1;

   if (fabs(double(dividerdl - tmp1)) > 0.1)
      MessageBox("Number of Squirts Not an Integer Divisor of\nCylinder Count");
   else
      mdb.putConstByte(Ddivider, dividerdl);

   // Alternating Injector Mode

   alternating = static_cast<unsigned char>(cboxes[0]->GetCurSel());
   if (!mdb.dualTable()) {
      mdb.putConstByte(Dalternate, static_cast<unsigned char>(alternating));
   }

   // Equation for determining Req_fuel_download from Req_fuel:
   //
   // REQ_FUEL_DL = REQ*FUEL * (B * N)/NINJ
   //
   // B = 1 if simultaneous, 2 if alternating
   // N = divider_number = ncyl/number_of_squirts
   // NINJ = number of injectors
   // When in dual table mode, alternating has a different meaning.

   double rfqnum = ((mdb.dualTable() ? 0 : alternating) + 1) * dividerdl;
   rfqnum    = rfqnum / double(mdb.nInjectors);
   reqfueldl = rfqnum * reqfuelsav;

   if ((int) reqfueldl != 10 * int(reqfueldlsav)) {
      if (reqfueldl > 255.0) {
         MessageBox("ERROR: Fuel/Squirt combination outside of Range\nIncrease Number of Squirts");
      }
      else if (reqfueldl <= 0.0) {
         MessageBox("ERROR: Injector Pulse Width Too Small\nDecrease Number of Squirts");
      }
      else {
         pp = hhh.GetBuffer(10); sprintf(pp, "%.1f", reqfueldl / 10.0); hhh.ReleaseBuffer();
         boxes[5]->SetWindowText(hhh);

         mdb.putConstByte(Dreq_fuel, static_cast<unsigned char>(reqfueldl));
      }
   }
}

//------------------------------------------------------------------------------

void Dconstants::OnTableUse()
{
   DtableUse tu;
   tu.DoModal();
}

//------------------------------------------------------------------------------

