/*
* pawseditorapp.cpp
*
* Copyright (C) 2005 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
*
* Credits : 
*            Michael Cummings <cummings.michael@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.
* 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.
*
* Creation Date: 1/20/2005
* Description : main class for pawseditor
*
*/

#include <psconfig.h>

#include <csutil/xmltiny.h>
#include <iutil/cfgmgr.h>
#include <csutil/event.h>
#include <iutil/eventq.h>
#include <iutil/objreg.h>
#include <iutil/plugin.h>
#include <iutil/vfs.h>
#include <ivideo/graph3d.h>
#include <ivideo/graph2d.h>
#include <ivideo/natwin.h>
#include <cstool/csview.h>
#include <iengine/mesh.h>
#include <iengine/movable.h>
#include <csutil/flags.h>
#include <iengine/region.h>
#include <imesh/spritecal3d.h>
#include <imesh/object.h>
#include <csutil/scf.h>
#include <iutil/object.h>
#include <igraphic/imageio.h>
#include <csutil/stringarray.h>
#include <csutil/databuf.h>

#include "util/pscssetup.h"
#include "util/psutil.h"
#include "util/psxmlparser.h"
#include "util/consoleout.h"

#include "paws/pawsmanager.h"
#include "paws/pawsmainwidget.h"

#include "pewidgettree.h"
#include "pawseditorapp.h"
#include "pawseditorglobals.h"
#include "pemenu.h"
#include "peeditablewidget.h"
#include "pepawsmanager.h"



const char * PawsEditorApp::CONFIG_FILENAME = "/this/pawseditor.cfg";
const char * PawsEditorApp::APP_NAME        = "planeshift.pawseditor.application";
const char * PawsEditorApp::WINDOW_CAPTION  = "Planeshift Paws Editor";
const char * PawsEditorApp::KEY_DEFS_FILENAME = "/this/data/pawseditor/keys_def.xml";

CS_IMPLEMENT_APPLICATION

PawsEditorApp *editApp;

PawsEditorApp::PawsEditorApp(iObjectRegistry *obj_reg)
            :camFlags(CAM_COUNT)
{
    object_reg  = obj_reg;
    paws        = 0;
    drawScreen  = true;

    currentWidget = NULL;
    widgetTree = NULL;
    
    camFlags.Clear();
    camYaw = 0.0f;
    camPitch = 0.0f;

    fileName = "";
}

PawsEditorApp::~PawsEditorApp()
{
    vfs->Unmount( mountedVPath.GetData(), mountedPath.GetData() );
    mountedVPath.Empty();
    mountedPath.Empty();

    if (event_handler && queue)
        queue->RemoveListener(event_handler);

    delete paws;
        
}

void PawsEditorApp::SevereError(const char* msg)
{
    csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, APP_NAME, msg);
}

bool PawsEditorApp::Init()
{
    queue = CS_QUERY_REGISTRY(object_reg, iEventQueue);
    if (!queue)
    {
        SevereError("No iEventQueue plugin!");
        return false;
    }

    vfs = CS_QUERY_REGISTRY(object_reg, iVFS);
    if (!vfs)
    {
        SevereError("No iVFS plugin!");
        return false;
    }

    engine = CS_QUERY_REGISTRY(object_reg, iEngine);
    if (!engine)
    {
        SevereError("No iEngine plugin!");
        return false;
    }

    cfgmgr = CS_QUERY_REGISTRY(object_reg, iConfigManager);
    if (!cfgmgr)
    {
        SevereError("No iConfigManager plugin!");
        return false;
    }
    // Load the log settings
    LoadLogSettings();

    g3d = CS_QUERY_REGISTRY(object_reg, iGraphics3D);
    if (!g3d)
    {
        SevereError("No iGraphics3D plugin!");
        return false;
    }

    g2d = g3d->GetDriver2D();
    if (!g2d)
    {
        SevereError("Could not load iGraphics2D!");
        return false;
    }
  
    loader = CS_QUERY_REGISTRY(object_reg, iLoader);
    if (!loader)
    {
        SevereError("Could not load iLoader!");
        return false;
    }

    // set the window caption
    iNativeWindow *nw = g3d->GetDriver2D()->GetNativeWindow();
    if (nw)
        nw->SetTitle(WINDOW_CAPTION);

    // paws initialization
    paws = new pePawsManager(object_reg, "/this/art/pawseditor.zip", "/this/pawseditor.cfg");
    if (!paws)
    {
        SevereError("Could not initialize PAWS!");
        return false;
    }

    mainWidget = new pawsMainWidget();
    paws->SetMainWidget(mainWidget);

    mainWidget->LoadFromFile( "data/pawseditor/peBackground.xml" );
    RegisterFactories();

    // Load and assign a default button click sound for pawsbutton
    paws->LoadSound("/this/art/music/gui/ccreate/next.wav","sound.standardButtonClick");

    if (!LoadWidgets())
    {
        csReport(object_reg, CS_REPORTER_SEVERITY_NOTIFY, APP_NAME,
            "Warning: Some PAWS widgets failed to load");
    }

    paws->GetMouse()->ChangeImage("peStandardMousePointer");

    paws->FindWidget( "peMenu" )->SetAlwaysOnTop( true );
    widgetTree = (peWidgetTree *) paws->FindWidget( "peWidgetTree" );
    widgetTree->SetAlwaysOnTop( true );

    skinSelector = (peSkinSelector *) paws->FindWidget( "peSkinSelector" );

    // Register our event handler
    event_handler = csPtr<EventHandler> (new EventHandler (this));
    csEventID esub[] = {
	  csevPreProcess (object_reg),
	  csevProcess (object_reg),
	  csevPostProcess (object_reg),
	  csevFinalProcess (object_reg),
	  csevFrame (object_reg),
	  csevMouseEvent (object_reg),
	  csevKeyboardEvent (object_reg),
	  CS_EVENTLIST_END
	};
    queue->RegisterListener(event_handler, esub);

    // Load Default Skin
    csString skin = cfgmgr->GetStr("Planeshift.GUI.Skin");
    if( strcmp( skin, "" ) )
        LoadSkin( skin );

    // Inform debug that everything initialized succesfully
    csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY, APP_NAME,
        "Application initialized successfully.");

    return true;
}

