#include <stdio.h>
#include <fcntl.h>
/***********************************************************
  -WINDOWS INITIALIZATION-
 ***********************************************************/
typedef BOOL (WINAPI* SLWA)(HWND, COLORREF, BYTE, DWORD);
SLWA SetLayeredWindowAttributes;
/***********************************************************
  -WINDOWS MESSSAGES-
 ***********************************************************/
#define CNM_CHANGE 900000
/***********************************************************
  -WINDOWS SYSTEM DIALOG FUNCTIONS-
 ***********************************************************/
BOOL ChooseColorDlg(HWND hwnd,CHOOSECOLOR* chc,DWORD flags=CC_FULLOPEN,COLORREF cf[16]=NULL){
  chc->lStructSize=sizeof(CHOOSECOLOR);
  chc->hwndOwner=hwnd;
  if(cf==NULL){
    COLORREF cf2[16];
    for(int i=0;i<16;i++){
      cf2[i]=0x00FFFFFF;        
    }
    chc->lpCustColors=cf2;
  }
  else{
    chc->lpCustColors=cf;
  }
  chc->Flags=flags;
  return ChooseColor(chc);
}

BOOL OpenFileDlg(OPENFILENAME* ofn,HWND hwnd,char* buff,char* defExt=".txt\0",char* filter="Plain Text (.TXT)\0*.txt\0Rich Text Format (.RTF)\0*.rtf\0All Files\0*.*\0\0",int filterIndex=0){
  ofn->lStructSize=sizeof(OPENFILENAME);
  ofn->hwndOwner=hwnd;
  ofn->hInstance=GetModuleHandle(0);
  ofn->lpstrFilter=filter;
  ofn->lpstrDefExt=defExt;
  ofn->lpstrFile=buff;
  ofn->nMaxFile=256;
  ofn->Flags=OFN_EXPLORER;
  ofn->nFilterIndex=filterIndex;
  return GetOpenFileName(ofn);
}

BOOL SaveFileDlg(OPENFILENAME* ofn,HWND hwnd,char* buff,char* defExt=".txt\0",char* filter="Plain Text (.TXT)\0*.txt\0Rich Text Format (.RTF)\0*.rtf\0All Files\0*.*\0\0",int filterIndex=0){
  ofn->lStructSize=sizeof(OPENFILENAME);
  ofn->hwndOwner=hwnd;
  ofn->hInstance=GetModuleHandle(0);
  ofn->lpstrFilter=filter;
  ofn->lpstrDefExt=defExt;
  ofn->lpstrFile=buff;
  ofn->nMaxFile=256;
  ofn->Flags=OFN_EXPLORER;
  ofn->nFilterIndex=filterIndex;
  return GetSaveFileName(ofn);
}
/***********************************************************
  -WINDOWS PROGRAM DEBUGGING FUNCTIONS-
 ***********************************************************/
#ifndef de_error_windowsh
void UseConsole(){
  int hCrt;
  FILE *hf;
  AllocConsole();
  hCrt=_open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT);
  hf = fdopen(hCrt,"w");
  *stdout=*hf;
  setvbuf(stdout,NULL,_IONBF,0);
}

void DebugError(){
  LPVOID lpMsgBuf;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL);
  std::cout<<"ERROR: "<<(const CHAR*)lpMsgBuf<<std::endl;
  LocalFree( lpMsgBuf );     
}
#endif

#define GetWindowTitle(hwnd,cVar)         \
  char cVar[GetWindowTextLength(hwnd)+1];   \
GetWindowText(hwnd,cVar,GetWindowTextLength(hwnd)+1);

#define GetWindowClassName(hwnd,cVar)     \
  char cVar[257];                           \
GetClassName(hwnd,cVar,256);

#ifndef de_error_windowsh
void itest(int i,HWND hwnd=0){
  char integer[10];
  itoa(i,integer,10);
  MessageBox(hwnd,integer,"Integer Testing",MB_OK);
}
#endif
/***********************************************************
  -WINDOW CREATION AND MANIPULATION-
 ***********************************************************/
class WINCLASSEX{
  public:

    WNDCLASSEX wc;
    WINCLASSEX(HINSTANCE hinst,WNDPROC wproc,LPSTR cls,int cx=0,int wx=0){
      this->wc.lpfnWndProc=wproc;        
      this->wc.cbClsExtra=cx;
      this->wc.cbSize=sizeof(WNDCLASSEX);
      this->wc.cbWndExtra=wx;

      this->wc.lpszMenuName=0;
      this->wc.lpszClassName=cls;
      this->wc.style=CS_DBLCLKS;
      this->wc.hInstance=hinst;

      this->wc.hbrBackground=(HBRUSH)COLOR_BACKGROUND;
      this->wc.hIcon=LoadIcon(0,IDI_APPLICATION);
      this->wc.hIconSm=LoadIcon(0,IDI_APPLICATION);
      this->wc.hCursor=LoadCursor(0,IDC_ARROW);
    }
    WNDCLASSEX GetStruct(){
      return this->wc;
    }
    void Register(){
      if(!RegisterClassEx(&this->wc)){
        MessageBox(0,"The specified window could not be registered",this->wc.lpszClassName,MB_OK);
      }
    }
};

