/// OutputTreeNew.cpp
/** Classes used to display the folder heirarchy and list
    of files to be produced.
*/

#if defined(__WXGTK__) || defined(__WXMOTIF__)
  #include <wx/wx.h>
#endif

#include <wx/wxprec.h>
#include <wx/treectrl.h>
#include <wx/imaglist.h>
#include <wx/filename.h>

#include "file.xpm"
#include "folder.xpm"
#include "open_folder.xpm"

#include "OutputterBase.h"
#include "OutputList.h"
#include "OutputTreeNew.h"
#include "StringConvert.h"
#include "McPanel.h"
#include "Converter.h"

using namespace jcs;

enum {
  MENU_RENAME,
  MENU_OPTIONS,
  MENU_OVERRIDE
};

BEGIN_EVENT_TABLE(NewOutputTreeCtrl, wxTreeCtrl)

  EVT_TREE_ITEM_RIGHT_CLICK(-1, NewOutputTreeCtrl::OnContextMenu)
  EVT_MENU(MENU_OVERRIDE,     NewOutputTreeCtrl::OnMenuOverride)
  EVT_MENU(MENU_RENAME,     NewOutputTreeCtrl::OnMenuRename)
  EVT_MENU(MENU_OPTIONS,      NewOutputTreeCtrl::OnMenuOptions)

//  EVT_TREE_BEGIN_LABEL_EDIT(-1, NewOutputTreeCtrl::OnEditBegin)

END_EVENT_TABLE()


///
/**
*/
NewOutputTreeCtrl::NewOutputTreeCtrl(McPanel* parent, wxWindowID id, const wxString& root) :
    wxTreeCtrl(parent, id, wxDefaultPosition, wxDefaultSize, 
      /*wxTR_EDIT_LABELS |*/ wxTR_HAS_BUTTONS | wxSUNKEN_BORDER), parentPanel(parent)
{
  CreateImageList();
  AddRoot(root, folder_icon, -1, new RootItem(root, *this));
  SetItemImage(GetRootItem(), open_folder_icon, wxTreeItemIcon_Expanded);
}


///
/**
*/
NewOutputTreeCtrl::~NewOutputTreeCtrl()
{
}


///
/**
*/
void
NewOutputTreeCtrl::SetOutputter(OutputterBase* outputter)
{
  mOutputter = outputter;
  mOutputter->mOutputList.rootDir = GetItemText(GetRootItem());

  Reset();
  RefreshList();
}


///
/**
*/
void
NewOutputTreeCtrl::CreateImageList(int size)
{
  wxImageList* images = new wxImageList(size, size, true);

  images->Add(wxIcon(folder_xpm));
  images->Add(wxIcon(open_folder_xpm));
  images->Add(wxIcon(file_xpm));

  AssignImageList(images);
}


///
/**
*/
void
NewOutputTreeCtrl::RemoveBranch(wxTreeItemId branch)
{
  if (branch != GetRootItem()) {

    if (dynamic_cast<FileItem*> (GetItemData(branch))) {
      return;
    }

    wxTreeItemId parent = GetItemParent(branch);
    Delete(branch);

    if (parent != GetRootItem() && !ItemHasChildren(parent)) {
      RemoveBranch(parent);
    }
  }
}


///
/**
*/
void
NewOutputTreeCtrl::Reset()
{
  Collapse(GetRootItem());
  DeleteChildren(GetRootItem());
}


///
/**
*/
void
NewOutputTreeCtrl::OnMenuOverride(wxCommandEvent& event)
{
  wxTreeItemId item = GetSelection();
  if (!item.IsOk()) {
    return;
  }

  // this is lame...this control is such a mess
  OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));

  std::string seriesUid = data->GetSeriesUid();

  if (seriesUid.empty()) {
    return;
  }

  parentPanel->OnOverride(seriesUid);
  parentPanel->mpConverter->UpdateOutput(seriesUid);
  Reset();
  RefreshList();
}


///
/**
*/
// Find an item in a tree given a parent and unique ID.
// If not found, return invalid item.
wxTreeItemId
NewOutputTreeCtrl::FindTreeItem(wxTreeItemId parent, const std::string& uid)
{
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(parent, cookie);
  while (item.IsOk()) {

    OutputTreeItemData* itemData = dynamic_cast<OutputTreeItemData*> (GetItemData(item));
    if (itemData->GetUid() == uid) {
      return item;
    }
    item = GetNextChild(parent, cookie);
  }
  return item;
}


