/* AbiCollab- Code to enable the modification of remote documents.
 * Copyright (C) 2005 by Martin Sevior
 *
 * 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#ifdef ABI_PLUGIN_BUILTIN
#define abi_plugin_register abipgn_abicollab_register
#define abi_plugin_unregister abipgn_abicollab_unregister
#define abi_plugin_supports_version abipgn_abicollab_supports_version
#endif

#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "xap_Module.h"
#include "xap_App.h"
#include "xap_Frame.h"
#include "fv_View.h"
#include "xav_View.h"
#include "xav_Listener.h"
#include "fl_BlockLayout.h"
#include "pd_Document.h"
#include "px_ChangeRecord.h"
#include "px_CR_SpanChange.h"
#include "px_CR_FmtMarkChange.h"  
#include "px_CR_SpanChange.h"
#include "px_CR_FmtMark.h"        
#include "px_CR_Span.h"
#include "px_CR_Glob.h"           
#include "px_CR_StruxChange.h"
#include "px_CR_ObjectChange.h"   
#include "px_CR_Strux.h"
#include "px_CR_Object.h"

#include "AbiCollab.h"

#include "ap_Menu_Id.h"
#include "ev_Menu_Actions.h"
#include "ev_Menu.h"
#include "ev_Menu_Layouts.h"
#include "ev_Menu_Labels.h"
#include "ev_EditMethod.h"
#include "xap_Menu_Layouts.h"
#include "ie_exp.h"
#include "ie_types.h"
#include "ut_types.h"
#include "ut_misc.h"
#include "ut_units.h"

#include "ut_sleep.h"
#include <sys/types.h>  
#include <sys/stat.h>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include "ut_files.h"
#endif

//
// AbiGOChart_addToMenus
// -----------------------
//   Adds "Object" "Gnome Office Chart" options to AbiWord's Main Menu.
//

static bool AbiCollab_FileMenuAttach(AV_View* v, EV_EditMethodCallData *d);
// FIXME make these translatable strings

static const char * szCollaborate = "Collaborate";
static const char * szCollaborateTip = "Collaborate with remote user";

ABI_Collab_Export::ABI_Collab_Export(PD_Document* pDoc) : 
  m_pDoc(pDoc),
  m_iGlobCounter(0),
  m_bEndFootnoteProcessedInBlock(false),
  m_chgMaskCached(0),
  m_bCacheChanges(false),
  m_lid(0)
{}

ABI_Collab_Export::~ABI_Collab_Export()
{
}

void ABI_Collab_Export::setLID(UT_uint32 lid)
{
  m_lid = lid;
}


bool ABI_Collab_Export::populate(PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr)
{
  UT_UTF8String sType;
  switch(pcr->getType())
    {
    case PX_ChangeRecord::PXT_GlobMarker:
      break;
    case PX_ChangeRecord::PXT_InsertSpan:
      {
	const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *>(pcr);
	PT_BlockOffset blockOffset = pcrs->getBlockOffset();
	UT_uint32 len = pcrs->getLength();
	UT_ASSERT(len>0);

	PT_BufIndex bi = pcrs->getBufIndex();
	const UT_UCSChar* pChars = m_pDoc->getPointer(bi);
	UT_UTF8String sText(pChars,len);
	UT_DEBUGMSG(("Insert Text |%s| \n",sText.utf8_str()));
	break;
      }
    case PX_ChangeRecord::PXT_DeleteSpan:
      break;
    case PX_ChangeRecord::PXT_ChangeSpan:
      break;
    case PX_ChangeRecord::PXT_InsertStrux:
      break;
    case PX_ChangeRecord::PXT_DeleteStrux:
      break;
    case PX_ChangeRecord::PXT_ChangeStrux:
      break;
    case PX_ChangeRecord::PXT_InsertObject:
      break;
    case PX_ChangeRecord::PXT_DeleteObject:
      break;
    case PX_ChangeRecord::PXT_ChangeObject:
      break;
    case PX_ChangeRecord::PXT_InsertFmtMark:
      break;
    case PX_ChangeRecord::PXT_DeleteFmtMark:
      break; 
    case PX_ChangeRecord::PXT_ChangeFmtMark:
	break;
    case PX_ChangeRecord::PXT_ChangePoint:
	break; 
    case PX_ChangeRecord::PXT_ListUpdate:
	break; 
    case PX_ChangeRecord::PXT_StopList:
	break; 
    case PX_ChangeRecord::PXT_UpdateField:
	break;
    case PX_ChangeRecord::PXT_RemoveList:
	break;
    case PX_ChangeRecord::PXT_UpdateLayout:
	break;
    default:
      break;
    }
  mapChangeRecordToString(pcr,sType);
  UT_DEBUGMSG(("Populate Export CR Type %s \n",sType.utf8_str()));
  return true;
}

bool  ABI_Collab_Export::mapChangeRecordToString(const PX_ChangeRecord * pcr, UT_UTF8String & sCR)
{
  switch(pcr->getType())
    {
    case PX_ChangeRecord::PXT_GlobMarker:
      sCR = "PX_ChangeRecord::PXT_GlobMarker";
      break;
    case PX_ChangeRecord::PXT_InsertSpan:
      sCR = "PX_ChangeRecord::PXT_InsertSpan";
      break;
    case PX_ChangeRecord::PXT_DeleteSpan:
      sCR = "PX_ChangeRecord::PXT_DeleteSpan";
      break;
    case PX_ChangeRecord::PXT_ChangeSpan:
      sCR = "PX_ChangeRecord::PXT_ChangeSpan";
      break;
    case PX_ChangeRecord::PXT_InsertStrux:
      sCR = "PX_ChangeRecord::PXT_InsertStrux";
      break;
    case PX_ChangeRecord::PXT_DeleteStrux:
      sCR = "PX_ChangeRecord::PXT_DeleteStrux";
      break;
    case PX_ChangeRecord::PXT_ChangeStrux:
      sCR = "PX_ChangeRecord::PXT_ChangeStrux";
      break;
    case PX_ChangeRecord::PXT_InsertObject:
      sCR = "PX_ChangeRecord::PXT_InsertObject";
      break;
    case PX_ChangeRecord::PXT_DeleteObject:
      sCR = "PX_ChangeRecord::PXT_DeleteObject";
      break;
    case PX_ChangeRecord::PXT_ChangeObject:
      sCR = "PX_ChangeRecord::PXT_ChangeObject";
      break;
    case PX_ChangeRecord::PXT_InsertFmtMark:
      sCR = "PX_ChangeRecord::PXT_InsertFmtMark";
      break;
    case PX_ChangeRecord::PXT_DeleteFmtMark:
      sCR = "PX_ChangeRecord::PXT_DeleteFmtMark";
      break; 
    case PX_ChangeRecord::PXT_ChangeFmtMark:
	sCR = "PX_ChangeRecord::PXT_ChangeFmtMark";
	break;
    case PX_ChangeRecord::PXT_ChangePoint:
	sCR = "PX_ChangeRecord::PXT_ChangePoint";
	break; 
    case PX_ChangeRecord::PXT_ListUpdate:
	sCR = "PX_ChangeRecord::PXT_ListUpdate";
	break; 
    case PX_ChangeRecord::PXT_StopList:
	sCR = "PX_ChangeRecord::PXT_StopList";
	break; 
    case PX_ChangeRecord::PXT_UpdateField:
	sCR = "PX_ChangeRecord::PXT_UpdateField";
	break;
    case PX_ChangeRecord::PXT_RemoveList:
	sCR = "PX_ChangeRecord::PXT_RemoveList";
	break;
    case PX_ChangeRecord::PXT_UpdateLayout:
	sCR = "PX_ChangeRecord::PXT_UpdateLayout";
	break;
    default:
      break;
    }
}
bool ABI_Collab_Export::populateStrux(PL_StruxDocHandle sdh,
				      const PX_ChangeRecord * pcr,
				      PL_StruxFmtHandle * psfh)
{
  *psfh = sdh;
  UT_UTF8String sType;
  switch(pcr->getType())
    {
    case PX_ChangeRecord::PXT_GlobMarker:
      break;
    case PX_ChangeRecord::PXT_InsertSpan:
      break;
    case PX_ChangeRecord::PXT_DeleteSpan:
      break;
    case PX_ChangeRecord::PXT_ChangeSpan:
      break;
    case PX_ChangeRecord::PXT_InsertStrux:
      break;
    case PX_ChangeRecord::PXT_DeleteStrux:
      break;
    case PX_ChangeRecord::PXT_ChangeStrux:
      break;
    case PX_ChangeRecord::PXT_InsertObject:
      break;
    case PX_ChangeRecord::PXT_DeleteObject:
      break;
    case PX_ChangeRecord::PXT_ChangeObject:
      break;
    case PX_ChangeRecord::PXT_InsertFmtMark:
      break;
    case PX_ChangeRecord::PXT_DeleteFmtMark:
      break; 
    case PX_ChangeRecord::PXT_ChangeFmtMark:
	break;
    case PX_ChangeRecord::PXT_ChangePoint:
	break; 
    case PX_ChangeRecord::PXT_ListUpdate:
	break; 
    case PX_ChangeRecord::PXT_StopList:
	break; 
    case PX_ChangeRecord::PXT_UpdateField:
	break;
    case PX_ChangeRecord::PXT_RemoveList:
	break;
    case PX_ChangeRecord::PXT_UpdateLayout:
	break;
    default:
      break;
    }
  mapChangeRecordToString(pcr,sType);
  UT_DEBUGMSG(("Populate Strux Export CR Type %s \n",sType.utf8_str()));
  return true;
}

 bool ABI_Collab_Export::change(PL_StruxFmtHandle sfh,
				const PX_ChangeRecord * pcr)
{
  UT_UTF8String sType;
  switch(pcr->getType())
    {
    case PX_ChangeRecord::PXT_GlobMarker:
      break;
    case PX_ChangeRecord::PXT_InsertSpan:
      {
	const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *>(pcr);
	PT_BlockOffset blockOffset = pcrs->getBlockOffset();
	UT_uint32 len = pcrs->getLength();
	UT_ASSERT(len>0);

	PT_BufIndex bi = pcrs->getBufIndex();
	const UT_UCSChar* pChars = m_pDoc->getPointer(bi);
	UT_UTF8String sText(pChars,len);
	UT_DEBUGMSG(("Insert Text |%s| \n",sText.utf8_str()));
      }
      break;
    case PX_ChangeRecord::PXT_DeleteSpan:
      break;
    case PX_ChangeRecord::PXT_ChangeSpan:
      break;
    case PX_ChangeRecord::PXT_InsertStrux:
      break;
    case PX_ChangeRecord::PXT_DeleteStrux:
      break;
    case PX_ChangeRecord::PXT_ChangeStrux:
      break;
    case PX_ChangeRecord::PXT_InsertObject:
      break;
    case PX_ChangeRecord::PXT_DeleteObject:
      break;
    case PX_ChangeRecord::PXT_ChangeObject:
      break;
    case PX_ChangeRecord::PXT_InsertFmtMark:
      break;
    case PX_ChangeRecord::PXT_DeleteFmtMark:
      break; 
    case PX_ChangeRecord::PXT_ChangeFmtMark:
	break;
    case PX_ChangeRecord::PXT_ChangePoint:
	break; 
    case PX_ChangeRecord::PXT_ListUpdate:
	break; 
    case PX_ChangeRecord::PXT_StopList:
	break; 
    case PX_ChangeRecord::PXT_UpdateField:
	break;
    case PX_ChangeRecord::PXT_RemoveList:
	break;
    case PX_ChangeRecord::PXT_UpdateLayout:
	break;
    default:
      break;
    }
  mapChangeRecordToString(pcr,sType);
  UT_DEBUGMSG(("Change Export CR Type %s \n",sType.utf8_str()));
  return true;
}

void  ABI_Collab_Export::deferNotifications(void)
{
}

void ABI_Collab_Export::processDeferredNotifications(void)
{
}

bool  ABI_Collab_Export::insertStrux(PL_StruxFmtHandle sfh,
									const PX_ChangeRecord * pcr,
									PL_StruxDocHandle sdh,
									PL_ListenerId lid,
									void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
															PL_ListenerId lid,
															PL_StruxFmtHandle sfhNew))
{
  UT_UTF8String sType;
  if(pfnBindHandles)
    {
      PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(this);
      pfnBindHandles(sdh,lid,sfhNew);
    }
 
  switch(pcr->getType())
    {
    case PX_ChangeRecord::PXT_GlobMarker:
      break;
    case PX_ChangeRecord::PXT_InsertSpan:
      break;
    case PX_ChangeRecord::PXT_DeleteSpan:
      break;
    case PX_ChangeRecord::PXT_ChangeSpan:
      break;
    case PX_ChangeRecord::PXT_InsertStrux:
      break;
    case PX_ChangeRecord::PXT_DeleteStrux:
      break;
    case PX_ChangeRecord::PXT_ChangeStrux:
      break;
    case PX_ChangeRecord::PXT_InsertObject:
      break;
    case PX_ChangeRecord::PXT_DeleteObject:
      break;
    case PX_ChangeRecord::PXT_ChangeObject:
      break;
    case PX_ChangeRecord::PXT_InsertFmtMark:
      break;
    case PX_ChangeRecord::PXT_DeleteFmtMark:
      break; 
    case PX_ChangeRecord::PXT_ChangeFmtMark:
	break;
    case PX_ChangeRecord::PXT_ChangePoint:
	break; 
    case PX_ChangeRecord::PXT_ListUpdate:
	break; 
    case PX_ChangeRecord::PXT_StopList:
	break; 
    case PX_ChangeRecord::PXT_UpdateField:
	break;
    case PX_ChangeRecord::PXT_RemoveList:
	break;
    case PX_ChangeRecord::PXT_UpdateLayout:
	break;
    default:
      break;
    }
  mapChangeRecordToString(pcr,sType);
  UT_DEBUGMSG(("Insert Strux Export CR Type %s \n",sType.utf8_str()));
  return true;
}

bool ABI_Collab_Export::signal(UT_uint32 iSignal)
{
  UT_DEBUGMSG(("Export Signal %d \n",iSignal));
  return true;
}

bool  ABI_Collab_Export::buildPacket(const PX_ChangeRecord * pcr,
					     PL_StruxDocHandle sdh,
					     UT_UTF8String & sPacket)
{
  return true;
}

//
// AbiCollab_FileMenuAttach
// ------------------------
//   Menu item to attach to the current document.
//   Expand this later. NOTE!! We must work out a good way to remove
//   The exporter!!!
//
bool 
AbiCollab_FileMenuAttach(AV_View* v, EV_EditMethodCallData *d)
{
    // Get the current view that the user is in.
    XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame();
    FV_View* pView = static_cast<FV_View*>(pFrame->getCurrentView());
    PD_Document * pDoc = static_cast<PD_Document *>(pFrame->getCurrentDoc());

    UT_DEBUGMSG(("Attaching export listener to document %x \n",pDoc));
    UT_uint32 lid = 0;
    ABI_Collab_Export * pExport = new ABI_Collab_Export(pDoc);
    pDoc->addListener(static_cast<PL_Listener *>(pExport),&lid);
    pExport->setLID(lid);
    return true;
}


static void AbiCollab_addToMenus()
{
    // First we need to get a pointer to the application itself.
    XAP_App *pApp = XAP_App::getApp();
    //
    // Translated Strings
    //
//     const XAP_StringSet * pSS = pApp->getStringSet();

    // it's callback function.  This is used to link the name to 
    // the callback.
    EV_EditMethod *myEditMethodFile = new EV_EditMethod(
        "AbiCollab_FileMenuAttach",  // name of callback function
        AbiCollab_FileMenuAttach,    // callback function itself.
        0,                      // no additional data required.
        ""                      // description -- allegedly never used for anything
    );
   
    // Now we need to get the EditMethod container for the application.
    // This holds a series of Edit Methods and links names to callbacks.
    EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer();
    
    // We have to add our EditMethod to the application's EditMethodList
    // so that the application will know what callback to call when a call
    // to "AbiMathView_FileInsert" is received.
    pEMC->addEditMethod(myEditMethodFile);
  

    // Now we need to grab an ActionSet.  This is going to be used later
    // on in our for loop.  Take a look near the bottom.
    EV_Menu_ActionSet* pActionSet = pApp->getMenuActionSet();

    
    // We need to go through and add the menu element to each "frame" 
    // of the application.  We can iterate through the frames by doing
    // XAP_App::getFrameCount() to tell us how many frames there are,
    // then calling XAP_App::getFrame(i) to get the i-th frame.

    int frameCount = pApp->getFrameCount();
    XAP_Menu_Factory * pFact = pApp->getMenuFactory();

    // Look to see if "Object" has been loaded already..
    XAP_Menu_Id newCollabID= 0;

    newCollabID= pFact->addNewMenuBefore("Main",NULL,"P&roperties",EV_MLF_Normal);

    UT_DEBUGMSG(("newCollabID %d \n",newCollabID));


    pFact->addNewLabel(NULL,newCollabID,szCollaborate, szCollaborateTip);


    // Create the Action that will be called.
    EV_Menu_Action* myAction = new EV_Menu_Action(
	newCollabID,        // id that the layout said we could use
	0,                      // no, we don't have a sub menu.
	1,                      // yes, we raise a dialog.
	0,                      // no, we don't have a checkbox.
	0,                      // no radio buttons for me, thank you
	"AbiCollab_FileMenuAttach",  // name of callback function to call.
	NULL,                   // don't know/care what this is for
	NULL                    // don't know/care what this is for
        );

    // Now what we need to do is add this particular action to the ActionSet
    // of the application.  This forms the link between our new ID that we 
    // got for this particular frame with the EditMethod that knows how to 
    // call our callback function.  

    pActionSet->addAction(myAction);

    for(int i = 0;i < frameCount;++i)
    {
        // Get the current frame that we're iterating through.
		XAP_Frame* pFrame = pApp->getFrame(i);
		pFrame->rebuildMenus();
    }
}

static void
AbiCollab_removeFromMenus ()
{
  // First we need to get a pointer to the application itself.
  XAP_App *pApp = XAP_App::getApp();

  // remove the edit method
  EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer() ;
  EV_EditMethod * pEM = ev_EditMethod_lookup ( "AbiCollab_FileMenuAttach" ) ;
  pEMC->removeEditMethod ( pEM ) ;
  DELETEP( pEM ) ;

  // now remove crap from the menus
  int frameCount = pApp->getFrameCount();
  XAP_Menu_Factory * pFact = pApp->getMenuFactory();

  pFact->removeMenuItem("Main",NULL,szCollaborate);
  for(int i = 0;i < frameCount;++i)
  {
      // Get the current frame that we're iterating through.
      XAP_Frame* pFrame = pApp->getFrame(i);
      pFrame->rebuildMenus();
  }
}
 
ABI_PLUGIN_DECLARE(AbiCollab)

// -----------------------------------------------------------------------
//
//      Abiword Plugin Interface 
//
// -----------------------------------------------------------------------

  
ABI_FAR_CALL
int abi_plugin_register (XAP_ModuleInfo * mi)
{
    mi->name = "AbiCollab";
    mi->desc = "This plugin allows modification of remote documents";
    mi->version = ABI_VERSION_STRING;
    mi->author = "Martin Sevior <msevior@physics.unimelb.edu.au>";
    mi->usage = "No Usage";
    
    // Add menu item

    AbiCollab_addToMenus();
    return 1;
}


ABI_FAR_CALL
int abi_plugin_unregister (XAP_ModuleInfo * mi)
{
    mi->name = 0;
    mi->desc = 0;
    mi->version = 0;
    mi->author = 0;
    mi->usage = 0;

    AbiCollab_removeFromMenus ();

    return 1;
}


ABI_FAR_CALL
int abi_plugin_supports_version (UT_uint32 major, UT_uint32 minor, UT_uint32 release)
{
    return 1; 
}