void PawsEditorApp::LoadSkin( const char* name )
{
    csString zip;
    zip += "/this/art/skins/";
    zip += name;
    zip += ".zip";

    //const char *zip = "/this/art/skins/cvs.zip";
    if ( !vfs->Exists( zip ) )
    {
        printf("Current skin doesn't exist, skipping..\n");
        return;
    }

    //// Make sure the skin is selected
    //if ( skins->GetSelectedRowString() != name )
    //{
    //    skins->Select( name );
    //    return; // To prevent recursion
    //}

    // Get the path
    csRef<iDataBuffer> real = vfs->GetRealPath(zip);
    
    // Unmount the current skin
    if ( mountedVPath.Length() != 0 ) vfs->Unmount( mountedVPath, mountedPath );

    // Temporarily mount zip to read skin.xml
    // Parse XML
    vfs->Mount( "/paws/pawseditor/pawsskin", real->GetData() );
    csRef<iDocument> xml = ParseFile( object_reg, "/paws/pawseditor/pawsskin/skin.xml" );
    if (!xml)
    {
        Error1("Failed to parse file /paws/pawseditor/pawsskin/skin.xml" );
        return;
    }
    vfs->Unmount( "/paws/pawseditor/pawsskin", real->GetData() );

    csRef<iDocumentNode> root = xml->GetRoot();
    if(!root)
    {
        Error1("No XML root in /paws/pawseditor/pawsskin/skin.xml");
        return;
    }
    root = root->GetNode("xml");
    if(!root)
    {
        Error1("No <xml> tag in /paws/pawseditor/pawsskin/skin.xml");
        return;
    }
    csRef<iDocumentNode> skinInfo = root->GetNode("skin_information");
    if(!skinInfo)
    {
        Error1("No <skin_information> tag in /paws/pawseditor/pawsskin/skin.xml");
        return;
    }

    csRef<iDocumentNode> mountNode = root->GetNode("mount_path");

    if( !mountNode )
    {
        printf("skin.xml is missing mount_path!\n");
        return;
    }

    // Mount the skin
    mountedVPath = mountNode->GetContentsValue();
    mountedPath = real->GetData();
    if ( !vfs->Mount( mountedVPath.GetData(), mountedPath.GetData() ) )
    {
        printf( "Failed to mount %s in %s to VFS at %s\n", name, mountedPath.GetData(), mountedVPath.GetData() );
        return;
    }
    printf( "Mounted %s in %s to VFS at %s\n", name, mountedPath.GetData(), mountedVPath.GetData() );

    currentSkin = name;

    // Load the skin
    if ( !LoadResources( mountedVPath ) )
    {
        paws->CreateWarningBox("Couldn't load skin! Check the console for detailed output");
        return;
    }
}