///
/**
*/
wxString
NewOutputTreeCtrl::GetStringSelection() const
{
  wxString retval = "error";
  wxTreeItemId item = GetSelection();
  if (item.IsOk()) {
    retval = GetItemText(item);
    return GetItemText(item);
  }
  else {
    return _("error");
  }
}


///
/**
*/
void
NewOutputTreeCtrl::OnMenuOptions(wxCommandEvent& event)
{
  parentPanel->OnOptions(event);
}


///
/**
*/
void
NewOutputTreeCtrl::OnMenuRename(wxCommandEvent& event)
{
  wxTreeItemId item = GetSelection();
  if (item.IsOk()) {
    OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
    data->OnRename();
//    parentPanel->SetPathInfo(GetStringSelection());
  }
}


///
/**
*/
void
NewOutputTreeCtrl::OnContextMenu(wxTreeEvent& event)
{
  wxTreeItemId item = event.GetItem();
  if (item.IsOk()) {
    SelectItem(item);
    OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
    wxMenu menu;
    data->FillContextMenu(menu);
    wxPoint pos = ::wxGetMousePosition();
    PopupMenu(&menu, ScreenToClient(pos));
  }
}


///
/**
*/
void
NewOutputTreeCtrl::OnContextMenuTest(wxContextMenuEvent& event)
{
  wxLogMessage(_("Got context menu event"));
}


///
/**
*/
void
NewOutputTreeCtrl::OnEditBegin(wxTreeEvent& event)
{
  wxTreeItemId item = event.GetItem();
  assert (item.IsOk());
  OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
  if (!data->IsEditable()) {
    event.Veto();
  }
}


///
/**
*/
void
OutputTreeItemData::FillContextMenu(wxMenu& menu)
{
  menu.Append(MENU_RENAME, _("Rename"));
  menu.Append(MENU_OPTIONS, _("Default format options"));
}


///
/**
*/
void
RootItem::OnRename()
{
  mTreeCtrl.ChooseRootDir();
}


///
/**
*/
void
FileItem::FillContextMenu(wxMenu& menu)
{
  OutputTreeItemData::FillContextMenu(menu);
  menu.Append(MENU_OVERRIDE, _("Override default options"));
  ImageFileName name = mTreeCtrl.mOutputter->GetImageFileName(GetSeriesUid(), mTreeCtrl.GetStringSelection().ToStdString());
  if (!name.isRenameable) {
    wxMenuItem* item = menu.FindItem(MENU_RENAME);
    item->Enable(false);
  }
}


///
/**
*/
void
FileItem::OnRename()
{
  ImageFileName name = mTreeCtrl.mOutputter->GetImageFileName(GetSeriesUid(), mTreeCtrl.GetStringSelection().ToStdString());
  if (!name.isRenameable) {
    return;
  }
  mTreeCtrl.RenameFiles(GetSeriesUid(), name.GetPrefix());
}


///
/**
*/
void
DirItem::OnRename()
{
  mTreeCtrl.RenameDir(GetId());
}


///
/**
*/
void
NewOutputTreeCtrl::RenameFiles(std::string series_uid, std::string prefix)
{
  wxTextEntryDialog* te_dlg
    = new wxTextEntryDialog(this, _("Please enter new name"), _("Rename output files"), prefix);
                            //wxString(prefix.c_str(), wxConvLocal));

  if (te_dlg->ShowModal() == wxID_OK) {
    mOutputter->ChangeFileName(series_uid, te_dlg->GetValue().ToStdString());
    Reset();
    RefreshList();
  }
  te_dlg->Destroy();
}


///
/**
*/
void
NewOutputTreeCtrl::RenameDir(wxTreeItemId diritem)
{
  wxTextEntryDialog* te_dlg
    = new wxTextEntryDialog(this, _T("Please enter new name"), 
      _T("Rename directory"), GetItemText(diritem));

  if (te_dlg->ShowModal() == wxID_OK) {

    int position = 0;
    wxTreeItemId parent = GetItemParent(diritem);
    while ((parent != GetRootItem()) && (position < 20)) {
      parent = GetItemParent(parent);
      ++position;
    }

    // get all series_uids affected by change --
    // only necessary for top-level directories
    std::vector<std::string> uids;
    if (position == 0) {
      wxTreeItemIdValue cookie;
      wxTreeItemId item = GetFirstChild(diritem, cookie);
      while (item.IsOk()) {
        OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
        uids.push_back(data->GetUid());
        item = GetNextChild(diritem, cookie);
      }
    }
    else {
      OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(diritem));
      uids.push_back(data->GetUid());
    }

    mOutputter->ChangeDirName(uids, te_dlg->GetValue().ToStdString(), position);
    Reset();
    RefreshList();
  }
}


