/***************************************************************************

    source::worx xtree
    Copyright © 2024-2025 c.holzheuer
    christoph.holzheuer@gmail.com

    This program 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 2 of the License, or
    (at your option) any later version.

***************************************************************************/


#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QQmlContext>

#include <xqmainwindow.h>
#include <xqcommand.h>
#include <xqexception.h>
#include <xqitemfactory.h>
#include <xqnodewriter.h>
#include <xqquickwidget.h>


//! konstruktor.

XQMainWindow::XQMainWindow( QWidget* parent )
    : QMainWindow(parent)
{
  setupUi(this);
  setWindowTitle( QString("XTree %1").arg(c_Version));
  initMainWindow();
}


// setzt das working directory: dieses muss das 'xml' datenverzeichnis enthalten.

void XQMainWindow::setupWorkingDir()
{
  QDir dir = QDir::current();

  while (dir.exists())
  {
    QString xmlPath = dir.absoluteFilePath("xml");
    if (QDir(xmlPath).exists())
    {
      qDebug() << " --- CD TO: " << dir.absolutePath();
      QDir::setCurrent( dir.absolutePath() );
    }
    if (!dir.cdUp())
      return;
  }
}


//! actions & document struktur einrichten.

void XQMainWindow::initMainWindow()
{

  // das working dir setzen: 'xml' muss als unterverzeichnis vorhanden sein.
  setupWorkingDir();

  // als allererstes laden wir die Modelschreibungen
  XQItemFactory::instance().initItemFactory( c_ModelSheetFileName );

  _undoView->setStack( &_undoStack );

  _actionUndo->setData(   XQCommand::cmdUndo);
  _actionRedo->setData(   XQCommand::cmdRedo);
  _actionCut->setData(    XQCommand::cmdCut);
  _actionCopy->setData(   XQCommand::cmdCopy);
  _actionPaste->setData(  XQCommand::cmdPaste);
  _actionNew->setData(    XQCommand::cmdNew);
  _actionDelete->setData( XQCommand::cmdDelete);

  connect( _actionUndo,   &QAction::triggered, this, &XQMainWindow::onUndo );
  connect( _actionRedo,   &QAction::triggered, this, &XQMainWindow::onRedo );

  connect( _actionOpen,   &QAction::triggered, this, &XQMainWindow::onOpenDocument );
  connect( _actionSave,   &QAction::triggered, this, &XQMainWindow::onSaveDocument );
  connect( _actionSaveAs, &QAction::triggered, this, &XQMainWindow::onSaveDocumentAs );
  connect( _actionExit,   &QAction::triggered, this, &XQMainWindow::onExit );
  connect( _actionAbout,  &QAction::triggered, this, &XQMainWindow::onAbout );

  connect( _tabWidget,    SIGNAL(tabBarClicked(int)),    this, SLOT(onChildViewTabClicked(int)) );

  //connect(&_mainModel, &QStandardItemModel::itemChanged, this, &XQMainWindow::onTreeItemChanged );
  //connect( _mainTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex)) );
  //connect( _mainTreeView, SIGNAL(clicked(QModelIndex)),  this, SLOT(onTreeItemClicked(QModelIndex)) );

  connect(&_mainModel, &XQViewModel::xqItemChanged, this, &XQMainWindow::onTreeViewItemChanged );

  connect( _mainTreeView, &QTreeView::clicked, this, [&,this](const QModelIndex& index)
  {
    onTreeViewItemClicked( XQItem::xqItemFromIndex(index) );
  });

  try
  {

    // hand over undostack
    _mainModel.setUndoStack(&_undoStack);
    // hand over left side navigation tree
    _mainModel.setTreeTable(_mainTreeView);
    // #1. init the left side main tree view
    _mainModel.initModel( c_MainModelName );

    // 1: Wiebelbach
    // 2: Gerbrunn
    // 3: TBB

    // #2. load demo data
    loadDocument( c_DocumentFileName1 );
    loadDocument( c_DocumentFileName2, true );
    //loadDocument( c_DocumentFileName2 );
    loadDocument( c_DocumentFileName3 );


    qDebug() << " --- #nodes created: " << XQNode::s_Count;

  }  
  catch( XQException& exception )
  {
    qDebug() << exception.what();
    QMessageBox::critical( this, "Failure", QString("Failure: %1").arg(exception.what()) );
  }
  
}


//! slot für zentrales undo