bool PawsEditorApp::LoadResources( const char* mountPath )
{
    csString imagelist( mountPath );
    imagelist += "/imagelist.xml";

    // Open the image list
    csRef<iDocument> xml = ParseFile( object_reg, imagelist );
    if(!xml)
    {
        Error2("Parse error in %s", imagelist.GetData());
        return false;
    }
    csRef<iDocumentNode> root = xml->GetRoot();
    if(!root)
    {
        Error2("No XML root in %s", imagelist.GetData());
        return false;
    }
    csRef<iDocumentNode> topNode = root->GetNode( "image_list" );
    if(!topNode)
    {
        Error1("No <image_list> tag in /paws/pawseditor/pawsskin/skin.xml");
        return false;
    }

    csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

    // Find the resource
    csString filename;
    csString resource;
    while ( iter->HasNext() )
    {
        csRef<iDocumentNode> node = iter->Next();

        if ( node->GetType() != CS_NODE_ELEMENT )
            continue;

        if ( strcmp( node->GetValue(), "image" ) == 0 )
        {
            // Remove the resource if it exists
            resource = node->GetAttributeValue( "resource" );
            filename = node->GetAttributeValue( "file" );

            paws->GetTextureManager()->Remove( resource );


            if( !filename.Length() )
            {
                printf( "Couldn't locate resource %s in the current skin!\n", resource.GetData() );
                return false;
            }

            if( !vfs->Exists( filename ) ) 
            {
                printf( "Skin is missing the '%s' resource at file '%s'!\n", resource.GetData(), filename.GetData() );
                //return false;
                continue;
            }

            // Compile the structure
            psImageDescription* desc = new psImageDescription( paws->GetTextureManager() );
            desc->refCount = 0;
            desc->resourceName = resource;
            desc->imageFileLocation = filename;
            desc->tiled = false;

            // Add the info
            paws->GetTextureManager()->AddImageDescription( desc );

        }
    }

    return true;
}

void PawsEditorApp::LoadLogSettings()
{
    int count=0;
    for (int i=0; i< MAX_FLAGS; i++)
    {
        if (pslog::GetName(i))
        {
            pslog::SetFlag(pslog::GetName(i),cfgmgr->GetBool(pslog::GetSettingName(i)), 0);  
            if(cfgmgr->GetBool(pslog::GetSettingName(i)))
                count++;
        }
    }
    if(count==0)
    {
        CPrintf(CON_CMDOUTPUT,"All LOGS are off.\n");
    }
    
    csString debugFile =  cfgmgr->GetStr("PlaneShift.DebugFile");
    if ( debugFile.Length() > 0 )
    {
        csRef<iStandardReporterListener> reporter = CS_QUERY_REGISTRY( object_reg, iStandardReporterListener );
        
        
        reporter->SetMessageDestination (CS_REPORTER_SEVERITY_DEBUG, true, false ,false, false, true, false);
        reporter->SetMessageDestination (CS_REPORTER_SEVERITY_ERROR, true, false ,false, false, true, false);       
        reporter->SetMessageDestination (CS_REPORTER_SEVERITY_BUG, true, false ,false, false, true, false);               
        
        time_t curr=time(0);
        tm* localtm = localtime(&curr);

        csString timeStr;
        timeStr.Format("-%d-%02d-%02d-%02d:%02d:%02d",
            localtm->tm_year+1900,
            localtm->tm_mon+1,
            localtm->tm_mday,
            localtm->tm_hour,
            localtm->tm_min,
            localtm->tm_sec);

        debugFile.Append( timeStr );                   
        reporter->SetDebugFile( debugFile, true );
       
        Debug2(LOG_STARTUP,0, "PlaneShift Server Log Opened............. %s", timeStr.GetData());
       
    }
}

bool PawsEditorApp::LoadWidget(const char * file)
{
    if (!paws->LoadWidget(file))
    {
        csString err = "Warning: Loading '" + csString(file) + "' failed!";
        SevereError(err);
        return false;
    }
    return true;
}

bool PawsEditorApp::LoadWidgets()
{
    bool succeeded = true;

    succeeded &= LoadWidget("data/pawseditor/peWidgetTree.xml");
    succeeded &= LoadWidget("data/pawseditor/peMenu.xml");
    succeeded &= LoadWidget("data/pawseditor/peSkinSelector.xml");

    return succeeded;
}

bool PawsEditorApp::HandleEvent(iEvent &ev)
{
    if (ev.Name == csevMouseMove (object_reg, 0))
    {
        csMouseEventData mouse;
        csMouseEventHelper::GetEventData(&ev,mouse);

        mousePointer.x = mouse.x;
        mousePointer.y = mouse.y;
    }

    if (paws->HandleEvent(ev))
        return true;

    if (ev.Name == csevProcess (object_reg))
    {
        Update();
        return true;
    }
    else if (ev.Name == csevFinalProcess (object_reg))
    {
        g3d->FinishDraw ();
        g3d->Print (0);
        return true;
    }
    else if (ev.Name == csevPostProcess (object_reg))
    {
        if (drawScreen)
        {
            g3d->BeginDraw(engine->GetBeginDrawFlags() | CSDRAW_2DGRAPHICS);
            paws->Draw();
        }
        else
        {
            csSleep(150);
        }
    }
    else if (ev.Name == csevCanvasHidden (object_reg, g3d->GetDriver2D ()))
    {
        drawScreen = false;
    }
    else if (ev.Name == csevCanvasExposed (object_reg, g3d->GetDriver2D ()))
    {
        drawScreen = true;
    }
    return false;
}




