#include "win32ui\winplus.h"
#include "cppdecl\plugcommon.hpp"
#pragma hdrstop
#include "res\SimpMesh.rh"
#include <stdio.h>
#include <stdlib.h>

#define  DECLARE_PLUGIN(name)\
class name##Plugin : public IPlugIn\
{\
  virtual char* __stdcall MenuName();\
  virtual Boolean __stdcall CanDo();\
  virtual void __stdcall PluginMain();\
} name##;
  

DECLARE_PLUGIN(SimplexGrid)
DECLARE_PLUGIN(SimplexExtremum)

static char findstr[]="Полином Шеффе";

char* __stdcall SimplexGridPlugin::MenuName() 
{ 
  return "Сетка плоского симплекса";
}

char* __stdcall SimplexExtremumPlugin::MenuName() 
{ 
  return "Экстремум плоского симплекса";
}

Boolean __stdcall SimplexGridPlugin::CanDo() 
{ 
  int k;
  IModel *model;
  if(GetMainServer()->CanDo(acExtremum)) {
    GetMainServer()->GetKNR(&k,NULL,NULL);
    if(k==3) {
      model=GetMainServer()->GetCurrentModel();
      if(model && !strncmp(model->Description(),findstr,strlen(findstr)))
        return 1;
    }
  }
  return 0;
}

Boolean __stdcall SimplexExtremumPlugin::CanDo() 
{ 
  int k;
  IModel *model;
  if(GetMainServer()->CanDo(acExtremum)) {
    GetMainServer()->GetKNR(&k,NULL,NULL);
    if(k==3) {
      model=GetMainServer()->GetCurrentModel();
      if(model && !strncmp(model->Description(),findstr,strlen(findstr)))
        return 1;
    }
  }
  return 0;
}

class CGridDialog : public CDialog
{
  CEdit           eSteps, eFile;
public:
  CGridDialog() : CDialog(IDD_GRIDDIALOG,GetForegroundWindow(),GetModuleHandle("SimpMesh.dll")) {}
  virtual LRESULT OnInitDialog(HWND hDlg, HWND hFocus);
  virtual LRESULT OnCommand(HWND hDlg, WPARAM idControl);
};

LRESULT CGridDialog::OnInitDialog(HWND hDlg, HWND hFocus)
{
  eSteps.Init(hDlg,IDC_ESTEPS);
  eFile.Init(hDlg,IDC_EFILE);
  eFile.SetText("РешеткаСимплекса.csv");
  eSteps.SetText("5");
  utCenterWindow(hDlg);
  return 1;
}

static void PerformGridTask(int Steps,char *FileName)
{
  // Основная задача.
  FILE *fout;
  CErrorReport err;
  double c1,c2,c3, step;
  double X[4];
  double res;
  fout=fopen(FileName,"wt");
  if(!fout)
  {
    err.Report("Не удается открыть файл '%s' для записи.",FileName);
    return;
  }
  step=1.0/Steps;
  c1=1;
  while(c1>=0)
  {
    c2=0;
    while((c1+c2)<=1)
    {
      c3=1-(c1+c2);
      X[0]=c1;
      X[1]=c2;
      X[2]=c3;
      GetMainServer()->GetY(X,&res);
      fprintf(fout,"%f;%f;%f;%f\n",c1,c2,c3,res);
      c2+=step;
    }
    c1-=step;
  }
  fclose(fout);
}

LRESULT CGridDialog::OnCommand(HWND hDlg, WPARAM idControl)
{
  switch(idControl) 
  {
    case IDC_BSELECT:
      {
        CFileSaveDialog dlg(hDlg,"Сохранить в файл",
          "Текстовые базы данных (*.csv)\0*.csv\0\0");
        dlg.DoModal();
        if(dlg.LastError()) break;
        eFile.SetText(dlg.GetName());
      }
      break;
    case IDC_BOK:
      {
        int steps=atoi(eSteps.GetText());
        if(steps<3 || steps >100)
        {
          CWarnReport rep;
          rep.Report("В поле 'Делений' должно находится число от 3 до 100.");
          break;
        }
        if(eFile.GetText()[0]==0)
        {
          CWarnReport rep;
          rep.Report("В поле 'Файл' должно находится имя файла.");
          break;
        }
        PerformGridTask(steps,eFile.GetText());
      }
    case IDC_BCANCEL:
      return CDialog::OnCommand(hDlg,idControl);
  }
  return 1;
}

