#include <iomanip>
#include <sstream>
#include "windows.h"
#include "commctrl.h"
#include "ifschooserwindow.hpp"
#include "ifs.hpp"

#include "debug.hpp"

using namespace std;

TIFSChooserWindow::TIFSChooserWindow(HINSTANCE xAppInst, int ShowCommand, char *Title)
{
 //Set up class globals
 AppInst = xAppInst;
 IFS = new TIFS();
 ReadOnlyIFS = new TReadOnlyIFS(IFS);
 ItemsInList = 0;
 IFSEntryWindowMode = MODE_ADD;
 SetupTriangle(); //For now...

 //Construct the windows
 ConstructIFSChooserWindow(Title);
 ConstructIFSEntryWindow();

 //Show and paint the window
 ShowWindow(Handle, ShowCommand);
 UpdateWindow(Handle);
}

TIFSChooserWindow::~TIFSChooserWindow()
{
 DestroyIFSEntryWindow();
 DestroyIFSChooserWindow();
 delete ReadOnlyIFS;
 delete IFS;
}

void TIFSChooserWindow::ConstructIFSChooserWindow(char *Title)
{
 Handle = CreateIFSChooserWindow(Title);

 //Device Context for the whole window
 DC = GetDC(Handle);

 //Events for the buttons
 MergePaintEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 RePaintEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 //Create the buttons
 MergePaintButton = CreateIFSChooserWindowButton("Merge Paint!", CHILD_MERGEPAINTBUTTON, 10, 400, 100);
 RePaintButton = CreateIFSChooserWindowButton("Repaint!", CHILD_REPAINTBUTTON, 120, 400, 100);
 AddButton = CreateIFSChooserWindowButton("Add", CHILD_ADDBUTTON, 10, 220, 50);
 EditButton = CreateIFSChooserWindowButton("Edit", CHILD_EDITBUTTON, 90, 220, 50);
 DeleteButton = CreateIFSChooserWindowButton("Delete", CHILD_DELETEBUTTON, 170, 220, 50);
 LoadIFSButton = CreateIFSChooserWindowButton("Load IFS", CHILD_LOADIFSBUTTON, 10, 260, 100);
 SaveIFSButton = CreateIFSChooserWindowButton("Save IFS", CHILD_SAVEIFSBUTTON, 120, 260, 100);

 //Create the progress bar
 DrawingProgressBar = CreateDrawingProgressBar();
 SendMessage(DrawingProgressBar, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0);

 //Create the listbox
 FunctionsListView = CreateFunctionsListView();

 //Set up the columns
 CreateListViewColumns();

 //For now, set up some initial entries.
 AddFunctionToList("1", "Top Triangle", "0.34");
 AddFunctionToList("2", "Left Triangle", "0.33");
 AddFunctionToList("3", "Right Triangle", "0.33");

 return;
}

LRESULT CALLBACK TIFSChooserWindow::WndProc(HWND hwnd, UINT MsgIdent, WPARAM wParam, LPARAM lParam)
{
 switch(MsgIdent)
 {
  case WM_COMMAND:
  if (HIWORD(wParam) == BN_CLICKED)
  {
   if (LOWORD(wParam) == CHILD_MERGEPAINTBUTTON)
   {
    SendMessage(DrawingProgressBar, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0);
    SetEvent(MergePaintEvent);
    while (*ProgressPtr != 100.0)
    {
     SendMessage(DrawingProgressBar, PBM_SETPOS, (WPARAM) (*ProgressPtr), (LPARAM) 0);
    }
   }
   if (LOWORD(wParam) == CHILD_REPAINTBUTTON)
   {
    SendMessage(DrawingProgressBar, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0);
    SetEvent(RePaintEvent);
    while (*ProgressPtr != 100.0)
    {
     SendMessage(DrawingProgressBar, PBM_SETPOS, (WPARAM) (*ProgressPtr), (LPARAM) 0);
    }
   }
   if (LOWORD(wParam) == CHILD_ADDBUTTON)
   {
    EnableWindow(Handle, FALSE);
    IFSEntryWindowMode = MODE_ADD;
    ShowWindow(IFSEntryWindow, SW_SHOW);
    UpdateWindow(IFSEntryWindow);
   }
   if (LOWORD(wParam) == CHILD_EDITBUTTON)
   {
    EnableWindow(Handle, FALSE);
    IFSEntryWindowMode = MODE_EDIT;
    ShowWindow(IFSEntryWindow, SW_SHOW);
    UpdateWindow(IFSEntryWindow);
   }
   if (LOWORD(wParam) == CHILD_DELETEBUTTON)
   {
    if (MessageBox(Handle, "Really delete this function from the IFS?", "Deletion Confirmation", MB_YESNO) == IDYES)
    {
     DeleteSelectedFunction();
    }
   }
  }
  return 0;
  case WM_NOTIFY:
  if (((NMHDR *) lParam)->code == NM_CLICK)
  {
   if (((NMHDR *) lParam)->idFrom == CHILD_FUNCTIONSLISTVIEW)
   {
    ChangeFunctionSelected();
   }
  }
  else if (((NMHDR *) lParam)->code == NM_DBLCLK)
  {
   if (((NMHDR *) lParam)->idFrom == CHILD_FUNCTIONSLISTVIEW)
   {
    ChangeFunctionSelected();
   }
  }
  return 0;
  case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
  default:
  return DefWindowProc(hwnd, MsgIdent, wParam, lParam);
 }

 return 0;
}

