#pragma option w32
#parsecommandline TRUE

#include "wxCmm.h--"
#include "shellapi.h--"
#include "RAEdit.h--"
#include "CiDE.rc"

#winmonoblock FALSE

#pragma option ia

wxFrame frame;
wxStaticText text;
wxMenuBar menuBar;
wxStatusBar statusBar1;
wxFileDialog fileDialog;

word proc_y = 150;
dword hEdit, hProcList;
dword hEditFont;
dword RAOldProc, ListOldProc, FrameOldProc;
byte szOpenFile[256];
dword nLine;
dword statbar_size = 0;
byte expand_true = 1;
dword hDebug;

#define EN_SELCHANGE  0x702
#define WM_ENTER      WM_USER + 2
#define EM_FINDTEXTEX WM_USER + 79
#define EM_EXSETSEL   WM_USER + 55


struct FINDTEXTEX {
       CHARRANGE chrg;
       dword     lpstrText;
       CHARRANGE chrgText;
};

struct _KEYS {
       dword KeyCode;
       dword Control;
       dword Shift;
       dword Alt;
       dword Action;
       dword Code;
       dword Desc;
};

LOGFONT font_type = {
       10,0,GM_COMPATIBLE,0,FW_DONTCARE,0,0,0,OEM_CHARSET,OUT_DEFAULT_PRECIS,
       CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"Terminal"
};

#define no_blocks 1

_RABLOCKDEF blocks_def[no_blocks] = {
        "{", "}", 0, 0, BD_NONESTING,
};

#define no_keys 11

_KEYS keys_table[no_keys] = {
      // key     CTRL   SHIFT  ALT    action           code   description
      VK_ESCAPE, FALSE, FALSE, FALSE, #onMenuExit,     0,     "ESC - exit",
      0x4F,      TRUE,  FALSE, FALSE, #onMenuOpen,     0,     "CTRL + O - open",
      0x53,      TRUE,  FALSE, FALSE, #onMenuSave,     0,     "CTRL + S - save",
      0x51,      TRUE,  FALSE, FALSE, #expand_all,     0,     "CTRL + Q - expand/collapse all",
      0x57,      TRUE,  FALSE, FALSE, #put_line,       0,     "CTRL + W - put divider line",
      0x45,      TRUE,  FALSE, FALSE, #del_line,       0,     "CTRL + E - delete divider line",
      VK_F11,    FALSE, FALSE, FALSE, #onMenuCompile,  0,     "F11 - compile & run",
      VK_F12,    FALSE, FALSE, FALSE, #onMenuRun,      0,     "F12 - run",
      VK_F1,     FALSE, FALSE, FALSE, #help_file,      0,     "F1 - win32.hlp",
      VK_F1,     TRUE,  FALSE, FALSE, #onMenuAbout,    0,     "CTRL + F1 - about",
      VK_F1,     FALSE, TRUE,  FALSE, #onMenuKeys,     0,     "SHIFT + F1 - keyboard shortcuts"
};

_hRAEditFont hRAEditFont;
_RACOLOR edit_colors;
FINDTEXTEX find;

void main()
{
  dword hDLL;

  InitCommonControls();
  hDLL = LoadLibrary("RAEdit.dll");
  if (hDLL == 0) {
     MessageBox(0,"Error loading dll","Error",MB_ICONWARNING);
  }
  else
  {
     wxTheApp.onInit = #init;
     wxTheApp.mainLoop();
  };
}