void alert(char* msg="",char* title="Alert!"){
  MessageBox(0,msg,title,MB_OK);
}

void GetWndCursorPos(HWND hwnd,POINT* pt){
  GetCursorPos(pt);
  ScreenToClient(hwnd,pt);
}

void SetClipboard(UINT format,HANDLE obj,HWND hwnd=0){
  OpenClipboard(hwnd);
  EmptyClipboard();
  SetClipboardData(format,obj);
  CloseClipboard();
}

HANDLE GetClipboard(HWND hwnd,UINT format){
  OpenClipboard(hwnd);
  HANDLE rv=GetClipboardData(format);
  CloseClipboard();
  return rv;
}

void SetWindowAlpha(HWND hwnd,DWORD alpha){
  HMODULE u32dll=GetModuleHandle("USER32.DLL");
  SetLayeredWindowAttributes=(SLWA)GetProcAddress(u32dll, "SetLayeredWindowAttributes");
  if(SetLayeredWindowAttributes){
    SetLayeredWindowAttributes(hwnd,0,alpha,0x02);
  }
  FreeLibrary(u32dll);
}

#define StructToUserdata(HW,PTR) SetWindowLong(HW,GWL_USERDATA,(LONG)PTR);

#define UserdataToStruct(HW,TYPE,VARNAME) TYPE* VARNAME=new TYPE;\
                                                        VARNAME=(TYPE*)GetWindowLong(HW,GWL_USERDATA);

#define GetWndClientRect(hwnd,rect) RECT rect;GetWindowRect(hwnd,&rect);

#define AddMenuItem(menu,txt,id){\
  AppendMenu(menu,MF_STRING,id,txt);\      
}

#define TrackMenu(hwnd,menu){\
  POINT pt;\
  GetWndCursorPos(hwnd,&pt);\
  TrackPopupMenu(menu,TPM_LEFTALIGN,pt.x,pt.y,0,hwnd,NULL);\    
}


/***********************************************************
  -WINDOWS GDI-
 ***********************************************************/

COLORREF rgb(COLORREF cr){
  return ((DWORD) (((BYTE) (GetBValue(cr)) |  ((WORD) (GetGValue(cr)) << 8)) |  (((DWORD) (BYTE) (GetRValue(cr))) << 16)));
}

COLORREF rgb(short r,short g,short b){
  return rgb(RGB(r,g,b));
}

HBITMAP RectToPlainBitmap(const RECT* r,COLORREF color=RGB(255,255,255)){
  int w=r->right-r->left;
  int h=r->bottom-r->top;
  COLORREF* map=(COLORREF*)calloc(sizeof(COLORREF),w*h);
  for(int i=0;i<(w*h);i++){
    map[i]=color;
  }
  return CreateBitmap(w,h,1,32,map);
}

HBITMAP RectToBitmap(HDC hdc,const RECT* r){
  int w=r->right-r->left;
  int h=r->bottom-r->top;
  COLORREF* map=(COLORREF*)calloc(sizeof(COLORREF),w*h);
  int c=0;
  for(int y=r->top;y<r->bottom;y++){
    for(int x=r->left;x<r->right;x++){
      map[c]=GetPixel(hdc,x,y);
      c++;
    }      
  }
  return CreateBitmap(w,h,1,32,map);
}


HBITMAP WindowToBitmap(HWND hwnd){
  HDC hdcWnd,hdcBitmap;
  int w=100,h=100;
  HBITMAP hbmBitmap;
  HBITMAP hbmOld;
  hdcWnd=GetDC(hwnd);
  if(hdcWnd){
    RECT wnd;
    GetClientRect(hwnd,&wnd);
    w=wnd.right; 
    h=wnd.bottom;   
    hbmBitmap=(HBITMAP)CreateCompatibleBitmap(hdcWnd,w,h);
    if(!hbmBitmap){
      if(hdcWnd){
        RECT wnd;
        GetWindowRect(hwnd,&wnd);
        w=wnd.right; 
        h=wnd.bottom;   
        hbmBitmap=CreateCompatibleBitmap(hdcWnd,w,h);                   
      }             
    }
    if(!hbmBitmap){
      std::cout<<"Bitmap missing! "<<std::endl;
    }
    hdcBitmap=CreateCompatibleDC(hdcWnd);  
    hbmOld=(HBITMAP)SelectObject((HDC)hdcBitmap,(HGDIOBJ)(HBITMAP)hbmBitmap);  
    BitBlt(hdcBitmap,0,0,w,h,hdcWnd,0,0,SRCCOPY);
    ReleaseDC(hwnd,hdcWnd);      
    DeleteDC(hdcBitmap);            
  }          
  ReleaseDC(hwnd,hdcWnd);      
  return hbmBitmap;
} 