LRESULT CALLBACK TIFSChooserWindow::GlobalWndProc(HWND hwnd, UINT MsgIdent, WPARAM wParam, LPARAM lParam)
{
 TIFSChooserWindow *self;

 switch(MsgIdent)
 {
  case WM_CREATE:
  self = (TIFSChooserWindow *) ((CREATESTRUCT *) lParam)->lpCreateParams;
  SetWindowLongPtr(hwnd, GWLP_USERDATA, ((LONG_PTR) self));
  return self->WndProc(hwnd, MsgIdent, wParam, lParam);
  break;
  default:
  self = (TIFSChooserWindow *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  return self->WndProc(hwnd, MsgIdent, wParam, lParam);
  break;
 }

 return 0;
}

void TIFSChooserWindow::SetProgressPtr(int *xProgressPtr)
{
 ProgressPtr = xProgressPtr;
 return;
}

HANDLE TIFSChooserWindow::GetMergePaintEvent()
{
 return MergePaintEvent;
}

HANDLE TIFSChooserWindow::GetRePaintEvent()
{
 return RePaintEvent;
}

HWND TIFSChooserWindow::CreateIFSChooserWindow(char *Title)
{
 WNDCLASSEX IFSChooserClass;

 //Create the window class
 if (!GetClassInfoEx(AppInst, "TIFSChooserWindow", &IFSChooserClass))
 {
  IFSChooserClass.cbSize = sizeof(WNDCLASSEX); //Always set to this
  IFSChooserClass.style = CS_OWNDC; //Class-wide styles.
  IFSChooserClass.lpfnWndProc = &TIFSChooserWindow::GlobalWndProc; //Window Procedure.
  IFSChooserClass.cbClsExtra = 0; //Bytes to allocate for extra class information.
  IFSChooserClass.cbWndExtra = 0; //Bytes to allocate for extra window information.
  IFSChooserClass.hInstance = AppInst; //Instance of application which created the class.
  IFSChooserClass.hIcon = NULL; //Window Icon.
  IFSChooserClass.hCursor = LoadCursor(NULL, IDC_ARROW); //Window Cursor.
  IFSChooserClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND; //Window Background.
  IFSChooserClass.lpszMenuName = NULL; //Name of main menu (if we have one)
  IFSChooserClass.lpszClassName = "TIFSChooserWindow"; //Name of class.
  IFSChooserClass.hIconSm = NULL; //Window Small Icon.

  RegisterClassEx(&IFSChooserClass);
 }

 //Create the window
 return CreateWindowEx(
 WS_EX_CONTEXTHELP, //Window-wide styles (extended).
 "TIFSChooserWindow", //Class name.
 Title, //Title of window.
 (WS_CAPTION | WS_SYSMENU | WS_OVERLAPPED), //Window-wide styles.
 CW_USEDEFAULT, //X Position.
 CW_USEDEFAULT, //Y Position.
 240, //Width
 520, //Height
 (HWND) NULL, //Parent Window
 (HMENU) NULL, //Name of main menu.
 AppInst, //Instance of application which created the class.
 this //Extra user data to pass to WM_CREATE.
 );
}

void TIFSChooserWindow::DestroyIFSChooserWindow()
{
 DestroyWindow(DrawingProgressBar);
 DestroyWindow(FunctionsListView);
 DestroyWindow(SaveIFSButton);
 DestroyWindow(LoadIFSButton);
 DestroyWindow(DeleteButton);
 DestroyWindow(EditButton);
 DestroyWindow(AddButton);
 DestroyWindow(RePaintButton);
 DestroyWindow(MergePaintButton);
 ReleaseDC(Handle, DC);
 UnregisterClass("TIFSChooserWindow", AppInst);
}

HWND TIFSChooserWindow::CreateFunctionsListView()
{
 return CreateWindowEx(
 (DWORD) NULL,
 WC_LISTVIEW,
 "Functions List-View",
 (WS_CHILD | WS_VSCROLL | WS_BORDER | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS),
 10,
 10,
 210,
 200,
 Handle,
 (HMENU) CHILD_FUNCTIONSLISTVIEW,
 AppInst,
 (LPVOID) NULL
 );
}

HWND TIFSChooserWindow::CreateDrawingProgressBar()
{
 return CreateWindowEx(
 (DWORD) NULL,
 PROGRESS_CLASS,
 "Drawing Progress",
 (WS_CHILD | WS_BORDER | WS_VISIBLE | PBS_SMOOTH),
 10,
 450,
 210,
 20,
 Handle,
 (HMENU) CHILD_DRAWINGPROGRESSBAR,
 AppInst,
 (LPVOID) NULL
 );
}

HWND TIFSChooserWindow::CreateIFSChooserWindowButton(char *s, int ID, int x, int y, int Width)
{
 return CreateWindowEx(
 (DWORD) NULL,
 "BUTTON",
 s,
 (WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_TEXT),
 x,
 y,
 Width,
 30,
 Handle,
 (HMENU) ID,
 AppInst,
 (LPVOID) NULL
 );
}

void TIFSChooserWindow::CreateListViewColumns()
{
 LVCOLUMN NumberColumn, NameColumn, ProbColumn;

 NumberColumn.mask = (LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH);
 NameColumn.mask = NumberColumn.mask;
 ProbColumn.mask = NumberColumn.mask;

 NumberColumn.fmt = LVCFMT_LEFT;
 NameColumn.fmt = NumberColumn.fmt;
 ProbColumn.fmt = NumberColumn.fmt;

 NumberColumn.iSubItem = 0;
 NameColumn.iSubItem = 1;
 ProbColumn.iSubItem = 2;

 NumberColumn.pszText = "No.";
 NumberColumn.cx = 35;
 NameColumn.pszText = "Name";
 NameColumn.cx = 100;
 ProbColumn.pszText = "Probability";
 ProbColumn.cx = 65;

 SendMessage(FunctionsListView, LVM_INSERTCOLUMN, (WPARAM) NumberColumn.iSubItem, (LPARAM) &NumberColumn);
 SendMessage(FunctionsListView, LVM_INSERTCOLUMN, (WPARAM) NameColumn.iSubItem, (LPARAM) &NameColumn);
 SendMessage(FunctionsListView, LVM_INSERTCOLUMN, (WPARAM) ProbColumn.iSubItem, (LPARAM) &ProbColumn);

 return;
}

void TIFSChooserWindow::AddFunction(char *FunctionTitle, char *sx, char *sy, float p)
{
	stringstream idss;
	stringstream probss;
	char *idstr;
	char *probstr;
	
	idss << (ItemsInList + 1);
	idstr = new char[idss.str().length() + 1];
	idss.str().copy(idstr, idss.str().length(), 0);
	idstr[idss.str().length()] = '\0';
	
	probss << setprecision(3) << p;
	probstr = new char[probss.str().length() + 1];
	probss.str().copy(probstr, probss.str().length(), 0);
	probstr[probss.str().length()] = '\0';
	
	IFS->Add(sx, sy, p);
	AddFunctionToList(idstr, FunctionTitle, probstr);
	
	return;
}

void TIFSChooserWindow::AddFunctionToList(char *NumberString, char *NameString, char *ProbString)
{
 LVITEM Item;

 Item.mask = LVIF_TEXT;

 Item.iItem = ItemsInList;
 Item.iSubItem = 0; //Must be 0.
 Item.pszText = NumberString;

 Item.iItem = SendMessage(FunctionsListView, LVM_INSERTITEM, (WPARAM) 0, (LPARAM) &Item);
 ItemsInList++;

 Item.iSubItem = 1;
 Item.pszText = NameString;
 SendMessage(FunctionsListView, LVM_SETITEMTEXT, (WPARAM) Item.iItem, (LPARAM) &Item);

 Item.iSubItem = 2;
 Item.pszText = ProbString;
 SendMessage(FunctionsListView, LVM_SETITEMTEXT, (WPARAM) Item.iItem, (LPARAM) &Item);

 Item.state = LVIS_SELECTED;
 Item.stateMask = LVIS_SELECTED;
 SendMessage(FunctionsListView, LVM_SETITEMSTATE, (WPARAM) 0, (LPARAM) &Item);

 return;
}

void TIFSChooserWindow::ChangeFunctionSelected()
{
 LVITEM Item;

 if ((Item.iItem = SendMessage(FunctionsListView, LVM_GETNEXTITEM, (WPARAM) -1, (LPARAM) LVNI_SELECTED)) != -1)
 {
  ItemSelected = Item.iItem;
 }
 else
 {
  Item.state = LVIS_SELECTED;
  Item.stateMask = LVIS_SELECTED;
  SendMessage(FunctionsListView, LVM_SETITEMSTATE, (WPARAM) ItemSelected, (LPARAM) &Item);
 }
 return;
}

void TIFSChooserWindow::DeleteSelectedFunction()
{
 register int i;
 LVITEM Item;
 char *buf;

 IFS->Delete(ItemSelected);
 SendMessage(FunctionsListView, LVM_DELETEITEM, (WPARAM) ItemSelected, (LPARAM) 0);
 ItemsInList--;

 for (i = ItemSelected; i < ItemsInList; i++)
 {
  Item.iSubItem = 0;
  buf = new char[2+1];
  sprintf(buf, "%d", i+1);
  Item.pszText = buf;
  SendMessage(FunctionsListView, LVM_SETITEMTEXT, (WPARAM) i, (LPARAM) &Item);
  delete buf;
 }

 return;
}

void TIFSChooserWindow::BringToTop()
{
 SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0, (SWP_NOSIZE | SWP_NOMOVE));
 SetFocus(FunctionsListView);
 return;
}