void init()
{
  frame.wxFrameFullArgs(NULL,-1,"C-iDE",0, new WxSize(800,600), wxDEFAULT_FRAME_STYLE | WS_CLIPCHILDREN);
  frame.Centre();
  frame.SetIconFromResource(IDI_MAINICON);
  DragAcceptFiles(frame.hWnd, TRUE);

  // associate icon for the program window
  SendMessage(frame.hWnd,WM_SETICON,ICON_BIG,LoadIcon(hThisInst,IDI_MAINICON));

  hProcList = CreateWindowEx(WS_EX_STATICEDGE,"LISTBOX","",LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE,
              0,0,0,0,frame.hWnd,0,hThisInst,NULL);
  // create editor window
  hEdit = CreateWindowEx(WS_EX_STATICEDGE, "RAEDIT",NULL,WS_CHILD | WS_VISIBLE | STYLE_NOSIZEGRIP | STYLE_NOCOLLAPSE,
          0,0,frame.GetClientSizeX(),frame.GetClientSizeY(),frame.hWnd,NULL,hThisInst,0);
  if (hEdit == 0) MessageBox(0,"Error creating window","Error",MB_ICONWARNING);

  // create font
  hEditFont = CreateFontIndirect(#font_type);
  SendMessage(hProcList, WM_SETFONT, EAX, 0);
  hRAEditFont.hFont = hEditFont;

  // load file from commandline
  // if (PARAMCOUNT() == 2) LoadFile(PARAMSTR(1));
  LoadFile("cide.c--");

  // --- sent font for RAEdit
  SendMessage(hEdit,REM_SETFONT,1,#hRAEditFont);
  FindResource(NULL, ID_WORDS, RT_RCDATA);
  LoadResource(NULL, EAX);
  SendMessage(hEdit,REM_SETHILITEWORDS,0x808080,EAX);
  // --- disable ; as comment
  SendMessage(hEdit,REM_SETCHARTAB,3Bh,2);
  // --- enable // as comment
  SendMessage(hEdit,REM_SETCHARTAB,2Fh,6);

  RAOldProc = SendMessage(hEdit, REM_SUBCLASS, 0, #RAEditProc);
  ListOldProc = SetWindowLong(hProcList,GWL_WNDPROC, #ListProc);
  FrameOldProc = SetWindowLong(frame.hWnd,GWL_WNDPROC, #FrameProc);

  connect(#frame,wxEVT_SIZE,#onSize);
  connect(#frame,WM_SETFOCUS,#onEvent);
  connect(#frame,WM_NOTIFY,#onNotify);
  connect(#frame,WM_DROPFILES, #onDrop);
  connect(#frame,WM_COMMAND, #onCommand);

  menuBar.wxMenuBarFromResource(MAINMENU);
  frame.SetMenuBar(#menuBar);

  connectMenu(#frame,ID_COMPILE,#onMenuCompile);
  connectMenu(#frame,ID_RUN,#onMenuRun);
  connectMenu(#frame,wxID_OPEN,#onMenuOpen);
  connectMenu(#frame,wxID_SAVE,#onMenuSave);
  connectMenu(#frame,wxID_EXIT,#onMenuExit);
  connectMenu(#frame,wxID_ABOUT,#onMenuAbout);
  connectMenu(#frame,ID_KEYS,#onMenuKeys);
  connectMenu(#frame,ID_TESTGETBM,#onMenuTestGetBM);

  statusBar1.wxStatusBar(#frame,0,0);
  statusBar1.SetFieldsCount(1,0);
  statusBar1.SetStatusText("Ready",0);
  frame.SetStatusBar(#statusBar1);

//  DEBUG_StartConsole();
  frame.Show(TRUE);
}

void onEvent(dword event)
{
  SetFocus(hEdit);
}

void onMenuAbout(dword event)
{
  MessageBox(0,"C--IDE\x0d\x0A(c) 2oo3 by TBD",__DATESTR__,MB_ICONINFORMATION);
}

void onMenuKeys(dword event)
{
  dword hMem;
  int i;

  hMem =  GlobalAlloc(GMEM_ZEROINIT, 256);

  for (i=0;i<no_keys;i++) {
      lstrcat(hMem,keys_table[i].Desc);
      lstrcat(hMem,"\x0d\x0a");
  }

  MessageBox(0,hMem,"Keyboard shorcuts",MB_ICONINFORMATION);
}

void onNotify(dword event)
{
 dword temp;
 byte buff[20];

 EBX = event;
 EDI = EBX.wxEvent.lParam;

 if (EDI.NMHDR.hwndFrom != statusBar1.hWnd)
 {
  nLine = EDI._RASELCHANGE.line;
  wsprintf(#buff,"Line:%d Col:%d",nLine + 1,EDI._RASELCHANGE.chrg.cpMin - EDI._RASELCHANGE.cpLine + 1);
  statusBar1.SetStatusText(#buff,0);
   // check EN_SELCHANGE
   if (EDI.NMHDR.code == EN_SELCHANGE)
   {
      if (EDI._RASELCHANGE.seltyp == SEL_OBJECT)
      {
         SendMessage(hEdit,REM_GETBOOKMARK,EDI._RASELCHANGE.line,0);
         switch (EAX)
         {
           case 1:
                SendMessage(hEdit,REM_COLLAPSE,EDI._RASELCHANGE.line,#blocks_def);
           break;
           case 2:
                SendMessage(hEdit,REM_EXPAND,EDI._RASELCHANGE.line,0);
           break;
         }
      }
   }
 }
}

void onMenuExit(dword event)
{
  SendMessage(hEdit,EM_GETMODIFY,0,0);
  if (EAX)
  {
     MessageBox(0,"File unsaved.\nDo you want to exit ?","Confirmation",MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
     if (EAX == IDYES) delete frame;
  }
  else
     delete frame;
}

void onSize (dword event)
{
  RECT rc;

  GetWindowRect(statusBar1.hWnd, #rc);
  EBX = rc.bottom - rc.top;
  SetWindowPos(hEdit,HWND_TOP, 0, 0, frame.GetClientSizeX() - 150,frame.GetClientSizeY() - EBX, SWP_NOREDRAW);
  SetWindowPos(hProcList,HWND_TOP, frame.GetClientSizeX() - 150, 0,
               frame.GetClientSizeX(),frame.GetClientSizeY() - EBX, SWP_NOREDRAW);
}

void onCommand(dword event)
{
//   EBX = event;
//   switch(EBX.wxEvent.GetId() & 0xFF)
//   {
//      // button expand all
//      case 0xFD:
//         SendMessage(hEdit,REM_EXPANDALL,0,0);
//         SendMessage(hEdit,EM_SCROLLCARET,0,0);
//         break;
//      // button collapse all
//      case 0xFC:
//         SendMessage(hEdit,REM_COLLAPSEALL,0,#blocks_def);
//         SendMessage(hEdit,EM_SCROLLCARET,0,0);
//         break;
//   }
}

void onMenuOpen(dword event)
{
  wxList list;

  fileDialog.wxFileDialog(NULL,0,0,
      "*.c--","C-- (*.c--)|*.c--|C (*.c)|*.c|All files (*.*)|*.*",
      wxOPEN | wxMULTIPLE | wxHIDE_READONLY);
  if(fileDialog.ShowModal())
  {
     LoadFile(fileDialog.GetPath());
  }

}

void onDrop(dword event)
{
   byte buffer[256];

   EBX = event;
   DragQueryFile(EBX.wxEvent.wParam,0,#buffer,sizeof(buffer));
   LoadFile(#buffer);
}

void onMenuSave(dword event)
{
   SaveFile(#szOpenFile);
}

void onMenuRun(dword event)
{
  char buff[256];
  byte i;

  STARTUPINFO startup = 0;
  PROCESS_INFORMATION process = 0;
  lstrcpy(#buff,#szOpenFile);
  lstrlen(#szOpenFile);
  lstrcpy(#buff[EAX - 3],"exe");
  CreateProcess(0, #buff, 0, 0, 0, 0, 0, 0, #startup, #process);
}

void onMenuCompile(dword event)
{
  STARTUPINFO startup = 0;
  PROCESS_INFORMATION process = 0;
  dword xc;
  char buff[256];
  char file_buff[1024];

  if (SendMessage(hEdit, EM_GETMODIFY, 0, 0))
  {
     statusBar1.SetStatusText("Saving ...",0);
     SaveFile(#szOpenFile);
  }
  statusBar1.SetStatusText("Compiling ...",0);
  wsprintf(#buff, "\\c--\\c-- %s", #szOpenFile);
  startup.dwFlags = STARTF_USESHOWWINDOW;
  startup.wShowWindow = SW_HIDE;
  CreateProcess(0, #buff, 0, 0, 0, 0, 0, 0, #startup, #process);
loop_process:
   GetExitCodeProcess(process.hProcess,#xc);
   switch (xc)
  {
     case 0:
           statusBar1.SetStatusText("Running ...",0);
           onMenuRun(0);
           break;
     case 9:
           statusBar1.SetStatusText("ERROR(9)::access denied",0);
           break;
     case 0x103:
           goto loop_process; // process is active
           break;
     default:
           statusBar1.SetStatusText("ERROR::unknown error",0);
             lstrcpy(#buff,#szOpenFile);
             lstrlen(#szOpenFile);
             EBX = EAX;
             ECX = 0;
             ESI = #buff;
        _find_dot:
             lodsb
             cmp al, 0x2e  // "."
             je _found_dot
             inc ecx
             cmp ecx, ebx
             jnge _find_dot
         _found_dot:
             lstrcpy(#buff[ECX],".err");
             xc =_lopen(#buff,OF_READ);
             _lread(xc,#file_buff,1024);
             file_buff[EAX] = 0;
             _lclose(xc);
             DeleteFile(#buff);
             MessageBox(0,#file_buff,0,0);
           break;
  }
}

dword StreamInProc(dword hFileIn, dword pBuffer, dword NumBytes, dword pBytesRead)
{
  ReadFile(hFileIn, pBuffer, NumBytes, pBytesRead, 0);
  EAX = 0;
}

dword StreamOutProc (dword hFile,dword pBuffer;dword NumBytes;dword pBytesWritten)
{
  WriteFile(hFile,pBuffer,NumBytes,pBytesWritten,0);
  EAX = 0;
}

void LoadFile(dword hFile)
{
  _EDITSTREAM editstream;

  lstrcpy(#szOpenFile,hFile);
  SendMessage(hEdit,WM_SETTEXT,0,0);
  hFile = CreateFile(hFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  editstream.dwCookie = EAX;
  editstream.pfnCallback = #StreamInProc;
  SendMessage(hEdit,EM_STREAMIN,SF_TEXT,#editstream);
  // --- set highlight scheme
  SendMessage(hEdit,REM_SETBLOCKS,0,#blocks_def);
  CloseHandle(hFile);
  SendMessage(hEdit,EM_SETMODIFY,0,0);
  SetWindowText(frame.hWnd, #szOpenFile);
  onMenuTestGetBM(0);
}

void SaveFile(dword FileName)
{
  dword hFile;
  _EDITSTREAM editstream;

  hFile = CreateFile(FileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
  if (hFile != NULL)
  {
     editstream.dwCookie = EAX;
     editstream.pfnCallback = #StreamOutProc;
     SendMessage(hEdit,EM_STREAMOUT,SF_TEXT,#editstream);
     CloseHandle(hFile);
     SendMessage(hEdit,EM_SETMODIFY, 0, 0);
  }
  else
  {
     MessageBox(0,"Cannot save file","Error",MB_ICONWARNING);
  }

}

dword FrameProc(dword hWnd; dword uMsg; dword wParam; dword lParam)
{
  if (uMsg == WM_CTLCOLORLISTBOX)
  {
     // set ProcList background & text colors
     SetTextColor(wParam, 0);
     SetBkColor(wParam,0xC0F0F0);
     EAX = CreateSolidBrush(0xC0F0F0);
  }
  else CallWindowProc(FrameOldProc, hWnd, uMsg, wParam, lParam);
}

dword ListProc(dword hWnd; dword uMsg; dword wParam; dword lParam)
{
  byte buff[128];

  if (uMsg == WM_LBUTTONUP)
  {
     SendMessage(hProcList,LB_GETCURSEL,0,0);
     EDX = EAX;
     SendMessage(hProcList,LB_GETTEXT,EDX,#buff);
     find.lpstrText = #buff;
     find.chrg.cpMin = 0;
     find.chrg.cpMax = -1;
   _search_bm:
     SendMessage(hEdit,EM_FINDTEXTEX,FR_MATCHCASE | FR_WHOLEWORD | FR_DOWN,#find);
     if (EAX != -1)
     {
        SendMessage(hEdit,EM_LINEFROMCHAR,find.chrgText.cpMin,0);
        SendMessage(hEdit,REM_GETBOOKMARK,EAX + 1,0);
        if ((EAX == 1) || (EAX == 2))
        {
           SendMessage(hEdit,EM_EXSETSEL,0,#find.chrgText);
           SendMessage(hEdit,REM_VCENTER,0,0);
           SetFocus(hEdit);
        }
        else {find.chrg.cpMin = find.chrgText.cpMin + 1; jmp _search_bm;}
     }
  }
  CallWindowProc(ListOldProc, hWnd, uMsg, wParam, lParam);
}

dword RAEditProc(dword hWnd; dword uMsg; dword wParam; dword lParam)
{
   dword counter;

   if (uMsg == WM_KEYDOWN)
   {
      for (counter = 0; counter < no_keys; counter++)
        {
          if ( (keys_table[counter].KeyCode == wParam) &&
               (keys_table[counter].Control == GetKeyState(VK_CONTROL) & 0x80 >> 7) &&
               (keys_table[counter].Shift == GetKeyState(VK_SHIFT) & 0x80 >> 7)
              )
             {
                push keys_table[counter].Code
                call keys_table[counter].Action
             }
        }
      }

   CallWindowProc(RAOldProc, hWnd, uMsg, wParam, lParam);
}

void help_file(dword unused)
{
   char buff[100];

   SendMessage(hEdit, REM_GETWORD, 100, #buff);
   WinHelp(frame.hWnd,"\\masm32\\help\\win32.hlp",HELP_KEY,#buff);
}

void expand_all(dword unused)
{
 if (expand_true) SendMessage(hEdit,REM_COLLAPSEALL,0,#blocks_def);
 else SendMessage(hEdit,REM_EXPANDALL,0,0);
 SendMessage(hEdit,EM_SCROLLCARET,0,0);
 xor expand_true, 1;
}

void put_line(dword unused)
{
 SendMessage(hEdit,REM_SETDIVIDERLINE, nLine, 1);
}

void del_line(dword unused)
{
 SendMessage(hEdit,REM_SETDIVIDERLINE, nLine, 0);
}

void onMenuTestGetBM (dword event)
{
 dword nLine = 0, nWritten, i, nLength;
 byte buff[128];
 byte line[256];

 SendMessage(hProcList,LB_RESETCONTENT,0,#line);
loop_bm:
 nLine = SendMessage(hEdit,REM_NXTBOOKMARK,nLine,1);
 if (EAX != -1)
 {
     nLength = SendMessage(hEdit,EM_GETLINE,nLine - 1,#line);
     if (EAX != 0)
     {
       for(i=0;i<nLength;i++) 
       {
          if (line[i] == 0x20) jmp _skip_space;
       }
    _skip_space:
       nWritten = i + 1;
       for(;i<nLength;i++)
       {
          if (line[i] == 0x28) {mov buff[i],0 jmp _search_over}
          buff[i] = line[i];
       }
    _search_over:
       SendMessage(hProcList,LB_ADDSTRING,0,#buff[nWritten]);
     }
     jmp loop_bm
 }
 SendMessage(hProcList,LB_SETCURSEL ,0,0);
}

void DEBUG_StartConsole()
{
  GetProcAddress(GetModuleHandle("kernel32.dll"),"AllocConsole");
  call eax
  hDebug = GetStdHandle(STD_OUTPUT_HANDLE);
}

void DEBUG_Write(dword format; dword string)
{
  byte buff[128];
  dword nWritten;

  pusha
  wsprintf(#buff, format, string);
  WriteFile(hDebug, #buff, lstrlen(#buff),#nWritten, 0);
  popa
}