csVector2 PawsEditorApp::GetMousePointer()
{
    return mousePointer;
}

csRef<iConfigManager> PawsEditorApp::GetConfigManager()
{
    return cfgmgr;
}

csRef<iVFS> PawsEditorApp::GetVFS()
{
    return vfs;
}

void PawsEditorApp::Exit()
{
    queue->GetEventOutlet()->Broadcast (csevQuit (object_reg));
}

    
void PawsEditorApp::RegisterFactories()
{
    new peEditableWidgetFactory();
    new peWidgetTreeFactory();
    new peMenuFactory();
    new peSkinSelectorFactory();
}

void PawsEditorApp::Update()
{

}


void PawsEditorApp::SetCamFlag(int flag, bool val)
{
    if (val)
        camFlags.SetBit(flag);
    else
        camFlags.ClearBit(flag);
}

bool PawsEditorApp::GetCamFlag(int flag)
{
    return camFlags.IsBitSet(flag);
}

void PawsEditorApp::TakeScreenshot(const csString & fileName)
{
    csRef<iImageIO> imageio = CS_QUERY_REGISTRY(object_reg, iImageIO);
    csRef<iImage> image = g2d->ScreenShot();
    csRef<iDataBuffer> buffer = imageio->Save(image, "image/jpg");
    vfs->WriteFile(fileName.GetData(), buffer->GetData(), buffer->GetSize());

    printf("Screenshot taken.\n");
}

csRef<iDocumentNode> PawsEditorApp::ParseWidgetFile( const char* widgetFile )
{
    csRef<iDocument> doc = ParseFile(paws->GetObjectRegistry(), widgetFile);  

    if (!doc)
    {
        Error2("Parse error in %s", widgetFile);
        return NULL;
    }
    csRef<iDocumentNode> root = doc->GetRoot();
    if(!root)
    {
        Error2("No XML root %s", widgetFile);
        return NULL;
    }
    csRef<iDocumentNode> widgetNode = root->GetNode("widget_description");
    if (!widgetNode)
        Error2("File %s has no <widget_description> tag", widgetFile);
    return widgetNode;
}
void PawsEditorApp::ReloadWidget()
{
    if ( fileName.Length() != 0 ) OpenWidget( fileName.GetData() );
}

void PawsEditorApp::OpenWidget(const csString & text)
{
    if ( text.Length() == 0 ) return;

    fileName = text;

    if ( currentWidget )
    {
        currentWidget->Close();
        delete currentWidget;
    }

    csRef<iDocumentNode> widgetNode = ParseWidgetFile( text );
    csRef<iDocumentNodeIterator> iter = widgetNode->GetNodes();

    while ( iter->HasNext() )
    {
        csRef<iDocumentNode> node = iter->Next();

        if ( node->GetType() != CS_NODE_ELEMENT )
            continue;

        // This is a widget so read it's factory to create it.
        if ( strcmp( node->GetValue(), "widget" ) == 0 )
        {
            //node->RemoveAttribute( node->GetAttribute( "factory" ) );
            node->SetAttribute( "factory" , "peEditableWidget" );
            csString name = node->GetAttribute( "name" )->GetValue();
            csString factory = node->GetAttribute( "factory" )->GetValue();
            widgetTree->LoadWidget( widgetNode );
            currentWidget = dynamic_cast<pawsWidget *> ( paws->LoadWidget( node ) ) ;
            if ( !currentWidget ) 
            {
                csString err = "Warning: Loading '" + csString( text ) + "' failed!";
                editApp->SevereError( err );
                return;
            }
            paws->GetMainWidget()->FindWidget( "preview" )->AddChild( currentWidget );
            currentWidget->SetFilename( text );
            currentWidget->SetMinAlpha( 0 );
            currentWidget->SetMaxAlpha( 1 );
            currentWidget->Show();

            return;
        }
    }

}

void PawsEditorApp::OpenImageList(const csString & text)
{    
    paws->GetTextureManager()->LoadImageList( text );
}


/** Application entry point
 */
int main (int argc, char *argv[])
{
    psCSSetup *CSSetup = new psCSSetup(argc, argv, PawsEditorApp::CONFIG_FILENAME, PawsEditorApp::CONFIG_FILENAME);
    iObjectRegistry *object_reg = CSSetup->InitCS();
    
    editApp = new PawsEditorApp(object_reg);

    // Initialize application
    if (!editApp->Init())
    {
        editApp->SevereError("Failed to init app!");
        PS_PAUSEEXIT(1);
    }

    // start the main event loop
    csDefaultRunLoop(object_reg);

    // clean up
    delete editApp;
    delete CSSetup;

    csInitializer::DestroyApplication(object_reg);
    return 0;
}