void XQMainWindow::onUndo()
{
  qDebug() << " --- undo Pressed";
  if(_undoStack.canUndo())
    _undoStack.undo();

}


//! slot für zentrales redo

void XQMainWindow::onRedo()
{
  qDebug() << " --- redo Pressed";
  if(_undoStack.canRedo())
    _undoStack.redo();
}


//! erzeugt ein document

void XQMainWindow::onCreateDocument()
{
   qDebug() << " ---- create document Pressed!";
}


//! öffnet ein XML document

void XQMainWindow::onOpenDocument()
{
  QString fileName = QFileDialog::getOpenFileName(this, tr("Open Project"), c_DocumentDirectory, tr("project data(*.xtr)") );
  QFile file(fileName);

  if (!file.open(QFile::ReadOnly | QFile::Text))
  {
    QMessageBox::warning(this, "Warning", "Cannot load file: " + file.errorString());
    return;
  }
  // close dummy file ...
  file.close();

  loadDocument( fileName );
}


//! speichert ein XML document

void XQMainWindow::onSaveDocument()
{
  qDebug() << " ---- save Pressed!";
  saveDocument( c_ModelDummyFileName );
}


//! fragt nach einem datei-namen und speichert das akutelle XML
//! document unter diesem

void XQMainWindow::onSaveDocumentAs()
{
  QString fileName = QFileDialog::getSaveFileName(this, "Save as", c_DocumentDirectory, tr("project data(*.xtr)") );
  QFile file(fileName);
  // open dummy file
  if (!file.open(QFile::WriteOnly | QFile::Text))
  {
    QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());
    return;
  }
  // close dummy file ...
  file.close();
  // and create a xml stream
  saveDocument( fileName );
}


//! wird aufgerufen, wenn ein XML geschlossen werden soll.

void XQMainWindow::onCloseDocument()
{
   qDebug() << " ---- close Pressed!";
}


//! beendet diese application

void XQMainWindow::onExit()
{
  qApp->exit();
}


//! zeigt den about-dialog

void XQMainWindow::onAbout()
{

  QMessageBox msgBox(QMessageBox::NoIcon, "About", "", QMessageBox::Ok);

  QString text = "<b>xtree concept</b><br>";
  text += "2024-2025 c.holzheuer<br><br>";
  text += "<a href=\"https://sourceworx.org/xtree\">sourceworx.org/xtree</a>";

  msgBox.setTextFormat(Qt::RichText); // This allows you to click the link
  msgBox.setText( text );

  msgBox.exec();
}



//! wenn ein item im navigations-baum geklickt wird, soll die document
//! view rechts angepasst werden.

void XQMainWindow::onTreeViewItemClicked( const XQItem& item )
{
  //qDebug() << " --- Tree item CLICK:"  << item.text() << " : " << item.itemType().text();
  if( item.itemType().text() == "TreeChildType" )
  {
    setChildTabByName( item.text() );
  }
}

void XQMainWindow::onTreeViewItemChanged(const XQItem& item )
{
  // hier müssen wir erst das projekt aktivieren
  XQItem* xqItem = static_cast<XQItem*>(item.parent());
  onTreeViewItemClicked( *xqItem );
  //if( item.itemType().text() == "TreeSectionType" )
  {
    int idx = _tabWidget->currentIndex();
    if(_documentStore.contains(idx) )
    {      
      XQViewModel& childModel = *_documentStore[idx].viewModel;
      childModel.onToggleSection(item.text());
    }
  }
}


//! beim click auf ein tab im linken fenster wird der navigationsbaum angepasst.

void XQMainWindow::onChildViewTabClicked( int idx )
{
  if(_documentStore.contains(idx) )
  {
    QModelIndex treeIndex =_documentStore[idx].treeItem->index();
    _mainModel.expandNewItem( treeIndex );
  }
}


//! SLOT, der aufgerufen wird, sobald eine section erzeugt worden ist.

void XQMainWindow::onSectionCreated( const XQModelSection& section )
{
  if( _currentProjectItem )
  {
     _mainModel.addSectionItem( section, _currentProjectItem );
  }
}


//! SLOT, der aufgerufen wird, wenn eine section getoggelt wurde.

void XQMainWindow::onSectionToggled( const XQModelSection& section )
{ 
  for (int row = 0; row < _currentProjectItem->rowCount(); ++row)
  {
    QStandardItem* child = _currentProjectItem->child(row);
    if (child->text() == section.contentType() )
    {
      bool checked = (child->checkState() == Qt::Checked);
      child->setCheckState( checked ?  Qt::Unchecked :Qt::Checked );
      break;
    }
  }
}