void TIFSChooserWindow::SetupTriangle()
{
	//Old code for triangle:
 //TAffineTransformation TopTriangle = {0.5, 0, 0, 0.5, 0, 0.5, 33.34};
 //TAffineTransformation LeftTriangle = {0.5, 0, 0, 0.5, -0.5, 0, 33.33};
 //TAffineTransformation RightTriangle = {0.5, 0, 0, 0.5, 0.5, 0, 33.33};

 //Triangle:
 IFS->Add("0.5*x", "0.5*y+0.5", 33.34);
 IFS->Add("0.5*x-0.5", "0.5*y", 33.33);
 IFS->Add("0.5*x+0.5", "0.5*y", 33.33);

 //Barnsley original fern (10x10 grid):
 //IFS->Add("0", "0.16*y", 1);
 //IFS->Add("0.85*x+0.04*y", "0.85*y-0.04*x+1.6", 85);
 //IFS->Add("0.2*x-0.26*y", "0.23*x+0.22*y+1.6", 7);
 //IFS->Add("0.28*y-0.15*x", "0.26*x+0.24*y+0.44", 7);

 //Modified fern (10x10 grid):
 //IFS->Add("0", "0.16*y", 1);
 //IFS->Add("0.85*x+0.04*y", "0.85*y-0.04*x+1.6", 85);
 //IFS->Add("0.2*x-0.26*y", "0.23*x+0.22*y+1.6", 7);
 //IFS->Add("0.28*y-0.15*x", "0.26*x+0.24*y+0.44", 7);

 return;
}
