text: //
  // Copyright (c) 2008-2016 the Urho3D project.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  //
  
  #include <Urho3D/Engine/Application.h>
  #include <Urho3D/Graphics/Camera.h>
  #include <Urho3D/Engine/Console.h>
  #include <Urho3D/UI/Cursor.h>
  #include <Urho3D/Engine/DebugHud.h>
  #include <Urho3D/Engine/Engine.h>
  #include <Urho3D/IO/FileSystem.h>
  #include <Urho3D/Graphics/Graphics.h>
  #include <Urho3D/Input/Input.h>
  #include <Urho3D/Input/InputEvents.h>
  #include <Urho3D/Graphics/Renderer.h>
  #include <Urho3D/Resource/ResourceCache.h>
  #include <Urho3D/Scene/Scene.h>
  #include <Urho3D/Scene/SceneEvents.h>
  #include <Urho3D/UI/Sprite.h>
  #include <Urho3D/Graphics/Texture2D.h>
  #include <Urho3D/Core/Timer.h>
  #include <Urho3D/UI/UI.h>
  #include <Urho3D/Resource/XMLFile.h>
  #include <Urho3D/IO/Log.h>
  
  Sample::Sample(Context* context) :
      Application(context),
      yaw_(0.0f),
      pitch_(0.0f),
      touchEnabled_(false),
      screenJoystickIndex_(M_MAX_UNSIGNED),
      screenJoystickSettingsIndex_(M_MAX_UNSIGNED),
      paused_(false),
      useMouseMode_(MM_ABSOLUTE)
  {
  }
  
  void Sample::Setup()
  {
      // Modify engine startup parameters
      engineParameters_["WindowTitle"] = GetTypeName();
      engineParameters_["LogName"]     = GetSubsystem<FileSystem>()->GetAppPreferencesDir("urho3d", "logs") + GetTypeName() + ".log";
      engineParameters_["FullScreen"]  = false;
      engineParameters_["Headless"]    = false;
      engineParameters_["Sound"]       = false;
  
      // Construct a search path to find the resource prefix with two entries:
      // The first entry is an empty path which will be substituted with program/bin directory -- this entry is for binary when it is still in build tree
      // The second and third entries are possible relative paths from the installed program/bin directory to the asset directory -- these entries are for binary when it is in the Urho3D SDK installation location
      if (!engineParameters_.Contains("ResourcePrefixPaths"))
          engineParameters_["ResourcePrefixPaths"] = ";../share/Resources;../share/Urho3D/Resources";
  }
  
  void Sample::Start()
  {
      if (GetPlatform() == "Android" || GetPlatform() == "iOS")
          // On mobile platform, enable touch by adding a screen joystick
          InitTouchInput();
      else if (GetSubsystem<Input>()->GetNumJoysticks() == 0)
          // On desktop platform, do not detect touch when we already got a joystick
          SubscribeToEvent(E_TOUCHBEGIN, URHO3D_HANDLER(Sample, HandleTouchBegin));
  
      // Create logo
      CreateLogo();
  
      // Set custom window Title & Icon
      SetWindowTitleAndIcon();
  
      // Create console and debug HUD
      CreateConsoleAndDebugHud();
  
      // Subscribe key down event
      SubscribeToEvent(E_KEYDOWN, URHO3D_HANDLER(Sample, HandleKeyDown));
      // Subscribe key up event
      SubscribeToEvent(E_KEYUP, URHO3D_HANDLER(Sample, HandleKeyUp));
      // Subscribe scene update event
      SubscribeToEvent(E_SCENEUPDATE, URHO3D_HANDLER(Sample, HandleSceneUpdate));
  }
  
  void Sample::Stop()
  {
      engine_->DumpResources(true);
  }
  
  void Sample::InitTouchInput()
  {
      touchEnabled_ = true;
  
      ResourceCache* cache = GetSubsystem<ResourceCache>();
      Input* input = GetSubsystem<Input>();
      XMLFile* layout = cache->GetResource<XMLFile>("UI/ScreenJoystick_Samples.xml");
      const String& patchString = GetScreenJoystickPatchString();
      if (!patchString.Empty())
      {
          // Patch the screen joystick layout further on demand
          SharedPtr<XMLFile> patchFile(new XMLFile(context_));
          if (patchFile->FromString(patchString))
              layout->Patch(patchFile);
      }
      screenJoystickIndex_ = (unsigned)input->AddScreenJoystick(layout, cache->GetResource<XMLFile>("UI/DefaultStyle.xml"));
      input->SetScreenJoystickVisible(screenJoystickSettingsIndex_, true);
  }
  
  void Sample::InitMouseMode(MouseMode mode)
  {
      useMouseMode_ = mode;
  
      Input* input = GetSubsystem<Input>();
  
      if (GetPlatform() != "Web")
      {
          if (useMouseMode_ == MM_FREE)
              input->SetMouseVisible(true);
  
          Console* console = GetSubsystem<Console>();
          if (useMouseMode_ != MM_ABSOLUTE)
          {
              input->SetMouseMode(useMouseMode_);
              if (console && console->IsVisible())
                  input->SetMouseMode(MM_ABSOLUTE, true);
          }
      }
      else
      {
          input->SetMouseVisible(true);
          SubscribeToEvent(E_MOUSEBUTTONDOWN, URHO3D_HANDLER(Sample, HandleMouseModeRequest));
          SubscribeToEvent(E_MOUSEMODECHANGED, URHO3D_HANDLER(Sample, HandleMouseModeChange));
      }
  }
  
  void Sample::SetLogoVisible(bool enable)
  {
      if (logoSprite_)
          logoSprite_->SetVisible(enable);
  }
  
  void Sample::CreateLogo()
  {
      // Get logo texture
      ResourceCache* cache = GetSubsystem<ResourceCache>();
      Texture2D* logoTexture = cache->GetResource<Texture2D>("Textures/LogoLarge.png");
      if (!logoTexture)
          return;
  
      // Create logo sprite and add to the UI layout
      UI* ui = GetSubsystem<UI>();
      logoSprite_ = ui->GetRoot()->CreateChild<Sprite>();
  
      // Set logo sprite texture
      logoSprite_->SetTexture(logoTexture);
  
      int textureWidth = logoTexture->GetWidth();
      int textureHeight = logoTexture->GetHeight();
  
      // Set logo sprite scale
      logoSprite_->SetScale(256.0f / textureWidth);
  
      // Set logo sprite size
      logoSprite_->SetSize(textureWidth, textureHeight);
  
      // Set logo sprite hot spot
      logoSprite_->SetHotSpot(0, textureHeight);
  
      // Set logo sprite alignment
      logoSprite_->SetAlignment(HA_LEFT, VA_BOTTOM);
  
      // Make logo not fully opaque to show the scene underneath
      logoSprite_->SetOpacity(0.75f);
  
      // Set a low priority for the logo so that other UI elements can be drawn on top
      logoSprite_->SetPriority(-100);
  }
  
  void Sample::SetWindowTitleAndIcon()
  {
      ResourceCache* cache = GetSubsystem<ResourceCache>();
      Graphics* graphics = GetSubsystem<Graphics>();
      Image* icon = cache->GetResource<Image>("Textures/UrhoIcon.png");
      graphics->SetWindowIcon(icon);
      graphics->SetWindowTitle("Urho3D Sample");
  }
  
  void Sample::CreateConsoleAndDebugHud()
  {
      // Get default style
      ResourceCache* cache = GetSubsystem<ResourceCache>();
      XMLFile* xmlFile = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
  
      // Create console
      Console* console = engine_->CreateConsole();
      console->SetDefaultStyle(xmlFile);
      console->GetBackground()->SetOpacity(0.8f);
  
      // Create debug HUD.
      DebugHud* debugHud = engine_->CreateDebugHud();
      debugHud->SetDefaultStyle(xmlFile);
  }
  
  
  void Sample::HandleKeyUp(StringHash /*eventType*/, VariantMap& eventData)
  {
      using namespace KeyUp;
  
      int key = eventData[P_KEY].GetInt();
  
      // Close console (if open) or exit when ESC is pressed
      if (key == KEY_ESCAPE)
      {
          Console* console = GetSubsystem<Console>();
          if (console->IsVisible())
              console->SetVisible(false);
          else
          {
              if (GetPlatform() == "Web")
              {
                  GetSubsystem<Input>()->SetMouseVisible(true);
                  if (useMouseMode_ != MM_ABSOLUTE)
                      GetSubsystem<Input>()->SetMouseMode(MM_FREE);
              }
              else
                  engine_->Exit();
          }
      }
  }
  
  void Sample::HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData)
  {
      using namespace KeyDown;
  
      int key = eventData[P_KEY].GetInt();
  
      // Toggle console with F1
      if (key == KEY_F1)
          GetSubsystem<Console>()->Toggle();
  
      // Toggle debug HUD with F2
      else if (key == KEY_F2)
          GetSubsystem<DebugHud>()->ToggleAll();
  
      // Common rendering quality controls, only when UI has no focused element
      else if (!GetSubsystem<UI>()->GetFocusElement())
      {
          Renderer* renderer = GetSubsystem<Renderer>();
  
          // Preferences / Pause
          if (key == KEY_SELECT && touchEnabled_)
          {
              paused_ = !paused_;
  
              Input* input = GetSubsystem<Input>();
              if (screenJoystickSettingsIndex_ == M_MAX_UNSIGNED)
              {
                  // Lazy initialization
                  ResourceCache* cache = GetSubsystem<ResourceCache>();
                  screenJoystickSettingsIndex_ = (unsigned)input->AddScreenJoystick(cache->GetResource<XMLFile>("UI/ScreenJoystickSettings_Samples.xml"), cache->GetResource<XMLFile>("UI/DefaultStyle.xml"));
              }
              else
                  input->SetScreenJoystickVisible(screenJoystickSettingsIndex_, paused_);
          }
  
          // Texture quality
          else if (key == '1')
          {
              int quality = renderer->GetTextureQuality();
              ++quality;
              if (quality > QUALITY_HIGH)
                  quality = QUALITY_LOW;
              renderer->SetTextureQuality(quality);
          }
  
          // Material quality
          else if (key == '2')
          {
              int quality = renderer->GetMaterialQuality();
              ++quality;
              if (quality > QUALITY_HIGH)
                  quality = QUALITY_LOW;
              renderer->SetMaterialQuality(quality);
          }
  
          // Specular lighting
          else if (key == '3')
              renderer->SetSpecularLighting(!renderer->GetSpecularLighting());
  
          // Shadow rendering
          else if (key == '4')
              renderer->SetDrawShadows(!renderer->GetDrawShadows());
  
          // Shadow map resolution
          else if (key == '5')
          {
              int shadowMapSize = renderer->GetShadowMapSize();
              shadowMapSize *= 2;
              if (shadowMapSize > 2048)
                  shadowMapSize = 512;
              renderer->SetShadowMapSize(shadowMapSize);
          }
  
          // Shadow depth and filtering quality
          else if (key == '6')
          {
              ShadowQuality quality = renderer->GetShadowQuality();
              quality = (ShadowQuality)(quality + 1);
              if (quality > SHADOWQUALITY_BLUR_VSM)
                  quality = SHADOWQUALITY_SIMPLE_16BIT;
              renderer->SetShadowQuality(quality);
          }
  
          // Occlusion culling
          else if (key == '7')
          {
              bool occlusion = renderer->GetMaxOccluderTriangles() > 0;
              occlusion = !occlusion;
              renderer->SetMaxOccluderTriangles(occlusion ? 5000 : 0);
          }
  
          // Instancing
          else if (key == '8')
              renderer->SetDynamicInstancing(!renderer->GetDynamicInstancing());
  
          // Take screenshot
          else if (key == '9')
          {
              Graphics* graphics = GetSubsystem<Graphics>();
              Image screenshot(context_);
              graphics->TakeScreenShot(screenshot);
              // Here we save in the Data folder with date and time appended
              screenshot.SavePNG(GetSubsystem<FileSystem>()->GetProgramDir() + "Data/Screenshot_" +
                  Time::GetTimeStamp().Replaced(':', '_').Replaced('.', '_').Replaced(' ', '_') + ".png");
          }
      }
  }
  
  void Sample::HandleSceneUpdate(StringHash /*eventType*/, VariantMap& eventData)
  {
      // Move the camera by touch, if the camera node is initialized by descendant sample class
      if (touchEnabled_ && cameraNode_)
      {
          Input* input = GetSubsystem<Input>();
          for (unsigned i = 0; i < input->GetNumTouches(); ++i)
          {
              TouchState* state = input->GetTouch(i);
              if (!state->touchedElement_)    // Touch on empty space
              {
                  if (state->delta_.x_ ||state->delta_.y_)
                  {
                      Camera* camera = cameraNode_->GetComponent<Camera>();
                      if (!camera)
                          return;
  
                      Graphics* graphics = GetSubsystem<Graphics>();
                      yaw_ += TOUCH_SENSITIVITY * camera->GetFov() / graphics->GetHeight() * state->delta_.x_;
                      pitch_ += TOUCH_SENSITIVITY * camera->GetFov() / graphics->GetHeight() * state->delta_.y_;
  
                      // Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero
                      cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
                  }
                  else
                  {
                      // Move the cursor to the touch position
                      Cursor* cursor = GetSubsystem<UI>()->GetCursor();
                      if (cursor && cursor->IsVisible())
                          cursor->SetPosition(state->position_);
                  }
              }
          }
      }
  }
  
  void Sample::HandleTouchBegin(StringHash /*eventType*/, VariantMap& eventData)
  {
      // On some platforms like Windows the presence of touch input can only be detected dynamically
      InitTouchInput();
      UnsubscribeFromEvent("TouchBegin");
  }
  
  // If the user clicks the canvas, attempt to switch to relative mouse mode on web platform
  void Sample::HandleMouseModeRequest(StringHash /*eventType*/, VariantMap& eventData)
  {
      Console* console = GetSubsystem<Console>();
      if (console && console->IsVisible())
          return;
      Input* input = GetSubsystem<Input>();
      if (useMouseMode_ == MM_ABSOLUTE)
          input->SetMouseVisible(false);
      else if (useMouseMode_ == MM_FREE)
          input->SetMouseVisible(true);
      input->SetMouseMode(useMouseMode_);
  }
  
  void Sample::HandleMouseModeChange(StringHash /*eventType*/, VariantMap& eventData)
  {
      Input* input = GetSubsystem<Input>();
      bool mouseLocked = eventData[MouseModeChanged::P_MOUSELOCKED].GetBool();
      input->SetMouseVisible(!mouseLocked);
  }
  