//------------------------------------------------------------------------------

#include "stdafx.h"
#include "megatune.h"
#include "hexedit.h"
#include "msDatabase.h"
#include "veconst.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

extern msDatabase mdb;

//------------------------------------------------------------------------------

hexedit::hexedit(CWnd* pParent /*=NULL*/, bool ramEdit)
 : CDialog   (hexedit::IDD, pParent),
   _base     (16),
   _ramEdit  (ramEdit),
   _baseAddr (0)
{
   //{{AFX_DATA_INIT(hexedit)
      // NOTE: the ClassWizard will add member initialization here
   //}}AFX_DATA_INIT
}

//------------------------------------------------------------------------------

void hexedit::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(hexedit)
   DDX_Control(pDX, IDC_BASEADDR_LABEL, m_baseAddrLabel);
   DDX_Control(pDX, IDC_BIN,      m_units);
   DDX_Control(pDX, IDC_COUNTER,  m_counter);
   DDX_Control(pDX, IDC_REALTIME, m_realtime);
   DDX_Control(pDX, IDC_BASEADDR, m_baseAddr);
   //}}AFX_DATA_MAP
}

//------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP(hexedit, CDialog)
   //{{AFX_MSG_MAP(hexedit)
      ON_BN_CLICKED(IDC_BIN,      OnBinary)
      ON_BN_CLICKED(IDC_BURN,     OnBurn)
      ON_BN_CLICKED(IDC_DEC,      OnDecimal)
      ON_BN_CLICKED(IDC_FETCH,    OnFetch)
      ON_BN_CLICKED(IDC_HEX,      OnHexadecimal)
      ON_BN_CLICKED(IDC_REALTIME, OnRealtime)

      ON_CONTROL_RANGE(EN_KILLFOCUS, IDC_CELL001, IDC_CELL256, OnCell)

      ON_EN_KILLFOCUS (IDC_BASEADDR, OnBaseAddr)

      ON_WM_CTLCOLOR()
      ON_WM_DESTROY()
      ON_WM_TIMER()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//------------------------------------------------------------------------------

static char *Bin(int n)
{
   static char d[10];
   int mask = 0x80;
   d[0] = '%';
   for (int i = 0; i < 8; i++) {
      d[i+1] = (mask & n) ? '1' : '0';
      mask = mask >> 1;
   }
   d[9] = 0;
   return d;
}

static char *Dec(int n)
{
   static char d[10];
   sprintf(d, "%d", n);
   return d;
}

static char *Hex(int n, int w=2)
{
   static char h[10];
   sprintf(h, "$%0*X", w, n);
   return h;
}

char *hexedit::Fmt(int n)
{
   switch (_base) {
      case  2: return Bin(n);
      case 10: return Dec(n);
      case 16: return Hex(n);
      default: return "???";
   }
}

//------------------------------------------------------------------------------

void hexedit::updateDisplay(bool force)
{
   for (int i = 0; i < nCell; i++) {
      unsigned char b = inputBuffer[i];
      if (force || displayBuffer[i] != b) {
         displayBuffer[i] = b;
         bool hasText = i < _tableSize || _ramEdit;
         hx[i]->SetWindowText(hasText ? Fmt(b) : "");
         hx[i]->EnableWindow (hasText);
      }
   }
}

//------------------------------------------------------------------------------

BOOL hexedit::OnInitDialog()
{
   CDialog::OnInitDialog();

   CFont   *f = this->GetFont();

   int r, c;

   // Make the row labels.
   for (r = 0; r < nRow; r++) {
      CRect l;
      l.top  = (H+7)+r*H;  l.bottom = l.top+H+1;
      l.left = 1;          l.right  = l.left+W-1;
      rl[r]  = new CStatic();
      rl[r]->Create(Hex(r*16), WS_CHILD | WS_VISIBLE | ES_RIGHT, l, this, 1);
      rl[r]->SetFont(f);
   }

   // Make the column labels.
   for (c = 0; c < nCol; c++) {
      CRect l;
      l.top  = 6;                   l.bottom = l.top+H+1;
      l.left = (W+(c<8?6:10))+c*W;  l.right  = l.left+W-4;
      cl[c]  = new CStatic();
      cl[c]->Create(Hex(c), WS_CHILD | WS_VISIBLE | ES_RIGHT, l, this, 1);
      cl[c]->SetFont(f);
   }

   for (r = 0; r < nRow; r++) {
      CRect l;
      l.top    = (H+5)+r*H;
      l.bottom = l.top+H+1;

      for (c = 0; c < nCol; c++) {
         int b     = r*nRow + c;

         l.left    = (W+(c<8?5:9))+c*W;
         l.right   = l.left+W+1;

         DWORD style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_RIGHT | ES_AUTOHSCROLL;

         hx[b] = new Cell(r, c, b);
         hx[b]->Create(style, l, this, IDC_CELL001+b);
         hx[b]->SetFont(f);
         hx[b]->SetReadOnly(_ramEdit);
      }
   }

   m_baseAddr.SetWindowText(Hex(_baseAddr, 4));
   m_baseAddr.ShowWindow       (_ramEdit);
   m_baseAddrLabel.ShowWindow  (_ramEdit);

   m_counter.SetWindowText("");

   static_cast<CButton *>(GetDlgItem(IDC_HEX))->SetCheck(true);

   fetch();
   updateDisplay(true);

   hx[0]->SetFocus();
   return FALSE;
}