///
/**
*/
void
NewOutputTreeCtrl::ChooseRootDir()
{
  wxDirDialog *dlg = new wxDirDialog(this, 
    _("Select a directory for converted files"), 
    GetItemText(GetRootItem()),
    wxDD_DEFAULT_STYLE);

  if (dlg->ShowModal() == wxID_OK)  {
    SetRoot(dlg->GetPath());
  }

  dlg->Destroy();
}


///
/**
*/
void
NewOutputTreeCtrl::SetRoot(const wxString& text)
{
  SetItemText(GetRootItem(), text);
  mOutputter->mOutputList.rootDir = text;
}


///
/**
*/
// todo -- partial update by series?
// this does conversion using wxString::From8BitData, perhaps the OutputList
// should hold this in proper UTF8 form rather than Unicode?
void
NewOutputTreeCtrl::RefreshList()
{
  OutputList::ListType::iterator it = mOutputter->mOutputList.fileNameList.begin();
  OutputList::ListType::iterator it_end = mOutputter->mOutputList.fileNameList.end();
  for (;it != it_end; ++it) {
    int nDirs = it->second.GetDirCount();
    wxTreeItemId parent = GetRootItem();
    for (int i = 0; i < nDirs; ++i) {
      std::string dirUid = it->second.GetDirUid(i);
      wxTreeItemIdValue cookie;
      wxTreeItemId item = GetFirstChild(parent, cookie);
      while (item.IsOk()) {
        OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
        if (data->GetUid() == dirUid) {
          it->second.SetDirName(i, std::string(GetItemText(item).mb_str(wxConvLocal)));
          break;
        }
        item = GetNextChild(parent, cookie);
      }
      if (!item.IsOk()) {
        DirItem* new_item = new DirItem(dirUid, *this);
        AppendItem(parent, wxString(it->second.GetDirName(i)), folder_icon, -1, new_item);
        item = new_item->GetId();
      }
      parent = item;
    }

    // Only add if file isn't already there
    wxTreeItemIdValue cookie;
    wxTreeItemId item = GetFirstChild(parent, cookie);
    std::string file_uid = it->first + it->second.GetExt();
    while (item.IsOk()) {
      OutputTreeItemData* data = dynamic_cast<OutputTreeItemData*>(GetItemData(item));
      if (data->GetUid() == file_uid) {
        break;
      }
      item = GetNextChild(parent, cookie);
    }

    if (!item.IsOk()) {
      FileItem* new_file_item = new FileItem(file_uid, *this);
      new_file_item->SetSeriesUid(it->second.seriesUid);
      AppendItem(parent, wxString(it->second.GetFullName()), file_icon, -1, new_file_item);
    }
  }
  if (!mOutputter->mOutputList.fileNameList.empty()) {
    Expand(GetRootItem());
  }
}


/// Removes a specific series
/** Used to remove a series from the list of series to be processed.
    \param seriesUid String representation of the series.
*/
void
NewOutputTreeCtrl::RemoveSeries(const std::string& seriesUid)
{
  std::vector<wxTreeItemId> items_to_delete;
  wxTreeItemId root = GetRootItem();
  CheckChildren(root, items_to_delete, seriesUid);
  while (!items_to_delete.empty()) {
    std::vector<wxTreeItemId>::iterator it = items_to_delete.begin();
    std::vector<wxTreeItemId>::iterator it_end = items_to_delete.end();
    for (; it != it_end; ++it) {
      Delete(*it);
    }
    items_to_delete.clear();
    CheckChildren(root, items_to_delete, seriesUid);
  }
}


///
/**
*/
void
NewOutputTreeCtrl::CheckChildren(wxTreeItemId& parent, std::vector<wxTreeItemId>& items, const std::string& seriesUid)
{
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(parent, cookie);
  while (item.IsOk()) {
    if (!ItemHasChildren(item)) {
      FileItem* data = dynamic_cast<FileItem*>(GetItemData(item));
      if (data == 0) { // a childless directory
        items.push_back(item);
      }
      else if (data->GetSeriesUid() == seriesUid) {
        items.push_back(item);
      }
    }
    else {
      CheckChildren(item, items, seriesUid);
    }
    item = GetNextChild(parent, cookie);
  }
}
