/*
 *   ORXONOX - the hottest 3D action shooter ever to exist
 *
 *
 *   License notice:
 *
 *   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 3 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, see .
 *
 *
 *   Author:
 *      Benjamin Knecht , (C) 2007
 *   Co-authors:
 *      ...
 *
 */
/**
 @file  orxonox.cc
 @brief Orxonox Main File
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include "xml/xmlParser.h"
#include "loader/LevelLoader.h"
#include "spaceship_steering.h"
SpaceshipSteering* steering;
// some tests to see if enet works without includsion
//#include 
//#include 
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include 
// This function will locate the path to our application on OS X,
// unlike windows you can not rely on the curent working directory
// for locating your configuration files and resources.
std::string macBundlePath()
{
  char path[1024];
  CFBundleRef mainBundle = CFBundleGetMainBundle();
  assert(mainBundle);
  CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
  assert(mainBundleURL);
  CFStringRef cfStringRef = CFURLCopyFileSystemPath( mainBundleURL, kCFURLPOSIXPathStyle);
  assert(cfStringRef);
  CFStringGetCString(cfStringRef, path, 1024, kCFStringEncodingASCII);
  CFRelease(mainBundleURL);
  CFRelease(cfStringRef);
  return std::string(path);
}
#endif
namespace orxonox
{
using namespace Ogre;
class OrxExitListener : public FrameListener, public OIS::MouseListener
{
  public:
    OrxExitListener(OIS::Keyboard *keyboard, OIS::Mouse *mouse)
  : mKeyboard(keyboard), mMouse(mouse)
    {
      speed = 250;
      loop = 100;
      rotate = 10;
      mouseX = 0;
      mouseY = 0;
      maxMouseX = 0;
      minMouseX = 0;
      moved = false;
      steering->brakeRotate(rotate*10);
      steering->brakeLoop(loop);
      mMouse->setEventCallback(this);
    }
    bool frameStarted(const FrameEvent& evt)
    {
      mKeyboard->capture();
      mMouse->capture();
      if (mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W))
        steering->moveForward(speed);
      else
        steering->moveForward(0);
      if(mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S))
        steering->brakeForward(speed);
      else
        steering->brakeForward(speed/10);
      if (mKeyboard->isKeyDown(OIS::KC_RIGHT) || mKeyboard->isKeyDown(OIS::KC_D))
        steering->loopRight(loop);
      else
        steering->loopRight(0);
      if (mKeyboard->isKeyDown(OIS::KC_LEFT) || mKeyboard->isKeyDown(OIS::KC_A))
        steering->loopLeft(loop);
      else
        steering->loopLeft(0);
      if(moved) {
        if (mouseY<0)
          steering->rotateUp(-mouseY*rotate);
        if (mouseY>0)
          steering->rotateDown(mouseY*rotate);
        if (mouseX>0)
          steering->rotateRight(mouseX*rotate);
        if (mouseX<0)
          steering->rotateLeft(-mouseX*rotate);
        moved = false;
      }
      else {
        steering->rotateUp(0);
        steering->rotateDown(0);
        steering->rotateRight(0);
        steering->rotateLeft(0);
      }
      steering->tick(evt.timeSinceLastFrame);
//	scenemanager->spacehip->tick(evt.timesincelastframe);
      if(mKeyboard->isKeyDown(OIS::KC_ESCAPE))
        cout << "maximal MouseX: " << maxMouseX << "\tminMouseX: " << minMouseX << endl;
      return !mKeyboard->isKeyDown(OIS::KC_ESCAPE);
    }
    bool mouseMoved(const OIS::MouseEvent &e)
    {
      mouseX = e.state.X.rel;
      mouseY = e.state.Y.rel;
      if(mouseX>maxMouseX) maxMouseX = mouseX;
      if(mouseXdestroyInputObject(mKeyboard);
        OIS::InputManager::destroyInputSystem(mInputManager);
//        delete mRenderer;
//        delete mSystem;
        delete mListener;
        delete mRoot;
      }
    private:
      Ogre::Root *mRoot;
      OIS::Keyboard *mKeyboard;
      OIS::Mouse *mMouse;
      OIS::InputManager *mInputManager;
      CEGUI::OgreCEGUIRenderer *mRenderer;
      CEGUI::System *mSystem;
      OrxExitListener *mListener;
      void createRoot()
      {
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
        mRoot = new Ogre::Root(macBundlePath() + "/Contents/Resources/plugins.cfg");
#else
        mRoot = new Ogre::Root();
#endif
      }
      void defineResources()
      {
        Ogre::String secName, typeName, archName;
        Ogre::ConfigFile cf;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
        cf.load(macBundlePath() + "/Contents/Resources/resources.cfg");
#else
        cf.load("resources.cfg");
#endif
        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
        while (seci.hasMoreElements())
        {
          secName = seci.peekNextKey();
          Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
          Ogre::ConfigFile::SettingsMultiMap::iterator i;
          for (i = settings->begin(); i != settings->end(); ++i)
          {
            typeName = i->first;
            archName = i->second;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation( String(macBundlePath() + "/" + archName), typeName, secName);
#else
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName);
#endif
          }
        }
      }
      void setupRenderSystem()
      {
        if (!mRoot->restoreConfig() && !mRoot->showConfigDialog())
          throw Exception(52, "User canceled the config dialog!", "OrxApplication::setupRenderSystem()");
      }
      void createRenderWindow()
      {
        mRoot->initialise(true, "Ogre Render Window");
      }
      void initializeResourceGroups()
      {
        TextureManager::getSingleton().setDefaultNumMipmaps(5);
        ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
      }
      void createScene(void)
      {
      string levelFile = "sp_level_moonstation.oxw";
      loader::LevelLoader* loader = new loader::LevelLoader(levelFile);
    }
    void setupScene()
    {
      SceneManager *mgr = mRoot->createSceneManager(ST_GENERIC, "Default SceneManager");
      Camera *cam = mgr->createCamera("Camera");
      cam->setPosition(Vector3(0,0,-250));
      cam->lookAt(Vector3(0,0,0));
      Viewport *vp = mRoot->getAutoCreatedWindow()->addViewport(cam);
      mgr->setAmbientLight(ColourValue(1,1,1));
      Entity* head = mgr->createEntity("head", "ogrehead.mesh");
      SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode", Vector3(0,0,0));
      node->attachObject(head);
      node->attachObject(cam);
      mgr->setSkyBox(true, "Examples/SceneSkyBox2");
      Entity* head1 = mgr->createEntity("head1", "ogrehead.mesh");
      SceneNode *node1 = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode1", Vector3(200,0,0));
      node1->attachObject(head1);
      Entity* head2 = mgr->createEntity("head2", "ogrehead.mesh");
      SceneNode *node2 = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode2", Vector3(200,400,-100));
      node2->attachObject(head2);
      Entity* head3 = mgr->createEntity("head3", "ogrehead.mesh");
      SceneNode *node3 = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode3", Vector3(0,400,200));
      node3->attachObject(head3);
      Entity* head4 = mgr->createEntity("head4", "ogrehead.mesh");
      SceneNode *node4 = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode4", Vector3(-400,-200,600));
      node4->attachObject(head4);
      Entity* head5 = mgr->createEntity("head5", "ogrehead.mesh");
      SceneNode *node5 = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode5", Vector3(0,0,-400));
      node5->attachObject(head5);
      steering = new SpaceshipSteering(500, 200, 200, 200);
      steering->addNode(node);
    }
      void setupInputSystem()
      {
        size_t windowHnd = 0;
        std::ostringstream windowHndStr;
        OIS::ParamList pl;
        Ogre::RenderWindow *win = mRoot->getAutoCreatedWindow();
        win->getCustomAttribute("WINDOW", &windowHnd);
        windowHndStr << windowHnd;
        pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
        mInputManager = OIS::InputManager::createInputSystem(pl);
        try
        {
          mKeyboard = static_cast(mInputManager->createInputObject(OIS::OISKeyboard, false));
          mMouse = static_cast(mInputManager->createInputObject(OIS::OISMouse, true));
        }
        catch (const OIS::Exception &e)
        {
          throw new Ogre::Exception(42, e.eText, "OrxApplication::setupInputSystem");
        }
      }
      void setupCEGUI()
      {
        Ogre::SceneManager *mgr = mRoot->getSceneManager("Default SceneManager");
        Ogre::RenderWindow *win = mRoot->getAutoCreatedWindow();
        // CEGUI setup
//        mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);
//        mSystem = new CEGUI::System(mRenderer);
        // Other CEGUI setup here.
      }
    void createFrameListener()
    {
      mListener = new OrxExitListener(mKeyboard, mMouse);
      mRoot->addFrameListener(mListener);
    }
      void startRenderLoop()
      {
        mRoot->startRendering();
      }
  };
}
using namespace Ogre;
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
             INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
             int main(int argc, char **argv)
#endif
{
  try
  {
    orxonox::OrxApplication orxonox;
    orxonox.go();
  }
  catch(Exception& e)
  {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
    fprintf(stderr, "An exception has occurred: %s\n",
            e.getFullDescription().c_str());
#endif
  }
  return 0;
}