//------------------------------------------------------------------------------

void hexedit::OnDestroy()
{
   CDialog::OnDestroy();

   for (int r = 0; r < nRow;  r++) delete rl[r];
   for (int c = 0; c < nCol;  c++) delete cl[c];
   for (int i = 0; i < nCell; i++) delete hx[i];
}

//------------------------------------------------------------------------------

void hexedit::fetch()
{
   if (_ramEdit) {// "F"etch command is available, so use it
      mdb.getMemory(static_cast<BYTE>(_baseAddr>>8), inputBuffer);
      _tableSize = 256;
   }
   else {
      mdb.getConst(0);
      if (!mdb.dualTable())
         _tableSize = 125;
      else {
         mdb.getConst(1);
         _tableSize = 256;
      }

      for (int i = 0; i < _tableSize; i++) inputBuffer[i] = mdb.ConstRaw(i);
   }
}

void hexedit::OnFetch()
{
   fetch();
   updateDisplay();
}

//------------------------------------------------------------------------------

void hexedit::timer(int state)
{
   if (state == on) {
      if (SetTimer(1, mdb.timerInterval, NULL) == 0)
         MessageBox("ERROR: Cannot install timer.\nKill other useless Windows Apps.");
   }
   else {
      KillTimer(1);
      Sleep(mdb.timerInterval);
   }
}

//------------------------------------------------------------------------------

void hexedit::OnTimer(UINT nIDEvent)
{
   OnFetch(); // Just duplicate the button click on timer event.

   static int c = 0;
   m_counter.SetWindowText(Dec(c++));
   CDialog::OnTimer(nIDEvent);
}

//------------------------------------------------------------------------------

void hexedit::OnRealtime()
{
   timer(m_realtime.GetCheck());
}

//------------------------------------------------------------------------------

void hexedit::OnBinary()
{
   _base =  2;
   updateDisplay(true);
}

void hexedit::OnDecimal()
{
   _base = 10;
   updateDisplay(true);
}

void hexedit::OnHexadecimal()
{
   _base = 16;
   updateDisplay(true);
}

//------------------------------------------------------------------------------

BOOL hexedit::DestroyWindow()
{
   return CDialog::DestroyWindow();
}

//------------------------------------------------------------------------------

HBRUSH hexedit::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
   HBRUSH hbr;

   pDC->SetBkMode(TRANSPARENT);

   Cell *pCel = dynamic_cast<Cell *>(pWnd);
   if (pCel == NULL)
      hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   else {
      COLORREF color = RGB(208, 208, 208);
      if (mdb.dualTable() || pCel->ofs < _tableSize) {
         int r = pCel->row;
         int c = 0;//pCel->col;
         color = (r%2) ? RGB(225+2*c, 225-2*c, 255) : RGB(255,255,255);
      }
      static CBrush brush;
      brush.DeleteObject();
      brush.CreateSolidBrush(color);
      hbr = brush;
   }

   return hbr;
}

//------------------------------------------------------------------------------

void hexedit::OnBaseAddr()
{
   char number[20];
   m_baseAddr.GetWindowText(number, sizeof(number));
   long addr;
   if (msDatabase::number(number, addr)) {
      _baseAddr = static_cast<WORD>(addr & 0xFF00);
      fetch();
      updateDisplay(true);
      m_baseAddr.SetWindowText(Hex(_baseAddr, 4));
   }
}

//------------------------------------------------------------------------------

void hexedit::OnCell(UINT nId)
{
   Cell *pCel = hx[nId-IDC_CELL001];

   char number[20];
   pCel->GetWindowText(number, sizeof(number));

   BYTE n;
   if (msDatabase::number(number, n)) {
      if (n != displayBuffer[pCel->ofs]) {
         if (!_ramEdit) {
            int pn = mdb.setPageNo(0);
            mdb.putConstByte(pCel->ofs, n, true);
            mdb.setPageNo(pn);
         }
         inputBuffer  [pCel->ofs] = BYTE(n);
         displayBuffer[pCel->ofs] = BYTE(n);
         pCel->SetWindowText(Fmt(n));
      }
   }
}

//------------------------------------------------------------------------------

void hexedit::OnBurn()
{
   mdb.burnConst();
}