class CExtremumDialog : public CDialog
{
  CEdit           eSteps, eResults;
  CButton         bMin, bMax;
public:
  CExtremumDialog() : CDialog(IDD_EXTREMUMDIALOG,GetForegroundWindow(),GetModuleHandle("SimpMesh.dll")) {}
  virtual LRESULT OnInitDialog(HWND hDlg, HWND hFocus);
  virtual LRESULT OnCommand(HWND hDlg, WPARAM idControl);
};

LRESULT CExtremumDialog::OnInitDialog(HWND hDlg, HWND hFocus)
{
  eSteps.Init(hDlg,IDC_ESTEPS);
  eResults.Init(hDlg,IDC_ERESULTS);
  bMin.Init(hDlg,IDC_RBMIN);
  bMax.Init(hDlg,IDC_RBMAX);
  bMin.Check(1);
  eSteps.SetText("5");
  utCenterWindow(hDlg);
  return 1;
}

static void PerformExtremumTask(int Steps, char *Out, int Min)
{
  double X[4], Xbest[4];
  double c1,c2,c3,step;
  double current, best;
  
  #define STORE_POINT {Xbest[0]=X[0]; Xbest[1]=X[1]; Xbest[2]=X[2]; best=current;}
  
  // Начальное приближение - центральная точка.
  X[0]=1./3.;
  X[1]=1./3.;
  X[2]=1./3.;
  GetMainServer()->GetY(X,&current);
  STORE_POINT;
  step=1.0/Steps;
  if(Min)
  {
    // Минимум
    c1=1;
    while(c1>=0)
    {
      c2=0;
      while((c1+c2)<=1)
      {
        c3=1-(c1+c2);
        X[0]=c1;
        X[1]=c2;
        X[2]=c3;
        GetMainServer()->GetY(X,&current);
        if(current<best) STORE_POINT;
        c2+=step;
      }
      c1-=step;
    }
  }
  else
  {
    // Максимум
    c1=1;
    while(c1>=0)
    {
      c2=0;
      while((c1+c2)<=1)
      {
        c3=1-(c1+c2);
        X[0]=c1;
        X[1]=c2;
        X[2]=c3;
        GetMainServer()->GetY(X,&current);
        if(current>best) STORE_POINT;
        c2+=step;
      }
      c1-=step;
    }
  }
  sprintf(Out,"Координаты точки %s:\r\n%f\r\n%f\r\n%f\r\nЗначение: %f",Min ? "минимума" : "максимума",
    Xbest[0],Xbest[1],Xbest[2],best);
    
  #undef STORE_POINT
}

LRESULT CExtremumDialog::OnCommand(HWND hDlg, WPARAM idControl)
{
  switch(idControl)
  {
    case IDC_BFIND:
      {
        int steps=atoi(eSteps.GetText());
        if(steps<3 || steps >100)
        {
          CWarnReport rep;
          rep.Report("В поле 'Делений' должно находится число от 3 до 100.");
          break;
        }
        char res[512];
        PerformExtremumTask(steps,res,bMin.IsChecked());
        eResults.SetText(res);
      }
      break;
    case IDC_BCLOSE:
      return CDialog::OnCommand(hDlg,idControl);
  }
  return 1;
}

void __stdcall SimplexGridPlugin::PluginMain() 
{
  CGridDialog dlg;
  dlg.DoModal();
}

void __stdcall SimplexExtremumPlugin::PluginMain()
{
  CExtremumDialog dlg;
  dlg.DoModal();
}

int __declspec(dllexport) __stdcall PlugInsEnumerate(TEnumPlugInsProc EnumProc)
{
  if(EnumProc(&SimplexGrid)==PLGENUM_OK)
    if(EnumProc(&SimplexExtremum)==PLGENUM_OK)
      return 1;
  return 1;
}

// Точка входа
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
  return 1;
}

