#include "diffractioncontrol.h"
#include "paintcanvas.h"
#include "winexception.h"
#include "resstring.h"
#include "utils.h"
#include "config.h"
#include "ressources.h"
#include <string>

#define ABS(a) (arg = (a), arg > 0 ? arg : -arg)

DiffractionControl::DiffractionControl(HWND hwnd, int id, int IDmap): 
    SimpleControl(hwnd, id), _cmap(IDmap),
    _model(NULL), fileOpen(FALSE), fileSaved(FALSE),
    alreadyDiffract(FALSE), lastFileOpen(""), 
    _params(NULL)
{
    _bmp = new CRaster;
    
    if(!_bmp)
      throw WinException(ResString(ERROR_MEMORY_ALLOC));
    
    try
      {
      _params = new Parameters(std::string(ResString(DEFAULT_PARAM_NAME)));
      
      if(!_params)
        throw WinException(ResString(ERROR_MEMORY_ALLOC));
      }
    catch(WinException e)
      {
      if(e.GetErrorParam() == ERROR_NO_FILE_PARAMS || e.GetErrorParam() == ERROR_FILE_PARAMS)
        {
        int rep = ::MessageBox(_hwnd, "Le fichier des paramètres est endommagé ou inexistant. Le restaurer ?\n ATTENTION: répondre non à cette question provoquera l'arrêt du programme.",
               "Question", MB_ICONQUESTION | MB_YESNO | MB_SYSTEMMODAL);
               
        if(rep == IDNO) Command(ID_FILE_EXIT);
        else if(rep == IDYES) 
           {
           // Fallback on the default constructor
           _params = new Parameters();
           
           if(!_params)
             throw WinException(ResString(ERROR_MEMORY_ALLOC));
           
           _params->RestoreDefaults();
           }
        }
      else
        {
        ::MessageBox(_hwnd, ResString(ERROR_UNKNOWN_ERROR), "Erreur inconnue", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
        Command(ID_FILE_EXIT);
        }  
      }
}

DiffractionControl::~DiffractionControl()
{
    if(_bmp != NULL)    delete _bmp;
    if(_model != NULL)  delete _model;
    if(_params != NULL) delete _params;
}

DiffractionControl::operator HWND() const
{
    return _hwnd;
}

void DiffractionControl::Size(int x, int y)
{
    _view.SetSize(x, y);
}

void DiffractionControl::Paint()
{
    // Prepare the canvas and let View do the rest
    // Notice: destructor of PaintCanvas called automatically !
    PaintCanvas canvas(_hwnd);
    if(_model != NULL) _view.Paint(canvas, (*_model));
}

// Menu command processing
void DiffractionControl::Command(int cmd)
{
    switch(cmd)
       {
       case ID_FILE_OPEN: OpenFileProc(); break;
       case ID_FILE_SAVEAS: SaveAsFileProc(); break;
       case ID_FILE_EXPORT: ExportFileProc(); break;
       case ID_FILE_CLOSE: CloseFileProc(); break;
       case ID_DIFFRACT_DIFFRACT: DiffractionProc(); break;
       case ID_DIFFRACT_RELOAD: ReloadFileProc(); break;
       case ID_DIFFRACT_DEFPARAMS: _params->RestoreDefaults(); break;
       case ID_COLORMAP_PINK: SetMapProc(ID_MAP_PINK); break;
       case ID_COLORMAP_HOT: SetMapProc(ID_MAP_HOT); break;
       case ID_COLORMAP_JET: SetMapProc(ID_MAP_JET); break;
       case ID_COLORMAP_HSV: SetMapProc(ID_MAP_HSV); break;
       case ID_COLORMAP_GRAY: SetMapProc(ID_MAP_GRAY); break;
       case ID_COLORMAP_GRAYINV: SetMapProc(ID_MAP_GRAYINV); break;
       
       case ID_ABOUT: {
            HINSTANCE hInst = (HINSTANCE) ::GetModuleHandle(0);
            ResString text(hInst, ID_ABOUT_TEXT);
            ResString title(hInst, ID_ABOUT_TITLE);
            ::MessageBox(_hwnd, text, title, MB_ICONINFORMATION | MB_OK | MB_SYSTEMMODAL);
            }
       break;
       
       case ID_FILE_EXIT: ::PostMessage(_hwnd, WM_CLOSE, 0, 0); break;
       case WM_CLOSE: {
                int rep = CloseProc(); 
                if(rep == SUCCESS) ::PostQuitMessage(0);
                }
       break;
       default:
            throw WinException(ResString(ERROR_UNKNOWN_COMMAND));
       }
}

//
// Private functions
//

// Open a bitmap file
BOOL DiffractionControl::OpenFile()
{
    static int arg;
    OPENFILENAME ofn;
    char szFileName[MAX_PATH];
    
    ZeroMemory(&ofn, sizeof(ofn));
    szFileName[0] = 0;
    
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = _hwnd;
    ofn.lpstrFilter = "Fichier Bitmap (*.bmp)\0*.bmp\0\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrDefExt = "bmp";
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
    
    if(GetOpenFileName(&ofn))
      {
      if(_bmp->LoadBMP(_hwnd, szFileName) != SUCCESS)
        throw WinException(ResString(ERROR_READING_FILE));
        
      // Resizing the file to the requested size
      if(!IsPowerTwo(ABS(_bmp->Height)) || !IsPowerTwo(ABS(_bmp->Width)))  
        {
        int nearestHeight = NearestPowerTwoSup(ABS(_bmp->Height));
        int nearestWidth  = NearestPowerTwoSup(ABS(_bmp->Width));
        _bmp->ResizeBMP(nearestWidth, nearestHeight);
        }
        
      // Copy the name of the last open file
      lastFileOpen = szFileName;
      }
    else return FALSE;
    
    return TRUE;  
}

// Save BMP file
BOOL DiffractionControl::SaveFile()
{
    OPENFILENAME ofn;
    char szFileName[MAX_PATH];
    
    ZeroMemory(&ofn, sizeof(ofn));
    szFileName[0] = 0;
    
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = _hwnd;
    ofn.lpstrFilter = "Fichier Bitmap (*.bmp)\0*.bmp\0\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrDefExt = "bmp";
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
    
    if(GetSaveFileName(&ofn))
      {
      if(_bmp->SaveBMP(_hwnd, szFileName) != SUCCESS)
        throw WinException(ResString(ERROR_SAVING_FILE));
        
      return TRUE;
      }  
    else return FALSE;
    
    return TRUE;
}

// Export data to text file
BOOL DiffractionControl::ExportFile() const
{
    OPENFILENAME ofn;
    char szFileName[MAX_PATH];
    
    ZeroMemory(&ofn, sizeof(ofn));
    szFileName[0] = 0;
    
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = _hwnd;
    ofn.lpstrFilter = "Fichier text (*.txt)\0*.txt\0\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrDefExt = "txt";
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
    
    if(GetSaveFileName(&ofn))
      {
      if((_model->Export(szFileName)) != SUCCESS)
        throw WinException(ResString(ERROR_EXPORTING_DATA));
            
      return TRUE;
      }
    else return FALSE;  
      
return TRUE;      
}

// Open file procedure
void DiffractionControl::OpenFileProc()
{
    if(alreadyDiffract == TRUE && fileSaved == FALSE)
      {
      int rep = ::MessageBox(_hwnd, "L'image par diffraction n'a pas été sauvegardée. L'enregistrer ?", 
                "Question", MB_ICONQUESTION | MB_YESNOCANCEL | MB_SYSTEMMODAL);
      
      switch(rep)
         {
         case IDYES: fileSaved = SaveFile();
         break;
         
         case IDCANCEL: return;
         break;
         }          
      }
      
    // If other file open previously, erase its image
    // in the program and free memory
    if(fileOpen == TRUE)
      {
      _view.Erase((*_model));
      _bmp->EraseBMP();
      fileOpen = alreadyDiffract = fileSaved = FALSE;
      }
    
    if(OpenFile() == TRUE)
      {
      fileOpen = TRUE;
      fileSaved = alreadyDiffract = FALSE;
      
      // Free memory
      if(_model != NULL)
        {
        delete _model;
        _model = NULL;
        }
        
      // Show the image
      _model = new Model(_hwnd, _bmp, &_cmap);
      _view.Update((*_model));
      }    
}

// Close file procedure
void DiffractionControl::CloseFileProc()
{
    if(fileOpen == TRUE)
      {
      if(alreadyDiffract == TRUE && fileSaved == FALSE)
        {
        int rep = ::MessageBox(_hwnd, "L'image par diffraction n'a pas été sauvegardée. L'enregistrer ?",
                 "Question", MB_ICONQUESTION | MB_YESNOCANCEL | MB_SYSTEMMODAL);
        
        switch(rep)
           {
           case IDYES: fileSaved = SaveFile();
           break;
           
           case IDCANCEL: return;
           break;
           }
        }
        
      _view.Erase((*_model));
      _bmp->EraseBMP();
      delete _model; _model = NULL;
      lastFileOpen = "";
      
      fileOpen = alreadyDiffract = fileSaved = FALSE;
      }
}

// Reload file procedure
void DiffractionControl::ReloadFileProc()
{
    static int arg;
    
    if(fileOpen == TRUE)
      {
      // Free Memory
      _bmp->EraseBMP();
      
      if(_bmp->LoadBMP(_hwnd, lastFileOpen.c_str()) != SUCCESS)
        throw WinException(ResString(ERROR_RELOAD_FILE));
      
      // Resizing the file to the requested size
      if(!IsPowerTwo(ABS(_bmp->Height)) || !IsPowerTwo(ABS(_bmp->Width)))
        {
        int nearestHeight = NearestPowerTwoSup(ABS(_bmp->Height));
        int nearestWidth  = NearestPowerTwoSup(ABS(_bmp->Width));
        _bmp->ResizeBMP(nearestWidth, nearestHeight);
        }
        
      _view.Update((*_model));
      alreadyDiffract = fileSaved = FALSE;
      }
    else throw WinException(ResString(ERROR_NO_FILE_OPEN));
}

// Diffraction procedure
int DiffractionControl::DiffractionProc()
{
    if(fileOpen == TRUE)
      {
      if(alreadyDiffract == FALSE)
        {
        _model->DoDiffraction(_params);
        alreadyDiffract = TRUE;
        fileSaved = FALSE;
        _view.Update((*_model));
        }
      else throw WinException(ResString(ERROR_DIFFRACT_DONE));
      }
    else throw WinException(ResString(ERROR_NO_FILE_OPEN));
          
return SUCCESS;      
}

// 'Save as' file procedure
void DiffractionControl::SaveAsFileProc()
{
    if(alreadyDiffract == TRUE) fileSaved = SaveFile();
    else
      {
      if(fileOpen == TRUE)
        {
        int rep = ::MessageBox(_hwnd, "L'image par diffraction n'a pas été calculée. Effectuer le calcul ?",
                    "Question", MB_ICONQUESTION | MB_YESNO | MB_SYSTEMMODAL);
        
        if(rep == IDYES)
          {
          if(DiffractionProc() == SUCCESS)
            {
            _view.Update((*_model));
            fileSaved = SaveFile();
            }
          }
        }
      else throw WinException(ResString(ERROR_NO_FILE_TO_SAVE));
      } 
}

// Export file procedure
void DiffractionControl::ExportFileProc()
{
    if(fileOpen == TRUE)
      {
      if(alreadyDiffract == FALSE)
        {
        int rep = ::MessageBox(_hwnd, "L'image par diffraction n'a pas été calculée. Effectuer le calcul ?",
                  "Question", MB_ICONQUESTION | MB_YESNO | MB_SYSTEMMODAL);
                  
        if(rep == IDNO) return;
        
        if(DiffractionProc() == SUCCESS)
          _view.Update((*_model));
        else return;
        }
        
      ExportFile();  
      }
    else throw WinException(ResString(ERROR_NO_DATA_TO_EXPORT));
}

// Close program procedure
int DiffractionControl::CloseProc()
{
    if(alreadyDiffract == TRUE && fileSaved == FALSE)
      {
      int rep = ::MessageBox(_hwnd, "Sauvegarder l'image par diffraction ?",
                    "Question", MB_ICONQUESTION | MB_YESNO | MB_SYSTEMMODAL);
                    
      switch(rep)
         {
         case IDYES: fileSaved = SaveFile();
         break;
         
         case IDNO: return SUCCESS;
         break;
         }              
      }
      
return SUCCESS;      
}

// Change colormap
void DiffractionControl::SetMapProc(int IDmap)
{
    if(fileOpen == TRUE)
      {
      if(alreadyDiffract == TRUE)
        {
        if(_cmap.GetIDmap() != IDmap)
          {
          _cmap.SetMap(IDmap);

          if(_model != NULL)
            {
            _model->ReloadMap(_params, &_cmap);
            _view.Update((*_model));
            fileSaved = FALSE;
            }
          }
        }        
      }
    else throw WinException(ResString(ERROR_PLEASE_OPEN_FILE));
}
