/*

  cone search

  Copyright © 2012-4 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  Munipack is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with Munipack.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "xmunipack.h"
#include <wx/wx.h>
#include <wx/animate.h>
#include <wx/statline.h>
#include <wx/regex.h>
#include <list>
#include <map>


using namespace std;


// --- MuniCone


MuniCone::MuniCone(wxWindow *w, MuniConfig *c):
  wxDialog(w,wxID_ANY,"VO Cone Search"),config(c),
  throbber(new wxAnimationCtrl(this,wxID_ANY,c->throbber)),
  running(false),apply(false),pipe(this)
{
  SetIcon(config->munipack_icon);

  wxSizerFlags lf, cf, uf, sl;
  lf.Align(wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT);
  cf.Align(wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT);
  uf.Align(wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT);
  sl.Border().Expand();

  wxStaticText *label;

  wxFont bf(*wxNORMAL_FONT);
  bf.SetWeight(wxFONTWEIGHT_BOLD);

  wxFont sf(*wxSMALL_FONT);

  // title
  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer *st = new wxBoxSizer(wxHORIZONTAL);

  MuniArtIcons ico(wxART_MESSAGE_BOX,wxSize(48,48));
  st->Add(new wxStaticBitmap(this,wxID_ANY,ico.Icon(wxART_FIND)),wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxStaticText *title = new wxStaticText(this,wxID_ANY,"Virtual Observatory Cone Search");
  title->SetFont(bf);
  st->Add(title,wxSizerFlags().DoubleBorder().Center());
  topsizer->Add(st,wxSizerFlags().Border().Center());
  topsizer->Add(new wxStaticLine(this,wxID_ANY),sl);

  // form
  wxFlexGridSizer *grid = new wxFlexGridSizer(2);
  grid->AddGrowableCol(1);
  
  label = new wxStaticText(this,wxID_ANY,"Catalogue:");
  grid->Add(label,lf);
  wxArrayString catalogs;
  vector<VOCatResources> cats(catconf.GetCatalogues());
  int idefault = 0;
  for(size_t i = 0; i < cats.size(); i++) {
    if( cats[i].GetName() == catconf.GetName() ) 
      idefault = i;
    catalogs.Add(cats[i].GetName());
  }
  wxChoice *wcat = new wxChoice(this,wxID_ANY,wxDefaultPosition,wxDefaultSize,catalogs);
  wcat->SetSelection(idefault);
  grid->Add(wcat,cf);

  wxBoxSizer *ss;

  label = new wxStaticText(this,wxID_ANY,L"Center in α:");
  grid->Add(label,lf);
  ss = new wxBoxSizer(wxHORIZONTAL);
  walpha = new wxSpinCtrlDouble(this,wxID_ANY,"",wxDefaultPosition,wxDefaultSize,
				wxSP_ARROW_KEYS|wxTE_PROCESS_ENTER,0.0,360.0,0.0,0.01);
  ss->Add(walpha);
  ss->Add(new wxStaticText(this,wxID_ANY,L"[°]"),uf);
  grid->Add(ss,cf);

  label = new wxStaticText(this,wxID_ANY,L"Center in δ:");
  grid->Add(label,lf);
  ss = new wxBoxSizer(wxHORIZONTAL);
  wdelta = new wxSpinCtrlDouble(this,wxID_ANY,"",wxDefaultPosition,wxDefaultSize,
				wxSP_ARROW_KEYS|wxTE_PROCESS_ENTER,-90.0,90.0,0.0,0.01);
  ss->Add(wdelta);
  ss->Add(new wxStaticText(this,wxID_ANY,L"[°]"),uf);
  grid->Add(ss,cf);

  label = new wxStaticText(this,wxID_ANY,"Cone Radius:");
  grid->Add(label,lf);

  wradius = new wxSpinCtrlDouble(this,wxID_ANY,"",wxDefaultPosition,wxDefaultSize,
				 wxSP_ARROW_KEYS|wxTE_PROCESS_ENTER,0.0,100.0,0.1,0.01);
  ss = new wxBoxSizer(wxHORIZONTAL);
  ss->Add(wradius);
  ss->Add(new wxStaticText(this,wxID_ANY,L"[°]"),uf);
  grid->Add(ss,cf);


  wxBoxSizer *sb = new wxBoxSizer(wxHORIZONTAL);

  sb->Add(throbber,lf);
  status = new wxStaticText(this,wxID_ANY,"Some description");
  status->SetFont(sf);
  sb->Add(status,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).DoubleBorder());

  /*
  wxCollapsiblePane *collPane = new wxCollapsiblePane(this,wxID_ANY,"Options");
  wxWindow *win = collPane->GetPane();

  ss = new wxBoxSizer(wxHORIZONTAL);
  ss->Add(new wxStaticText(win,wxID_ANY,"Save To:"),cf.Border(wxLEFT));
  wxFilePickerCtrl *ls = new wxFilePickerCtrl(win,wxID_ANY,"","Select A File",
					      "FITS files "+config->dirmask+")|"+
					      config->dirmask+"| All files (*)|*",
					      wxDefaultPosition,wxDefaultSize,
					      wxFLP_SAVE|wxFLP_OVERWRITE_PROMPT|wxFLP_USE_TEXTCTRL|wxFLP_SMALL);
  ss->Add(ls,cf.Expand().Proportion(1).Border(wxRIGHT));

  win->SetSizer(ss);
  ss->SetSizeHints(win);
  */

  topsizer->Add(grid,wxSizerFlags().Center());
  topsizer->Add(sb,wxSizerFlags().Center());
  //  topsizer->Add(collPane,wxSizerFlags().Expand());
  search = new wxButton(this,wxID_FIND,"Search");
  topsizer->Add(search,wxSizerFlags().Border().Expand());

  wxSizer *buttsize = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
  if( buttsize )
    topsizer->Add(buttsize,wxSizerFlags().Right().Border());

  SetSizer(topsizer);

  Bind(wxEVT_UPDATE_UI,&MuniCone::OnUpdateUI,this);
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniCone::OnSearch,this,wxID_FIND);
  //  Bind(wxEVT_COMMAND_FILEPICKER_CHANGED,&MuniCone::OnSave,this,ls->GetId());
  Bind(wxEVT_COMMAND_CHOICE_SELECTED,&MuniCone::OnService,this,wcat->GetId());
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniCone::OnOk,this,wxID_OK);
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniCone::OnCancel,this,wxID_CANCEL);

  throbber->Show(false);
  status->Show(false);
  Fit();

  ids.push_back(wcat->GetId());
  ids.push_back(walpha->GetId());
  ids.push_back(wdelta->GetId());
  ids.push_back(wradius->GetId());

  tmpfile = wxFileName::CreateTempFileName("xmunipack-cone_");
}