//! aktiviert das tab, das zum dokument mit dem schlüssel 'key' gehört.

void XQMainWindow::setChildTabByName( const QString& key )
{
  for( int i=0; i<_documentStore.size(); ++i )
  {
    if( key == _documentStore[i].friendlyName)
    {
      _tabWidget->setCurrentIndex(i);
      return;
    }
  }
}


XQNodePtr XQMainWindow::createDataTree( const QString& fileName )
{
  // gibts die Datei?
  if( !QFile::exists( fileName) )
    throw XQException( "no such file", fileName );

  XQNodeFactory treeLoader;
  // xml daten laden
  XQNodePtr rawTree = treeLoader.load_tree( qPrintable(fileName) );
  // versteckten root node ignorieren
  return rawTree->first_child();
}


XQChildModel* XQMainWindow::createChildModel( const XQNodePtr& contentRoot )
{
  // Ein neues Child-Model erzeugen
  XQChildModel* childModel = new XQChildModel(this);

  connect( childModel, SIGNAL(sectionCreated(XQModelSection)), this, SLOT(onSectionCreated(XQModelSection)) );
  connect( childModel, SIGNAL(sectionToggled(XQModelSection)), this, SLOT(onSectionToggled(XQModelSection)) );

  // Den globalen undo-stack ...
  childModel->setUndoStack(&_undoStack);

  // die Modelstruktur anlegen
  childModel->initModel( c_ChildModelName );

  // model inhalte laden
  childModel->addModelData( contentRoot->first_child() );
  childModel->setObjectName( contentRoot->friendly_name() );

  return childModel;

}

//! liest eine XML datei namens 'fileName'

void XQMainWindow::loadDocument( const QString& fileName, bool useQML )
{

  // Datenbaum laden
  XQNodePtr contentRoot = createDataTree( fileName );

  // Falls schon vorhanden ...
  const QString& pID   = contentRoot->attribute(c_ProjectID);
  int idx = _documentStore.indexOf( pID );
  if( idx > -1 )
  {
    const XQDocument& document = _documentStore.at(idx);
    QMessageBox::warning( this, "Load Document", QString("File: %1 already loaded.").arg( fileName ) );
    _mainTreeView->setCurrentIndex( document.treeItem->index() );
    _tabWidget->setCurrentIndex( idx );
    // ... wird nichts wieter unternommen
    return;
  }

  // 'friendly Name' ist ein Link auf ein anderes Attribute
  // das als Namen verwendet wird.
  const QString& fName = contentRoot->friendly_name();
  QString pTabTitle = QString("Project %1: %2").arg( pID, fName );

   // neuen eintrag im übsichts-baum erzeugen
  _currentProjectItem = _mainModel.addProjectItem( contentRoot );
  // Kindmodel für den Datenbaum erzeugen.
  XQChildModel* childModel = createChildModel(contentRoot);

  _documentStore.addDocument( fileName, fName, _currentProjectItem, childModel );


  QWidget* childView{};
  if(useQML)
  {
    XQQuickWidget* quickView = new XQQuickWidget(_tabWidget);
    //quickChild->setResizeMode(QQuickWidget::SizeViewToRootObject);

    quickView->rootContext()->setContextProperty("xtrChildModel", childModel);
    quickView->setSource(QUrl("qrc:/xqtreeview.qml"));
    childView = quickView;
  }
  else
  {
    // Eine neue TreeView erzeugn und im TabWidget parken.
    XQTreeTable* treeTable = new XQTreeTable(_tabWidget);
    // und die TreeView übergeben
    childModel->setTreeTable(treeTable);
    childView = treeTable;
  }

  _tabWidget->addTab( childView, pTabTitle );
  _tabWidget->setCurrentWidget( childView );
  setWindowTitle( pTabTitle );

}


//! speichert ein XML unter dem 'filename'

void XQMainWindow::saveDocument( const QString& fileName )
{
  XQNodeWriter nodeWriter;
  int curIdx = _tabWidget->currentIndex();
  //XQNodePtr rootNode = _documentStore[curIdx].treeItem->contentNode();
  XQNodePtr rootNode = _documentStore[curIdx].viewModel->contentRootNode();
  Q_ASSERT(rootNode);
  nodeWriter.dumpTree( rootNode, fileName );
}