//Color=Transparent color [For windows that don't want to be rectangles]
HRGN BitmapToRegion(HBITMAP hbm,COLORREF color=RGB(255,255,255)){
  HDC hdc=CreateCompatibleDC(NULL);
  BITMAP bm;
  SelectObject(hdc,hbm);
  GetObject(hbm,sizeof(bm),&bm);
  BeginPath(hdc);  
  MoveToEx(hdc,0,0,NULL);
  for(int y=0;y<bm.bmHeight;y++){
    for(int x=0;x<bm.bmWidth;x++){
      COLORREF clr=GetPixel(hdc,x,y);
      if(clr!=color && x>0){
        LineTo(hdc,x,y);
      }  
      else{     
        MoveToEx(hdc,x+1,y,NULL); 
      }   
    }
  }
  EndPath(hdc);
  WidenPath(hdc);
  HRGN rgn=PathToRegion(hdc);  
  DeleteDC(hdc);
  return rgn;     
}




HBITMAP __fastcall AreaToBitmap(int x,int y,int w,int h){
  HDC hdc=GetWindowDC(HWND_DESKTOP);
  HBITMAP hbm;
  COLORREF map[w*h];
  x+=1;
  y-=1;
  w-=1;
  h-=1;
  int tempx=x;
  int tempy=y;
  for(int i=0;i<w*h+1;i++){
    if(x==w){
      y++;   
      x=tempx;       
    }
    if(y==h){
      return CreateBitmap(w-x,h-tempy,1,32,map);
    }
    map[i]=rgb(GetPixel(hdc,x,y));
    x++;         
  }
  return CreateBitmap(w,h,1,32,map);
}

void DrawRect(HDC hdc,const RECT* rect){
  MoveToEx(hdc,rect->left,rect->top,NULL);
  LineTo(hdc,rect->right,rect->top);
  MoveToEx(hdc,rect->right,rect->top,NULL);
  LineTo(hdc,rect->right,rect->bottom);
  MoveToEx(hdc,rect->right,rect->bottom,NULL);
  LineTo(hdc,rect->left,rect->bottom);
  MoveToEx(hdc,rect->left,rect->bottom,NULL);
  LineTo(hdc,rect->left,rect->top);
}



void DrawEllipse(HDC hdc,const RECT* rect){
  Arc(hdc,rect->left,rect->top,rect->right,rect->bottom,0,0,0,0);
}

void RefreshWindow(HWND hwnd,UINT flags=RDW_ERASE|RDW_INVALIDATE,HRGN hrgn=0){
  RECT client;
  GetClientRect(hwnd,&client);
  UpdateWindow(hwnd);                   
  RedrawWindow(hwnd,&client,hrgn,flags);
}


int GetLBSelText(HWND hwnd,char* buff=NULL){
  int index=SendMessage(hwnd,LB_GETCURSEL,0,0);
  int len=SendMessage(hwnd,LB_GETTEXTLEN,index,0);
  if(buff!=NULL){
    SendMessage(hwnd,LB_GETTEXT,index,LPARAM(buff));               
  }
  return len;
}

int AddLBString(HWND hwnd,char* str){
  SendMessage(hwnd,LB_ADDSTRING,0,LPARAM(str));
}

/***********************************************************
  -WINDOW ENUMERATION-
 ***********************************************************/
namespace WINENUMDATA{
  HWND hwnd;
  int currentIndex;
};

BOOL WINENUM__EnumChildWindowsProc(HWND hwndIndex,LPARAM lParam){
  if(hwndIndex){
    if(lParam==WINENUMDATA::currentIndex){
      WINENUMDATA::hwnd=hwndIndex;
      return FALSE;
    }
    WINENUMDATA::currentIndex++;
    return TRUE;  
  }
  else{
    WINENUMDATA::hwnd=(HWND)NULL;
    return FALSE;     
  }
}

BOOL WINENUM__CountChildWindowsProc(HWND hwndIndex,LPARAM lParam){
  if(hwndIndex){
    WINENUMDATA::currentIndex++;
    return TRUE;  
  }
  else{
    return FALSE;     
  }
}

class WINENUM{
  private:
    void ClearData(){
      WINENUMDATA::currentIndex=0;
      WINENUMDATA::hwnd=(HWND)NULL;       
    }
  public: 
    WINENUM(){};
    HWND ChildWindow(HWND hwndOwner,int index){
      this->ClearData(); 
      EnumChildWindows(hwndOwner,(WNDENUMPROC)WINENUM__EnumChildWindowsProc,index);
      return WINENUMDATA::hwnd;
    }
    int CountChildWindows(HWND hwndOwner){
      this->ClearData(); 
      EnumChildWindows(hwndOwner,(WNDENUMPROC)WINENUM__CountChildWindowsProc,0);
      return WINENUMDATA::currentIndex;  
    }
};