MuniCone::~MuniCone()
{
  if( ! tmpfile.IsEmpty() && wxFileExists(tmpfile) )
    wxRemoveFile(tmpfile);
}

void MuniCone::OnClose(wxCloseEvent& event)
{
  wxLogDebug("MuniCone::OnClose");

  // Arranging of the code is really important
  // A use of Close() doesn't work because the event-system have no break
  // to execute other events (finishing subprocesses).
  if( running ) {
    pipe.Stop();
    wxQueueEvent(this,event.Clone());
  }
  else
    // Skipping here passes processing of Close event to default handler.
    event.Skip();
}

void MuniCone::OnCancel(wxCommandEvent& ev)
{
  wxLogDebug("Leaving Cone...");
  EndModal(wxID_CANCEL);
}

void MuniCone::OnOk(wxCommandEvent& ev)
{
  wxLogDebug("Applying Cone...");
  EndModal(wxID_OK);
}

void MuniCone::OnUpdateUI(wxUpdateUIEvent& event)
{
  wxASSERT(search);
  //  search->Enable(catconf.GetCatalogues().size() > 0);
  search->Enable(catconf.IsOk());

  wxASSERT(FindWindow(wxID_OK));
  FindWindow(wxID_OK)->Enable(apply && !running);

  wxASSERT(FindWindow(wxID_CANCEL));
  FindWindow(wxID_CANCEL)->Enable(!running);

  //  EnableCloseButton(!running);

  for(list<int>::const_iterator i = ids.begin(); i != ids.end(); ++i)
    FindWindow(*i)->Enable(!running);
}

void MuniCone::OnService(wxCommandEvent& event)
{
  catconf.SetCat(event.GetString());
}

void MuniCone::OnSearch(wxCommandEvent& event)
{
  if( running ) {
    pipe.Stop();
    status->SetLabel("Interrupted.");
    return;
  }

  wxRemoveFile(tmpfile);

  Bind(wxEVT_END_PROCESS,&MuniCone::OnFinish,this);
  
  MuniProcess *cone = new MuniProcess(&pipe,"cone");
  pipe.push(cone);

  wxString a;

  map<wxString,wxString> pars;
  a.Printf("%lf",wradius->GetValue());
  pars["SR"] = a; 
  a.Printf("%lf",walpha->GetValue());
  pars["RA"] = a; 
  a.Printf("%lf",wdelta->GetValue());
  pars["DEC"] = a;

  cone->Write("PIPELOG = T");
  cone->Write("SORT = '"+catconf.GetSort()+"'");
  cone->Write("TYPE = 'FITS'");
  cone->Write("URL = '" + catconf.GetUrl(pars) + "'");
  cone->WriteOutput(tmpfile);

  throbber->Show(true);
  throbber->Play();
  status->Show(true);
  status->SetLabel("Connecting VO...");
  search->SetLabel("STOP");
  running = true;
  apply = false;
  Layout();
  Fit();

  pipe.Start();
}

void MuniCone::OnFinish(wxProcessEvent& event)
{
  wxLogDebug("MuniCone::OnFinish");

  Unbind(wxEVT_END_PROCESS,&MuniCone::OnFinish,this);

  throbber->Stop();
  throbber->Show(false);
  search->SetLabel("Search");
  running = false;

  if( event.GetExitCode() != 0 )
    wxLogDebug("Failed with exit code %d",event.GetExitCode());
  else
    apply = ParseOutput(pipe.GetOutput());

  Layout();
  Fit();
}

/*
void MuniCone::OnSave(wxFileDirPickerEvent& event)
{
  savename = event.GetPath();
}
*/

bool MuniCone::ParseOutput(const wxArrayString& out)
{
  bool s = false;

  wxRegEx re("^=(.*)> (.+)");
  wxASSERT(re.IsValid());

  for(size_t i = 0; i < out.GetCount(); i++) {

    if( re.Matches(out[i]) ) {

      wxString key(re.GetMatch(out[i],1));
      wxString value(re.GetMatch(out[i],2));

      if( key == "CONE" ) {

	status->SetLabel(value);
	Layout();
	Fit();
      
	s = true;
      }
    }
  }

  return s;
}

